From 803666ef54c14e60beeead4926fa7f329aca3e01 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 19 Apr 2019 17:52:56 +0800 Subject: [PATCH 001/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0README=E8=B5=9E?= =?UTF-8?q?=E8=B5=8F=E7=A0=81=E5=8A=9F=E8=83=BD,=20=E5=8E=9A=E9=A2=9C?= =?UTF-8?q?=E6=B1=82=E4=B8=80=E6=9D=AF=E5=92=96=E5=95=A1.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 3171bd4d..bbdf7310 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,13 @@

-> 大道至简, 返璞归真. +> 大道至简, 返璞归真. + +> 简单, 不简单! + +> 没有其它框架比它更容易入手! 如果有, 那就是借鉴了它. + +> 使用cf后: 你无需[996](https://github.com/996icu/996.ICU), 不会进入[ICU](https://github.com/996icu/996.ICU) --- @@ -28,11 +34,7 @@ > [如何在容器内运行?](https://github.com/CandyMi/core_framework/wiki/Docker) -## 第一次运行CF - -> [CF如何运行?](https://github.com/CandyMi/core_framework/wiki/RUN) - -## 第一次使用CF的问题 +## 第一次使用CF遇到的问题 > [我有一些cf使用上的问题?](https://github.com/CandyMi/core_framework/wiki/QA) @@ -44,9 +46,11 @@ ## 支持 -> 简单, 不简单! +> 如果您觉得cf还不错, 可以支持一杯咖啡予以鼓励. + + + -> 使用cf的目标: [拒绝996](https://github.com/996icu/996.ICU), [没有ICU](https://github.com/996icu/996.ICU) ## 授权协议 From 9f115abac332c093982b7174535bce97913999ca Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 19 Apr 2019 18:39:00 +0800 Subject: [PATCH 002/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0README,=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0logo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index bbdf7310..b971ec8a 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,11 @@

-> 大道至简, 返璞归真. +

+ +

-> 简单, 不简单! +> 大道至简, 返璞归真. 简单? 不简单! > 没有其它框架比它更容易入手! 如果有, 那就是借鉴了它. @@ -48,9 +50,10 @@ > 如果您觉得cf还不错, 可以支持一杯咖啡予以鼓励. - - - +

+ + +

## 授权协议 From 4037e157ed6721f82fcefa96dcfc591ed8f48b07 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 22 Apr 2019 21:28:20 +0800 Subject: [PATCH 003/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E7=9A=84=E5=91=BD=E5=90=8D=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/{uitls => utils}/init.lua | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lualib/{uitls => utils}/init.lua (100%) diff --git a/lualib/uitls/init.lua b/lualib/utils/init.lua similarity index 100% rename from lualib/uitls/init.lua rename to lualib/utils/init.lua From 4aef00fed5acb768d05e40553f8b1cbec2972e3d Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 25 Apr 2019 04:34:51 +0800 Subject: [PATCH 004/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0xml2lua?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/XmlParser.lua | 13 +------------ lualib/xml2lua.lua | 20 +++----------------- lualib/xmlhandler/README.md | 2 +- lualib/xmlhandler/dom.lua | 2 +- lualib/xmlhandler/print.lua | 0 lualib/xmlhandler/tree.lua | 4 ++-- 6 files changed, 8 insertions(+), 33 deletions(-) mode change 100644 => 100755 lualib/xml2lua.lua mode change 100644 => 100755 lualib/xmlhandler/README.md mode change 100644 => 100755 lualib/xmlhandler/dom.lua mode change 100644 => 100755 lualib/xmlhandler/print.lua mode change 100644 => 100755 lualib/xmlhandler/tree.lua diff --git a/lualib/XmlParser.lua b/lualib/XmlParser.lua index 04c55c65..9ed8f41a 100644 --- a/lualib/XmlParser.lua +++ b/lualib/XmlParser.lua @@ -1,14 +1,3 @@ ---- this code fork from github.com/manoelcampos/xml2lua - -local tonumber = tonumber -local getmetatable = getmetatable -local setmetatable = setmetatable -local error = error -local pairs = pairs -local ipairs = ipairs -local table = table -local string = string - --- @module Class providing the actual XML parser. -- Available options are: -- * stripWS @@ -461,4 +450,4 @@ function XmlParser:parse(xml, parseAttributes) end XmlParser.__index = XmlParser -return XmlParser \ No newline at end of file +return XmlParser diff --git a/lualib/xml2lua.lua b/lualib/xml2lua.lua old mode 100644 new mode 100755 index 0725fbed..bad37276 --- a/lualib/xml2lua.lua +++ b/lualib/xml2lua.lua @@ -1,5 +1,3 @@ ---- this code fork from github.com/manoelcampos/xml2lua - --- @module Module providing a non-validating XML stream parser in Lua. -- -- Features: @@ -51,18 +49,9 @@ -- --@author Paul Chakravarti (paulc@passtheaardvark.com) --@author Manoel Campos da Silva Filho -local class = require "class" +local xml2lua = {} local XmlParser = require("XmlParser") -local tonumber = tonumber -local getmetatable = getmetatable -local setmetatable = setmetatable -local error = error -local pairs = pairs -local ipairs = ipairs -local table = table -local string = string - ---Recursivelly prints a table in an easy-to-ready format --@param tb The table to be printed --@param level the indentation level to start with @@ -90,10 +79,7 @@ end -- local handler = require("xmlhandler/tree"). --@return a XmlParser object used to parse the XML --@see XmlParser - -local xml2lua = {} - -function xml2lua.parser(handler) +function xml2lua.parser(handler) if handler == xml2lua then error("You must call xml2lua.parse(handler) instead of xml2lua:parse(handler)") end @@ -152,4 +138,4 @@ function xml2lua.loadFile(xmlFilePath) error(e) end -return xml2lua \ No newline at end of file +return xml2lua diff --git a/lualib/xmlhandler/README.md b/lualib/xmlhandler/README.md old mode 100644 new mode 100755 index a1a5e326..e70323f2 --- a/lualib/xmlhandler/README.md +++ b/lualib/xmlhandler/README.md @@ -12,4 +12,4 @@ Then, you have to use one the handler instance when getting an instance of the X Notice the module `xml2lua` should have been loaded before using `require("xml2lua")`. This way, the handler is called internally when the `parser:parse(xml)` function is called. -Check the documentation on the root directory for complete examples. +Check the documentation on the root directory for complete examples. \ No newline at end of file diff --git a/lualib/xmlhandler/dom.lua b/lualib/xmlhandler/dom.lua old mode 100644 new mode 100755 index 7ed27fc7..ae87c438 --- a/lualib/xmlhandler/dom.lua +++ b/lualib/xmlhandler/dom.lua @@ -132,4 +132,4 @@ end ---Parses CDATA tag content. dom.cdata = dom.text -return dom \ No newline at end of file +return dom diff --git a/lualib/xmlhandler/print.lua b/lualib/xmlhandler/print.lua old mode 100644 new mode 100755 diff --git a/lualib/xmlhandler/tree.lua b/lualib/xmlhandler/tree.lua old mode 100644 new mode 100755 index 7703799d..14e29e98 --- a/lualib/xmlhandler/tree.lua +++ b/lualib/xmlhandler/tree.lua @@ -5,7 +5,7 @@ local function init() } obj._stack = {obj.root, n=1} - return obj + return obj end --- @module XML Tree Handler. @@ -156,4 +156,4 @@ end ---Parses CDATA tag content. tree.cdata = tree.text tree.__index = tree -return tree \ No newline at end of file +return tree From 27a3a1bc5e51a3b622f8c72bf64bc46b80e5edc7 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 25 Apr 2019 05:25:42 +0800 Subject: [PATCH 005/956] =?UTF-8?q?=E8=B0=83=E6=95=B4xml2lua=E7=9A=84?= =?UTF-8?q?=E4=B8=80=E4=BA=9B=E6=96=87=E4=BB=B6=E4=B8=8E=E5=AF=BC=E5=85=A5?= =?UTF-8?q?=E8=B7=AF=E5=8A=B2,=20=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=BD=9C=E5=9C=A8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/{ => xml2lua}/XmlParser.lua | 201 ++++++++++++---------- lualib/xml2lua/init.lua | 27 +++ lualib/{ => xml2lua}/xml2lua.lua | 44 ++--- lualib/{ => xml2lua}/xmlhandler/README.md | 0 lualib/{ => xml2lua}/xmlhandler/dom.lua | 48 +++--- lualib/{ => xml2lua}/xmlhandler/print.lua | 0 lualib/{ => xml2lua}/xmlhandler/tree.lua | 0 script/text_xml.lua | 48 ++++++ 8 files changed, 229 insertions(+), 139 deletions(-) rename lualib/{ => xml2lua}/XmlParser.lua (74%) create mode 100644 lualib/xml2lua/init.lua rename lualib/{ => xml2lua}/xml2lua.lua (87%) rename lualib/{ => xml2lua}/xmlhandler/README.md (100%) rename lualib/{ => xml2lua}/xmlhandler/dom.lua (78%) rename lualib/{ => xml2lua}/xmlhandler/print.lua (100%) rename lualib/{ => xml2lua}/xmlhandler/tree.lua (100%) create mode 100644 script/text_xml.lua diff --git a/lualib/XmlParser.lua b/lualib/xml2lua/XmlParser.lua similarity index 74% rename from lualib/XmlParser.lua rename to lualib/xml2lua/XmlParser.lua index 9ed8f41a..a9823ea7 100644 --- a/lualib/XmlParser.lua +++ b/lualib/xml2lua/XmlParser.lua @@ -1,18 +1,33 @@ +local type = type +local error = error +local pairs = pairs +local ipairs = ipairs +local tonumber = tonumber +local setmetatable = setmetatable +local getmetatable = getmetatable + +local strfind = string.find +local strgsub = string.gsub +local strsub = string.sub +local strchar = string.char + +local table_remove = table.remove +local table_concat = table.concat --- @module Class providing the actual XML parser. -- Available options are: --- * stripWS --- Strip non-significant whitespace (leading/trailing) +-- * stripWS +-- Strip non-significant whitespace (leading/trailing) -- and do not generate events for empty text elements --- --- * expandEntities --- Expand entities (standard entities + single char --- numeric entities only currently - could be extended +-- +-- * expandEntities +-- Expand entities (standard entities + single char +-- numeric entities only currently - could be extended -- at runtime if suitable DTD parser added elements -- to table (see obj._ENTITIES). May also be possible -- to expand multibyre entities for UTF-8 only --- +-- -- * errorHandler --- Custom error handler function +-- Custom error handler function -- -- NOTE: Boolean options must be set to 'nil' not '0' @@ -23,9 +38,9 @@ local function decimalToHtmlChar(code) local n = tonumber(code) if n >= 0 and n < 256 then - return string.char(n) + return strchar(n) else - return "&#"..code..";" + return table_concat({"&#", code, ";"}) end end @@ -36,9 +51,9 @@ end local function hexadecimalToHtmlChar(code) local n = tonumber(code, 16) if n >= 0 and n < 256 then - return string.char(n) + return strchar(n) else - return "&#x"..code..";" + return table_concat({"&#x", code, ";"}) end end @@ -67,7 +82,7 @@ local XmlParser = { --Matches a closing tag such as or the end of a openning tag such as _TAGEXT = '(%/?)>', - _errstr = { + _errstr = { xmlErr = "Error Parsing XML", declErr = "Error Parsing XMLDecl", declStartErr = "XMLDecl not at start of document", @@ -81,7 +96,7 @@ local XmlParser = { incompleteXmlErr = "Incomplete XML Document", }, - _ENTITIES = { + _ENTITIES = { ["<"] = "<", [">"] = ">", ["&"] = "&", @@ -136,16 +151,16 @@ end --- Removes whitespaces local function stripWS(self, s) if self.options.stripWS then - s = string.gsub(s,'^%s+','') - s = string.gsub(s,'%s+$','') + s = strgsub(s,'^%s+','') + s = strgsub(s,'%s+$','') end return s end -local function parseEntities(self, s) +local function parseEntities(self, s) if self.options.expandEntities then for k,v in pairs(self._ENTITIES) do - s = string.gsub(s,k,v) + s = strgsub(s,k,v) end end @@ -155,25 +170,25 @@ end --- Parses a string representing a tag. --@param s String containing tag text --@return a {name, attrs} table --- where name is the name of the tag and attrs +-- where name is the name of the tag and attrs -- is a table containing the atributtes of the tag local function parseTag(self, s) local tag = { - name = string.gsub(s, self._TAG, '%1'), + name = strgsub(s, self._TAG, '%1'), attrs = {} - } + } - local parseFunction = function (k, v) + local parseFunction = function (k, v) tag.attrs[k] = parseEntities(self, v) - tag.attrs._ = 1 + tag.attrs._ = 1 end - - string.gsub(s, self._ATTR1, parseFunction) - string.gsub(s, self._ATTR2, parseFunction) + + strgsub(s, self._ATTR1, parseFunction) + strgsub(s, self._ATTR2, parseFunction) if tag.attrs._ then tag.attrs._ = nil - else + else tag.attrs = nil end @@ -182,26 +197,26 @@ end local function parseXmlDeclaration(self, xml, f) -- XML Declaration - f.match, f.endMatch, f.text = string.find(xml, self._PI, f.pos) - if not f.match then + f.match, f.endMatch, f.text = strfind(xml, self._PI, f.pos) + if not f.match then err(self, self._errstr.declErr, f.pos) - end + end if f.match ~= 1 then -- Must be at start of doc if present err(self, self._errstr.declStartErr, f.pos) end - local tag = parseTag(self, f.text) + local tag = parseTag(self, f.text) -- TODO: Check if attributes are valid -- Check for version (mandatory) if tag.attrs and tag.attrs.version == nil then err(self, self._errstr.declAttrErr, f.pos) end - if fexists(self.handler, 'decl') then - self.handler:decl(tag, f.match, f.endMatch) - end + if fexists(self.handler, 'decl') then + self.handler:decl(tag, f.match, f.endMatch) + end return tag end @@ -210,14 +225,14 @@ local function parseXmlProcessingInstruction(self, xml, f) local tag = {} -- XML Processing Instruction (PI) - f.match, f.endMatch, f.text = string.find(xml, self._PI, f.pos) - if not f.match then + f.match, f.endMatch, f.text = strfind(xml, self._PI, f.pos) + if not f.match then err(self, self._errstr.piErr, f.pos) - end - if fexists(self.handler, 'pi') then + end + if fexists(self.handler, 'pi') then -- Parse PI attributes & text - tag = parseTag(self, f.text) - local pi = string.sub(f.text, string.len(tag.name)+1) + tag = parseTag(self, f.text) + local pi = strsub(f.text, #tag.name + 1) if pi ~= "" then if tag.attrs then tag.attrs._text = pi @@ -225,19 +240,19 @@ local function parseXmlProcessingInstruction(self, xml, f) tag.attrs = { _text = pi } end end - self.handler:pi(tag, f.match, f.endMatch) + self.handler:pi(tag, f.match, f.endMatch) end return tag end local function parseComment(self, xml, f) - f.match, f.endMatch, f.text = string.find(xml, self._COMMENT, f.pos) - if not f.match then + f.match, f.endMatch, f.text = strfind(xml, self._COMMENT, f.pos) + if not f.match then err(self, self._errstr.commentErr, f.pos) - end + end - if fexists(self.handler, 'comment') then + if fexists(self.handler, 'comment') then f.text = parseEntities(self, stripWS(self, f.text)) self.handler:comment(f.text, next, f.match, f.endMatch) end @@ -246,30 +261,30 @@ end local function _parseDtd(self, xml, pos) -- match,endMatch,root,type,name,uri,internal local m,e,r,t,n,u,i - - m,e,r,t,u,i = string.find(xml, self._DTD1,pos) + + m,e,r,t,u,i = strfind(xml, self._DTD1,pos) if m then - return m, e, {_root=r,_type=t,_uri=u,_internal=i} + return m, e, {_root=r,_type=t,_uri=u,_internal=i} end - m,e,r,t,n,u,i = string.find(xml, self._DTD2,pos) + m,e,r,t,n,u,i = strfind(xml, self._DTD2,pos) if m then - return m, e, {_root=r,_type=t,_name=n,_uri=u,_internal=i} + return m, e, {_root=r,_type=t,_name=n,_uri=u,_internal=i} end - m,e,r,i = string.find(xml, self._DTD3,pos) + m,e,r,i = strfind(xml, self._DTD3,pos) if m then - return m, e, {_root=r,_internal=i} + return m, e, {_root=r,_internal=i} end - m,e,r,t,u = string.find(s,self._DTD4,pos) + m,e,r,t,u = strfind(s,self._DTD4,pos) if m then - return m,e,{_root=r,_type=t,_uri=u} + return m,e,{_root=r,_type=t,_uri=u} end - m,e,r,t,n,u = string.find(s,self._DTD5,pos) + m,e,r,t,n,u = strfind(s,self._DTD5,pos) if m then - return m,e,{_root=r,_type=t,_name=n,_uri=u} + return m,e,{_root=r,_type=t,_name=n,_uri=u} end return nil @@ -277,9 +292,9 @@ end local function parseDtd(self, xml, f) f.match, f.endMatch, attrs = self:_parseDtd(xml, f.pos) - if not f.match then + if not f.match then err(self, self._errstr.dtdErr, f.pos) - end + end if fexists(self.handler, 'dtd') then self.handler:dtd(attrs._root, attrs, f.match, f.endMatch) @@ -287,44 +302,44 @@ local function parseDtd(self, xml, f) end local function parseCdata(self, xml, f) - f.match, f.endMatch, f.text = string.find(xml, self._CDATA, f.pos) - if not f.match then + f.match, f.endMatch, f.text = strfind(xml, self._CDATA, f.pos) + if not f.match then err(self, self._errstr.cdataErr, f.pos) - end + end if fexists(self.handler, 'cdata') then self.handler:cdata(f.text, nil, f.match, f.endMatch) - end + end end --- Parse a Normal tag -- Need check for embedded '>' in attribute value and extend --- match recursively if necessary eg. +-- match recursively if necessary eg. local function parseNormalTag(self, xml, f) --Check for errors while 1 do --If there isn't an attribute without closing quotes (single or double quotes) --then breaks to follow the normal processing of the tag. --Otherwise, try to find where the quotes close. - f.errStart, f.errEnd = string.find(f.tagstr, self._ATTRERR1) + f.errStart, f.errEnd = strfind(f.tagstr, self._ATTRERR1) if f.errEnd == nil then - f.errStart, f.errEnd = string.find(f.tagstr, self._ATTRERR2) + f.errStart, f.errEnd = strfind(f.tagstr, self._ATTRERR2) if f.errEnd == nil then break end end - - f.extStart, f.extEnd, f.endt2 = string.find(xml, self._TAGEXT, f.endMatch+1) - f.tagstr = f.tagstr .. string.sub(xml, f.endMatch, f.extEnd-1) - if not f.match then + + f.extStart, f.extEnd, f.endt2 = strfind(xml, self._TAGEXT, f.endMatch+1) + f.tagstr = f.tagstr .. strsub(xml, f.endMatch, f.extEnd-1) + if not f.match then err(self, self._errstr.xmlErr, f.pos) - end + end f.endMatch = f.extEnd - end + end -- Extract tag name and attrs - local tag = parseTag(self, f.tagstr) + local tag = parseTag(self, f.tagstr) if (f.endt1=="/") then if fexists(self.handler, 'endtag') then @@ -332,13 +347,13 @@ local function parseNormalTag(self, xml, f) -- Shouldn't have any attributes in endtag err(self, string.format("%s (/%s)", self._errstr.endTagErr, tag.name), f.pos) end - if table.remove(self._stack) ~= tag.name then + if table_remove(self._stack) ~= tag.name then err(self, string.format("%s (/%s)", self._errstr.unmatchedTagErr, tag.name), f.pos) end self.handler:endtag(tag, f.match, f.endMatch) end else - table.insert(self._stack, tag.name) + self._stack[#self._stack+1] = tag.name if fexists(self.handler, 'starttag') then self.handler:starttag(tag, f.match, f.endMatch) end @@ -349,7 +364,7 @@ local function parseNormalTag(self, xml, f) -- Self-Closing Tag if (f.endt2=="/") then - table.remove(self._stack) + table_remove(self._stack) if fexists(self.handler, 'endtag') then self.handler:endtag(tag, f.match, f.endMatch) end @@ -361,15 +376,15 @@ end local function parseTagType(self, xml, f) -- Test for tag type - if string.find(string.sub(f.tagstr, 1, 5), "?xml%s") then + if strfind(strsub(f.tagstr, 1, 5), "?xml%s") then parseXmlDeclaration(self, xml, f) - elseif string.sub(f.tagstr, 1, 1) == "?" then + elseif strsub(f.tagstr, 1, 1) == "?" then parseXmlProcessingInstruction(self, xml, f) - elseif string.sub(f.tagstr, 1, 3) == "!--" then + elseif strsub(f.tagstr, 1, 3) == "!--" then parseComment(self, xml, f) - elseif string.sub(f.tagstr, 1, 8) == "!DOCTYPE" then + elseif strsub(f.tagstr, 1, 8) == "!DOCTYPE" then parseDtd(self, xml, f) - elseif string.sub(f.tagstr, 1, 8) == "![CDATA[" then + elseif strsub(f.tagstr, 1, 8) == "![CDATA[" then parseCdata(self, xml, f) else parseNormalTag(self, xml, f) @@ -379,31 +394,31 @@ end --- Get next tag (first pass - fix exceptions below). --@return true if the next tag could be got, false otherwise local function getNextTag(self, xml, f) - f.match, f.endMatch, f.text, f.endt1, f.tagstr, f.endt2 = string.find(xml, self._XML, f.pos) - if not f.match then - if string.find(xml, self._WS, f.pos) then + f.match, f.endMatch, f.text, f.endt1, f.tagstr, f.endt2 = strfind(xml, self._XML, f.pos) + if not f.match then + if strfind(xml, self._WS, f.pos) then -- No more text - check document complete if #self._stack ~= 0 then err(self, self._errstr.incompleteXmlErr, f.pos) else - return false + return false end else -- Unparsable text err(self, self._errstr.xmlErr, f.pos) end - end + end f.text = f.text or '' f.tagstr = f.tagstr or '' f.match = f.match or 0 - + return f.endMatch ~= nil end --Main function which starts the XML parsing process --@param xml the XML string to parse ---@param parseAttributes indicates if tag attributes should be parsed or not. +--@param parseAttributes indicates if tag attributes should be parsed or not. -- If omitted, the default value is true. function XmlParser:parse(xml, parseAttributes) if type(self) ~= "table" or getmetatable(self) ~= XmlParser then @@ -416,15 +431,15 @@ function XmlParser:parse(xml, parseAttributes) self.handler.parseAttributes = parseAttributes - --Stores string.find results and parameters + --Stores strfind results and parameters --and other auxiliar variables local f = { - --string.find return + --strfind return match = 0, endMatch = 0, -- text, end1, tagstr, end2, - --string.find parameters and auxiliar variables + --strfind parameters and auxiliar variables pos = 1, -- startText, endText, -- errStart, errEnd, extStart, extEnd, @@ -434,11 +449,11 @@ function XmlParser:parse(xml, parseAttributes) if not getNextTag(self, xml, f) then break end - + -- Handle leading text f.startText = f.match - f.endText = f.match + string.len(f.text) - 1 - f.match = f.match + string.len(f.text) + f.endText = f.match + #f.text - 1 + f.match = f.match + #f.text f.text = parseEntities(self, stripWS(self, f.text)) if f.text ~= "" and fexists(self.handler, 'text') then self.handler:text(f.text, nil, f.match, f.endText) diff --git a/lualib/xml2lua/init.lua b/lualib/xml2lua/init.lua new file mode 100644 index 00000000..c9adc2cd --- /dev/null +++ b/lualib/xml2lua/init.lua @@ -0,0 +1,27 @@ +-- 对xml2lua的简单封装, 简化xml2lua调用流程 +local xml2lua = require "xml2lua.xml2lua" +local xml2lua_handler = require "xml2lua.xmlhandler.tree" + + +local xml = {} + +-- xml字符串解析 +function xml.parser(xml_data) + local cls = xml2lua.parser(xml2lua_handler:new()) + cls:parse(xml_data) + return cls.handler.root +end + +-- xml文件读取 +function xml.load(xml_path) + if type(xml_path) ~= 'string' or xml_path == '' then + return nil, '无效的xml文件路径.' + end + local xfile, error = xml2lua.loadFile(xml_path) + if xfile then + return xml.parser(xfile) + end + return xfile, error +end + +return xml diff --git a/lualib/xml2lua.lua b/lualib/xml2lua/xml2lua.lua similarity index 87% rename from lualib/xml2lua.lua rename to lualib/xml2lua/xml2lua.lua index bad37276..3caefe12 100755 --- a/lualib/xml2lua.lua +++ b/lualib/xml2lua/xml2lua.lua @@ -1,8 +1,8 @@ ---- @module Module providing a non-validating XML stream parser in Lua. --- +--- @module Module providing a non-validating XML stream parser in Lua. +-- -- Features: -- ========= --- +-- -- * Tokenises well-formed XML (relatively robustly) -- * Flexible handler based event API (see below) -- * Parses all XML Infoset elements - ie. @@ -13,29 +13,29 @@ -- - XML Decl -- - Processing Instructions -- - DOCTYPE declarations --- * Provides limited well-formedness checking +-- * Provides limited well-formedness checking -- (checks for basic syntax & balanced tags only) -- * Flexible whitespace handling (selectable) -- * Entity Handling (selectable) --- +-- -- Limitations: -- ============ --- +-- -- * Non-validating --- * No charset handling --- * No namespace support +-- * No charset handling +-- * No namespace support -- * Shallow well-formedness checking only (fails -- to detect most semantic errors) --- +-- -- API: -- ==== -- --- The parser provides a partially object-oriented API with +-- The parser provides a partially object-oriented API with -- functionality split into tokeniser and handler components. --- +-- -- The handler instance is passed to the tokeniser and receives -- callbacks for each XML element processed (if a suitable handler --- function is defined). The API is conceptually similar to the +-- function is defined). The API is conceptually similar to the -- SAX API but implemented differently. -- -- XML data is passed to the parser instance through the 'parse' @@ -50,7 +50,7 @@ --@author Paul Chakravarti (paulc@passtheaardvark.com) --@author Manoel Campos da Silva Filho local xml2lua = {} -local XmlParser = require("XmlParser") +local XmlParser = require "xml2lua.XmlParser" ---Recursivelly prints a table in an easy-to-ready format --@param tb The table to be printed @@ -59,7 +59,7 @@ local function printableInternal(tb, level) if tb == nil then return end - + level = level or 1 local spaces = string.rep(' ', level*2) for k,v in pairs(tb) do @@ -69,7 +69,7 @@ local function printableInternal(tb, level) else print(spaces .. k..'='..v) end - end + end end ---Instantiates a XmlParser object to parse a XML string @@ -79,16 +79,16 @@ end -- local handler = require("xmlhandler/tree"). --@return a XmlParser object used to parse the XML --@see XmlParser -function xml2lua.parser(handler) +function xml2lua.parser(handler) if handler == xml2lua then error("You must call xml2lua.parse(handler) instead of xml2lua:parse(handler)") end - local options = { + local options = { --Indicates if whitespaces should be striped or not - stripWS = 1, + stripWS = 1, expandEntities = 1, - errorHandler = function(errMsg, pos) + errorHandler = function(errMsg, pos) error(string.format("%s [char=%d]\n", errMsg or "Parse Error", pos)) end } @@ -114,10 +114,10 @@ function xml2lua.toString(t) end for k,v in pairs(t) do - if type(v) == 'table' then + if type(v) == 'table' then v = xml2lua.toString(v) end - res = res .. sep .. string.format("%s=%s", k, v) + res = res .. sep .. string.format("%s=%s", k, v) sep = ',' end res = '{'..res..'}' @@ -134,7 +134,7 @@ function xml2lua.loadFile(xmlFilePath) --Gets the entire file content and stores into a string return f:read("*a") end - + error(e) end diff --git a/lualib/xmlhandler/README.md b/lualib/xml2lua/xmlhandler/README.md similarity index 100% rename from lualib/xmlhandler/README.md rename to lualib/xml2lua/xmlhandler/README.md diff --git a/lualib/xmlhandler/dom.lua b/lualib/xml2lua/xmlhandler/dom.lua similarity index 78% rename from lualib/xmlhandler/dom.lua rename to lualib/xml2lua/xmlhandler/dom.lua index ae87c438..b0bc56b8 100755 --- a/lualib/xmlhandler/dom.lua +++ b/lualib/xml2lua/xmlhandler/dom.lua @@ -1,7 +1,7 @@ ----- @module Handler to generate a DOM-like node tree structure with --- a single ROOT node parent - each node is a table comprising +---- @module Handler to generate a DOM-like node tree structure with +-- a single ROOT node parent - each node is a table comprising -- the fields below. --- +-- -- node = { _name = , -- _type = ROOT|ELEMENT|TEXT|COMMENT|PI|DECL|DTD, -- _attr = { Node attributes - see callback API }, @@ -16,7 +16,7 @@ -- -- Options -- ======= --- options.(comment|pi|dtd|decl)Node = bool +-- options.(comment|pi|dtd|decl)Node = bool -- - Include/exclude given node types -- -- License: @@ -34,15 +34,15 @@ local dom = { ---Parses a start tag. -- @param tag a {name, attrs} table --- where name is the name of the tag and attrs +-- where name is the name of the tag and attrs -- is a table containing the atributtes of the tag function dom:starttag(tag) - local node = { _type = 'ELEMENT', - _name = tag.name, - _attr = tag.attrs, - _children = {n=0} + local node = { _type = 'ELEMENT', + _name = tag.name, + _attr = tag.attrs, + _children = {n=0} } - + if self.root == nil then self.root = node end @@ -55,7 +55,7 @@ end ---Parses an end tag. -- @param tag a {name, attrs} table --- where name is the name of the tag and attrs +-- where name is the name of the tag and attrs -- is a table containing the atributtes of the tag function dom:endtag(tag, s) --Table representing the containing tag of the current tag @@ -71,7 +71,7 @@ end ---Parses a tag content. -- @param text text to process function dom:text(text) - local node = { _type = "TEXT", + local node = { _type = "TEXT", _text = text } table.insert(self.current._children, node) @@ -81,8 +81,8 @@ end -- @param text comment text function dom:comment(text) if self.options.commentNode then - local node = { _type = "COMMENT", - _text = text + local node = { _type = "COMMENT", + _text = text } table.insert(self.current._children, node) end @@ -90,27 +90,27 @@ end --- Parses a XML processing instruction (PI) tag -- @param tag a {name, attrs} table --- where name is the name of the tag and attrs +-- where name is the name of the tag and attrs -- is a table containing the atributtes of the tag function dom:pi(tag) if self.options.piNode then - local node = { _type = "PI", + local node = { _type = "PI", _name = tag.name, - _attr = tag.attrs, - } + _attr = tag.attrs, + } table.insert(self.current._children, node) end end ---Parse the XML declaration line (the line that indicates the XML version). -- @param tag a {name, attrs} table --- where name is the name of the tag and attrs +-- where name is the name of the tag and attrs -- is a table containing the atributtes of the tag function dom:decl(tag) if self.options.declNode then - local node = { _type = "DECL", + local node = { _type = "DECL", _name = tag.name, - _attr = tag.attrs, + _attr = tag.attrs, } table.insert(self.current._children, node) end @@ -118,13 +118,13 @@ end ---Parses a DTD tag. -- @param tag a {name, attrs} table --- where name is the name of the tag and attrs +-- where name is the name of the tag and attrs -- is a table containing the atributtes of the tag function dom:dtd(tag) if self.options.dtdNode then - local node = { _type = "DTD", + local node = { _type = "DTD", _name = tag.name, - _attr = tag.attrs, + _attr = tag.attrs, } table.insert(self.current._children, node) end diff --git a/lualib/xmlhandler/print.lua b/lualib/xml2lua/xmlhandler/print.lua similarity index 100% rename from lualib/xmlhandler/print.lua rename to lualib/xml2lua/xmlhandler/print.lua diff --git a/lualib/xmlhandler/tree.lua b/lualib/xml2lua/xmlhandler/tree.lua similarity index 100% rename from lualib/xmlhandler/tree.lua rename to lualib/xml2lua/xmlhandler/tree.lua diff --git a/script/text_xml.lua b/script/text_xml.lua new file mode 100644 index 00000000..5ed16e9a --- /dev/null +++ b/script/text_xml.lua @@ -0,0 +1,48 @@ +local xml2lua = require "xml2lua" +require "utils" + +local xml = [[ + + + 老虎 + meta + + + 狮子 + meta + + + + 水果糖 + 車先生 + + + + 买买买 + 玩玩玩 + 逛逛逛 + + 肉肉 + 小宝贝 + 小QQ + 車爪鱼 + + + + +]] + +-- benchmark time: ./cfadmin 耗时:3.6xx/Sec +for i = 1, 10000 do + xml2lua.parser(xml) +end + +-- 打印解析后的表结构 +local tab = xml2lua.parser(xml) +var_dump(tab) + +-- 原版xml2lua打印会出现相等的情况 +-- 这在cf中可能导致不可预知的情况. +local tab1 = xml2lua.parser(xml) +local tab2 = xml2lua.parser(xml) +print(tab1, tab2) From b0f9df94e1b24d1a9a29a8ef26f061c6a96f868e Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 25 Apr 2019 06:20:10 +0800 Subject: [PATCH 006/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0lpeg=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/Makefile | 6 +- luaclib/src/lhttpparser/lhttpparser.c | 7 +- luaclib/src/lpeg/HISTORY | 100 ++ luaclib/src/lpeg/lpcap.c | 555 +++++++++ luaclib/src/lpeg/lpcap.h | 57 + luaclib/src/lpeg/lpcode.c | 1014 ++++++++++++++++ luaclib/src/lpeg/lpcode.h | 40 + luaclib/src/lpeg/lpeg-128.gif | Bin 0 -> 4923 bytes luaclib/src/lpeg/lpeg.html | 1439 +++++++++++++++++++++++ luaclib/src/lpeg/lpprint.c | 244 ++++ luaclib/src/lpeg/lpprint.h | 36 + luaclib/src/lpeg/lptree.c | 1305 +++++++++++++++++++++ luaclib/src/lpeg/lptree.h | 82 ++ luaclib/src/lpeg/lptypes.h | 145 +++ luaclib/src/lpeg/lpvm.c | 374 ++++++ luaclib/src/lpeg/lpvm.h | 58 + luaclib/src/lpeg/makefile | 66 ++ luaclib/src/lpeg/re.html | 494 ++++++++ luaclib/src/lpeg/re.lua | 267 +++++ luaclib/src/lpeg/test.lua | 1523 +++++++++++++++++++++++++ 20 files changed, 7805 insertions(+), 7 deletions(-) create mode 100644 luaclib/src/lpeg/HISTORY create mode 100644 luaclib/src/lpeg/lpcap.c create mode 100644 luaclib/src/lpeg/lpcap.h create mode 100644 luaclib/src/lpeg/lpcode.c create mode 100644 luaclib/src/lpeg/lpcode.h create mode 100644 luaclib/src/lpeg/lpeg-128.gif create mode 100644 luaclib/src/lpeg/lpeg.html create mode 100644 luaclib/src/lpeg/lpprint.c create mode 100644 luaclib/src/lpeg/lpprint.h create mode 100644 luaclib/src/lpeg/lptree.c create mode 100644 luaclib/src/lpeg/lptree.h create mode 100644 luaclib/src/lpeg/lptypes.h create mode 100644 luaclib/src/lpeg/lpvm.c create mode 100644 luaclib/src/lpeg/lpvm.h create mode 100644 luaclib/src/lpeg/makefile create mode 100644 luaclib/src/lpeg/re.html create mode 100644 luaclib/src/lpeg/re.lua create mode 100755 luaclib/src/lpeg/test.lua diff --git a/luaclib/Makefile b/luaclib/Makefile index 112f2121..83ac640b 100644 --- a/luaclib/Makefile +++ b/luaclib/Makefile @@ -6,7 +6,7 @@ default : @echo "Please use 'make clean' command to clean all." @echo "=======================================" -INCLUDES += -I/usr/local/include +INCLUDES += -I/usr/local/include LIBS += -L/usr/local/lib -L./ -L../ # CFLAGS = -Wall -Os -fPIC --shared -I/usr/local/include -L./ -L../ -DJEMALLOC -ljemalloc # CFLAGS = -Wall -Os -fPIC --shared -I/usr/local/include -L./ -L../ -DTCMALLOC -ltcmalloc @@ -21,8 +21,9 @@ build : $(CC) -o task.so src/ltask.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore $(CC) -o crypt.so src/lcrypt.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore ### 以下为预留第三方库编译位置 ### - cd src/lhttpparser && rm -rf *.o *.so && make build# 增加PicoHTTPParser库 + cd src/lhttpparser && rm -rf *.o *.so && make build # 增加PicoHTTPParser库 cd src/lcjson && rm -rf *.o *.so && make build # 增加cjson库 + cd src/lpeg && rm -rf *.o *.so && make build # 增加lpeg库 rebuild : rm -rf *.o *.so @@ -35,6 +36,7 @@ rebuild : ### 以下为预留第三方库编译位置 ### cd src/lhttpparser && rm -rf *.o *.so && make rebuild # 增加PicoHTTPParser库 cd src/lcjson && rm -rf *.o *.so && make rebuild # 增加cjson库 + cd src/lpeg && rm -rf *.o *.so && make rebuild # 增加lpeg库 clean : rm -rf *.so diff --git a/luaclib/src/lhttpparser/lhttpparser.c b/luaclib/src/lhttpparser/lhttpparser.c index 854f542a..1a557353 100644 --- a/luaclib/src/lhttpparser/lhttpparser.c +++ b/luaclib/src/lhttpparser/lhttpparser.c @@ -1,9 +1,6 @@ #define LUA_LIB -#include -#include -#include -#include +#include "../../../src/core.h" #include "httpparser.h" int @@ -120,4 +117,4 @@ luaopen_httpparser(lua_State *L) { }; luaL_newlib(L, httpparser_libs); return 1; -} \ No newline at end of file +} diff --git a/luaclib/src/lpeg/HISTORY b/luaclib/src/lpeg/HISTORY new file mode 100644 index 00000000..66a8e14d --- /dev/null +++ b/luaclib/src/lpeg/HISTORY @@ -0,0 +1,100 @@ +HISTORY for LPeg 1.0.2 + +* Changes from version 1.0.1 to 1.0.2 + --------------------------------- + + some bugs fixed + +* Changes from version 0.12 to 1.0.1 + --------------------------------- + + group "names" can be any Lua value + + some bugs fixed + + other small improvements + +* Changes from version 0.11 to 0.12 + --------------------------------- + + no "unsigned short" limit for pattern sizes + + mathtime captures considered nullable + + some bugs fixed + +* Changes from version 0.10 to 0.11 + ------------------------------- + + complete reimplementation of the code generator + + new syntax for table captures + + new functions in module 're' + + other small improvements + +* Changes from version 0.9 to 0.10 + ------------------------------- + + backtrack stack has configurable size + + better error messages + + Notation for non-terminals in 're' back to A instead o + + experimental look-behind pattern + + support for external extensions + + works with Lua 5.2 + + consumes less C stack + + - "and" predicates do not keep captures + +* Changes from version 0.8 to 0.9 + ------------------------------- + + The accumulator capture was replaced by a fold capture; + programs that used the old 'lpeg.Ca' will need small changes. + + Some support for character classes from old C locales. + + A new named-group capture. + +* Changes from version 0.7 to 0.8 + ------------------------------- + + New "match-time" capture. + + New "argument capture" that allows passing arguments into the pattern. + + Better documentation for 're'. + + Several small improvements for 're'. + + The 're' module has an incompatibility with previous versions: + now, any use of a non-terminal must be enclosed in angle brackets + (like ). + +* Changes from version 0.6 to 0.7 + ------------------------------- + + Several improvements in module 're': + - better documentation; + - support for most captures (all but accumulator); + - limited repetitions p{n,m}. + + Small improvements in efficiency. + + Several small bugs corrected (special thanks to Hans Hagen + and Taco Hoekwater). + +* Changes from version 0.5 to 0.6 + ------------------------------- + + Support for non-numeric indices in grammars. + + Some bug fixes (thanks to the luatex team). + + Some new optimizations; (thanks to Mike Pall). + + A new page layout (thanks to Andre Carregal). + + Minimal documentation for module 're'. + +* Changes from version 0.4 to 0.5 + ------------------------------- + + Several optimizations. + + lpeg.P now accepts booleans. + + Some new examples. + + A proper license. + + Several small improvements. + +* Changes from version 0.3 to 0.4 + ------------------------------- + + Static check for loops in repetitions and grammars. + + Removed label option in captures. + + The implementation of captures uses less memory. + +* Changes from version 0.2 to 0.3 + ------------------------------- + + User-defined patterns in Lua. + + Several new captures. + +* Changes from version 0.1 to 0.2 + ------------------------------- + + Several small corrections. + + Handles embedded zeros like any other character. + + Capture "name" can be any Lua value. + + Unlimited number of captures. + + Match gets an optional initial position. + +(end of HISTORY) diff --git a/luaclib/src/lpeg/lpcap.c b/luaclib/src/lpeg/lpcap.c new file mode 100644 index 00000000..b332fde4 --- /dev/null +++ b/luaclib/src/lpeg/lpcap.c @@ -0,0 +1,555 @@ +/* +** $Id: lpcap.c $ +** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license) +*/ + +#include "lua.h" +#include "lauxlib.h" + +#include "lpcap.h" +#include "lptypes.h" + + +#define captype(cap) ((cap)->kind) + +#define isclosecap(cap) (captype(cap) == Cclose) + +#define closeaddr(c) ((c)->s + (c)->siz - 1) + +#define isfullcap(cap) ((cap)->siz != 0) + +#define getfromktable(cs,v) lua_rawgeti((cs)->L, ktableidx((cs)->ptop), v) + +#define pushluaval(cs) getfromktable(cs, (cs)->cap->idx) + + + +/* +** Put at the cache for Lua values the value indexed by 'v' in ktable +** of the running pattern (if it is not there yet); returns its index. +*/ +static int updatecache (CapState *cs, int v) { + int idx = cs->ptop + 1; /* stack index of cache for Lua values */ + if (v != cs->valuecached) { /* not there? */ + getfromktable(cs, v); /* get value from 'ktable' */ + lua_replace(cs->L, idx); /* put it at reserved stack position */ + cs->valuecached = v; /* keep track of what is there */ + } + return idx; +} + + +static int pushcapture (CapState *cs); + + +/* +** Goes back in a list of captures looking for an open capture +** corresponding to a close +*/ +static Capture *findopen (Capture *cap) { + int n = 0; /* number of closes waiting an open */ + for (;;) { + cap--; + if (isclosecap(cap)) n++; /* one more open to skip */ + else if (!isfullcap(cap)) + if (n-- == 0) return cap; + } +} + + +/* +** Go to the next capture +*/ +static void nextcap (CapState *cs) { + Capture *cap = cs->cap; + if (!isfullcap(cap)) { /* not a single capture? */ + int n = 0; /* number of opens waiting a close */ + for (;;) { /* look for corresponding close */ + cap++; + if (isclosecap(cap)) { + if (n-- == 0) break; + } + else if (!isfullcap(cap)) n++; + } + } + cs->cap = cap + 1; /* + 1 to skip last close (or entire single capture) */ +} + + +/* +** Push on the Lua stack all values generated by nested captures inside +** the current capture. Returns number of values pushed. 'addextra' +** makes it push the entire match after all captured values. The +** entire match is pushed also if there are no other nested values, +** so the function never returns zero. +*/ +static int pushnestedvalues (CapState *cs, int addextra) { + Capture *co = cs->cap; + if (isfullcap(cs->cap++)) { /* no nested captures? */ + lua_pushlstring(cs->L, co->s, co->siz - 1); /* push whole match */ + return 1; /* that is it */ + } + else { + int n = 0; + while (!isclosecap(cs->cap)) /* repeat for all nested patterns */ + n += pushcapture(cs); + if (addextra || n == 0) { /* need extra? */ + lua_pushlstring(cs->L, co->s, cs->cap->s - co->s); /* push whole match */ + n++; + } + cs->cap++; /* skip close entry */ + return n; + } +} + + +/* +** Push only the first value generated by nested captures +*/ +static void pushonenestedvalue (CapState *cs) { + int n = pushnestedvalues(cs, 0); + if (n > 1) + lua_pop(cs->L, n - 1); /* pop extra values */ +} + + +/* +** Try to find a named group capture with the name given at the top of +** the stack; goes backward from 'cap'. +*/ +static Capture *findback (CapState *cs, Capture *cap) { + lua_State *L = cs->L; + while (cap-- > cs->ocap) { /* repeat until end of list */ + if (isclosecap(cap)) + cap = findopen(cap); /* skip nested captures */ + else if (!isfullcap(cap)) + continue; /* opening an enclosing capture: skip and get previous */ + if (captype(cap) == Cgroup) { + getfromktable(cs, cap->idx); /* get group name */ + if (lp_equal(L, -2, -1)) { /* right group? */ + lua_pop(L, 2); /* remove reference name and group name */ + return cap; + } + else lua_pop(L, 1); /* remove group name */ + } + } + luaL_error(L, "back reference '%s' not found", lua_tostring(L, -1)); + return NULL; /* to avoid warnings */ +} + + +/* +** Back-reference capture. Return number of values pushed. +*/ +static int backrefcap (CapState *cs) { + int n; + Capture *curr = cs->cap; + pushluaval(cs); /* reference name */ + cs->cap = findback(cs, curr); /* find corresponding group */ + n = pushnestedvalues(cs, 0); /* push group's values */ + cs->cap = curr + 1; + return n; +} + + +/* +** Table capture: creates a new table and populates it with nested +** captures. +*/ +static int tablecap (CapState *cs) { + lua_State *L = cs->L; + int n = 0; + lua_newtable(L); + if (isfullcap(cs->cap++)) + return 1; /* table is empty */ + while (!isclosecap(cs->cap)) { + if (captype(cs->cap) == Cgroup && cs->cap->idx != 0) { /* named group? */ + pushluaval(cs); /* push group name */ + pushonenestedvalue(cs); + lua_settable(L, -3); + } + else { /* not a named group */ + int i; + int k = pushcapture(cs); + for (i = k; i > 0; i--) /* store all values into table */ + lua_rawseti(L, -(i + 1), n + i); + n += k; + } + } + cs->cap++; /* skip close entry */ + return 1; /* number of values pushed (only the table) */ +} + + +/* +** Table-query capture +*/ +static int querycap (CapState *cs) { + int idx = cs->cap->idx; + pushonenestedvalue(cs); /* get nested capture */ + lua_gettable(cs->L, updatecache(cs, idx)); /* query cap. value at table */ + if (!lua_isnil(cs->L, -1)) + return 1; + else { /* no value */ + lua_pop(cs->L, 1); /* remove nil */ + return 0; + } +} + + +/* +** Fold capture +*/ +static int foldcap (CapState *cs) { + int n; + lua_State *L = cs->L; + int idx = cs->cap->idx; + if (isfullcap(cs->cap++) || /* no nested captures? */ + isclosecap(cs->cap) || /* no nested captures (large subject)? */ + (n = pushcapture(cs)) == 0) /* nested captures with no values? */ + return luaL_error(L, "no initial value for fold capture"); + if (n > 1) + lua_pop(L, n - 1); /* leave only one result for accumulator */ + while (!isclosecap(cs->cap)) { + lua_pushvalue(L, updatecache(cs, idx)); /* get folding function */ + lua_insert(L, -2); /* put it before accumulator */ + n = pushcapture(cs); /* get next capture's values */ + lua_call(L, n + 1, 1); /* call folding function */ + } + cs->cap++; /* skip close entry */ + return 1; /* only accumulator left on the stack */ +} + + +/* +** Function capture +*/ +static int functioncap (CapState *cs) { + int n; + int top = lua_gettop(cs->L); + pushluaval(cs); /* push function */ + n = pushnestedvalues(cs, 0); /* push nested captures */ + lua_call(cs->L, n, LUA_MULTRET); /* call function */ + return lua_gettop(cs->L) - top; /* return function's results */ +} + + +/* +** Select capture +*/ +static int numcap (CapState *cs) { + int idx = cs->cap->idx; /* value to select */ + if (idx == 0) { /* no values? */ + nextcap(cs); /* skip entire capture */ + return 0; /* no value produced */ + } + else { + int n = pushnestedvalues(cs, 0); + if (n < idx) /* invalid index? */ + return luaL_error(cs->L, "no capture '%d'", idx); + else { + lua_pushvalue(cs->L, -(n - idx + 1)); /* get selected capture */ + lua_replace(cs->L, -(n + 1)); /* put it in place of 1st capture */ + lua_pop(cs->L, n - 1); /* remove other captures */ + return 1; + } + } +} + + +/* +** Return the stack index of the first runtime capture in the given +** list of captures (or zero if no runtime captures) +*/ +int finddyncap (Capture *cap, Capture *last) { + for (; cap < last; cap++) { + if (cap->kind == Cruntime) + return cap->idx; /* stack position of first capture */ + } + return 0; /* no dynamic captures in this segment */ +} + + +/* +** Calls a runtime capture. Returns number of captures "removed" by the +** call, that is, those inside the group capture. Captures to be added +** are on the Lua stack. +*/ +int runtimecap (CapState *cs, Capture *close, const char *s, int *rem) { + int n, id; + lua_State *L = cs->L; + int otop = lua_gettop(L); + Capture *open = findopen(close); /* get open group capture */ + assert(captype(open) == Cgroup); + id = finddyncap(open, close); /* get first dynamic capture argument */ + close->kind = Cclose; /* closes the group */ + close->s = s; + cs->cap = open; cs->valuecached = 0; /* prepare capture state */ + luaL_checkstack(L, 4, "too many runtime captures"); + pushluaval(cs); /* push function to be called */ + lua_pushvalue(L, SUBJIDX); /* push original subject */ + lua_pushinteger(L, s - cs->s + 1); /* push current position */ + n = pushnestedvalues(cs, 0); /* push nested captures */ + lua_call(L, n + 2, LUA_MULTRET); /* call dynamic function */ + if (id > 0) { /* are there old dynamic captures to be removed? */ + int i; + for (i = id; i <= otop; i++) + lua_remove(L, id); /* remove old dynamic captures */ + *rem = otop - id + 1; /* total number of dynamic captures removed */ + } + else + *rem = 0; /* no dynamic captures removed */ + return close - open - 1; /* number of captures to be removed */ +} + + +/* +** Auxiliary structure for substitution and string captures: keep +** information about nested captures for future use, avoiding to push +** string results into Lua +*/ +typedef struct StrAux { + int isstring; /* whether capture is a string */ + union { + Capture *cp; /* if not a string, respective capture */ + struct { /* if it is a string... */ + const char *s; /* ... starts here */ + const char *e; /* ... ends here */ + } s; + } u; +} StrAux; + +#define MAXSTRCAPS 10 + +/* +** Collect values from current capture into array 'cps'. Current +** capture must be Cstring (first call) or Csimple (recursive calls). +** (In first call, fills %0 with whole match for Cstring.) +** Returns number of elements in the array that were filled. +*/ +static int getstrcaps (CapState *cs, StrAux *cps, int n) { + int k = n++; + cps[k].isstring = 1; /* get string value */ + cps[k].u.s.s = cs->cap->s; /* starts here */ + if (!isfullcap(cs->cap++)) { /* nested captures? */ + while (!isclosecap(cs->cap)) { /* traverse them */ + if (n >= MAXSTRCAPS) /* too many captures? */ + nextcap(cs); /* skip extra captures (will not need them) */ + else if (captype(cs->cap) == Csimple) /* string? */ + n = getstrcaps(cs, cps, n); /* put info. into array */ + else { + cps[n].isstring = 0; /* not a string */ + cps[n].u.cp = cs->cap; /* keep original capture */ + nextcap(cs); + n++; + } + } + cs->cap++; /* skip close */ + } + cps[k].u.s.e = closeaddr(cs->cap - 1); /* ends here */ + return n; +} + + +/* +** add next capture value (which should be a string) to buffer 'b' +*/ +static int addonestring (luaL_Buffer *b, CapState *cs, const char *what); + + +/* +** String capture: add result to buffer 'b' (instead of pushing +** it into the stack) +*/ +static void stringcap (luaL_Buffer *b, CapState *cs) { + StrAux cps[MAXSTRCAPS]; + int n; + size_t len, i; + const char *fmt; /* format string */ + fmt = lua_tolstring(cs->L, updatecache(cs, cs->cap->idx), &len); + n = getstrcaps(cs, cps, 0) - 1; /* collect nested captures */ + for (i = 0; i < len; i++) { /* traverse them */ + if (fmt[i] != '%') /* not an escape? */ + luaL_addchar(b, fmt[i]); /* add it to buffer */ + else if (fmt[++i] < '0' || fmt[i] > '9') /* not followed by a digit? */ + luaL_addchar(b, fmt[i]); /* add to buffer */ + else { + int l = fmt[i] - '0'; /* capture index */ + if (l > n) + luaL_error(cs->L, "invalid capture index (%d)", l); + else if (cps[l].isstring) + luaL_addlstring(b, cps[l].u.s.s, cps[l].u.s.e - cps[l].u.s.s); + else { + Capture *curr = cs->cap; + cs->cap = cps[l].u.cp; /* go back to evaluate that nested capture */ + if (!addonestring(b, cs, "capture")) + luaL_error(cs->L, "no values in capture index %d", l); + cs->cap = curr; /* continue from where it stopped */ + } + } + } +} + + +/* +** Substitution capture: add result to buffer 'b' +*/ +static void substcap (luaL_Buffer *b, CapState *cs) { + const char *curr = cs->cap->s; + if (isfullcap(cs->cap)) /* no nested captures? */ + luaL_addlstring(b, curr, cs->cap->siz - 1); /* keep original text */ + else { + cs->cap++; /* skip open entry */ + while (!isclosecap(cs->cap)) { /* traverse nested captures */ + const char *next = cs->cap->s; + luaL_addlstring(b, curr, next - curr); /* add text up to capture */ + if (addonestring(b, cs, "replacement")) + curr = closeaddr(cs->cap - 1); /* continue after match */ + else /* no capture value */ + curr = next; /* keep original text in final result */ + } + luaL_addlstring(b, curr, cs->cap->s - curr); /* add last piece of text */ + } + cs->cap++; /* go to next capture */ +} + + +/* +** Evaluates a capture and adds its first value to buffer 'b'; returns +** whether there was a value +*/ +static int addonestring (luaL_Buffer *b, CapState *cs, const char *what) { + switch (captype(cs->cap)) { + case Cstring: + stringcap(b, cs); /* add capture directly to buffer */ + return 1; + case Csubst: + substcap(b, cs); /* add capture directly to buffer */ + return 1; + default: { + lua_State *L = cs->L; + int n = pushcapture(cs); + if (n > 0) { + if (n > 1) lua_pop(L, n - 1); /* only one result */ + if (!lua_isstring(L, -1)) + luaL_error(L, "invalid %s value (a %s)", what, luaL_typename(L, -1)); + luaL_addvalue(b); + } + return n; + } + } +} + + +#if !defined(MAXRECLEVEL) +#define MAXRECLEVEL 200 +#endif + + +/* +** Push all values of the current capture into the stack; returns +** number of values pushed +*/ +static int pushcapture (CapState *cs) { + lua_State *L = cs->L; + int res; + luaL_checkstack(L, 4, "too many captures"); + if (cs->reclevel++ > MAXRECLEVEL) + return luaL_error(L, "subcapture nesting too deep"); + switch (captype(cs->cap)) { + case Cposition: { + lua_pushinteger(L, cs->cap->s - cs->s + 1); + cs->cap++; + res = 1; + break; + } + case Cconst: { + pushluaval(cs); + cs->cap++; + res = 1; + break; + } + case Carg: { + int arg = (cs->cap++)->idx; + if (arg + FIXEDARGS > cs->ptop) + return luaL_error(L, "reference to absent extra argument #%d", arg); + lua_pushvalue(L, arg + FIXEDARGS); + res = 1; + break; + } + case Csimple: { + int k = pushnestedvalues(cs, 1); + lua_insert(L, -k); /* make whole match be first result */ + res = k; + break; + } + case Cruntime: { + lua_pushvalue(L, (cs->cap++)->idx); /* value is in the stack */ + res = 1; + break; + } + case Cstring: { + luaL_Buffer b; + luaL_buffinit(L, &b); + stringcap(&b, cs); + luaL_pushresult(&b); + res = 1; + break; + } + case Csubst: { + luaL_Buffer b; + luaL_buffinit(L, &b); + substcap(&b, cs); + luaL_pushresult(&b); + res = 1; + break; + } + case Cgroup: { + if (cs->cap->idx == 0) /* anonymous group? */ + res = pushnestedvalues(cs, 0); /* add all nested values */ + else { /* named group: add no values */ + nextcap(cs); /* skip capture */ + res = 0; + } + break; + } + case Cbackref: res = backrefcap(cs); break; + case Ctable: res = tablecap(cs); break; + case Cfunction: res = functioncap(cs); break; + case Cnum: res = numcap(cs); break; + case Cquery: res = querycap(cs); break; + case Cfold: res = foldcap(cs); break; + default: assert(0); res = 0; + } + cs->reclevel--; + return res; +} + + +/* +** Prepare a CapState structure and traverse the entire list of +** captures in the stack pushing its results. 's' is the subject +** string, 'r' is the final position of the match, and 'ptop' +** the index in the stack where some useful values were pushed. +** Returns the number of results pushed. (If the list produces no +** results, push the final position of the match.) +*/ +int getcaptures (lua_State *L, const char *s, const char *r, int ptop) { + Capture *capture = (Capture *)lua_touserdata(L, caplistidx(ptop)); + int n = 0; + if (!isclosecap(capture)) { /* is there any capture? */ + CapState cs; + cs.ocap = cs.cap = capture; cs.L = L; cs.reclevel = 0; + cs.s = s; cs.valuecached = 0; cs.ptop = ptop; + do { /* collect their values */ + n += pushcapture(&cs); + } while (!isclosecap(cs.cap)); + } + if (n == 0) { /* no capture values? */ + lua_pushinteger(L, r - s + 1); /* return only end position */ + n = 1; + } + return n; +} + + diff --git a/luaclib/src/lpeg/lpcap.h b/luaclib/src/lpeg/lpcap.h new file mode 100644 index 00000000..dc10d696 --- /dev/null +++ b/luaclib/src/lpeg/lpcap.h @@ -0,0 +1,57 @@ +/* +** $Id: lpcap.h $ +*/ + +#if !defined(lpcap_h) +#define lpcap_h + + +#include "lptypes.h" + + +/* kinds of captures */ +typedef enum CapKind { + Cclose, /* not used in trees */ + Cposition, + Cconst, /* ktable[key] is Lua constant */ + Cbackref, /* ktable[key] is "name" of group to get capture */ + Carg, /* 'key' is arg's number */ + Csimple, /* next node is pattern */ + Ctable, /* next node is pattern */ + Cfunction, /* ktable[key] is function; next node is pattern */ + Cquery, /* ktable[key] is table; next node is pattern */ + Cstring, /* ktable[key] is string; next node is pattern */ + Cnum, /* numbered capture; 'key' is number of value to return */ + Csubst, /* substitution capture; next node is pattern */ + Cfold, /* ktable[key] is function; next node is pattern */ + Cruntime, /* not used in trees (is uses another type for tree) */ + Cgroup /* ktable[key] is group's "name" */ +} CapKind; + + +typedef struct Capture { + const char *s; /* subject position */ + unsigned short idx; /* extra info (group name, arg index, etc.) */ + byte kind; /* kind of capture */ + byte siz; /* size of full capture + 1 (0 = not a full capture) */ +} Capture; + + +typedef struct CapState { + Capture *cap; /* current capture */ + Capture *ocap; /* (original) capture list */ + lua_State *L; + int ptop; /* index of last argument to 'match' */ + const char *s; /* original string */ + int valuecached; /* value stored in cache slot */ + int reclevel; /* recursion level */ +} CapState; + + +int runtimecap (CapState *cs, Capture *close, const char *s, int *rem); +int getcaptures (lua_State *L, const char *s, const char *r, int ptop); +int finddyncap (Capture *cap, Capture *last); + +#endif + + diff --git a/luaclib/src/lpeg/lpcode.c b/luaclib/src/lpeg/lpcode.c new file mode 100644 index 00000000..39234597 --- /dev/null +++ b/luaclib/src/lpeg/lpcode.c @@ -0,0 +1,1014 @@ +/* +** $Id: lpcode.c $ +** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license) +*/ + +#include + + +#include "lua.h" +#include "lauxlib.h" + +#include "lptypes.h" +#include "lpcode.h" + + +/* signals a "no-instruction */ +#define NOINST -1 + + + +static const Charset fullset_ = + {{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; + +static const Charset *fullset = &fullset_; + +/* +** {====================================================== +** Analysis and some optimizations +** ======================================================= +*/ + +/* +** Check whether a charset is empty (returns IFail), singleton (IChar), +** full (IAny), or none of those (ISet). When singleton, '*c' returns +** which character it is. (When generic set, the set was the input, +** so there is no need to return it.) +*/ +static Opcode charsettype (const byte *cs, int *c) { + int count = 0; /* number of characters in the set */ + int i; + int candidate = -1; /* candidate position for the singleton char */ + for (i = 0; i < CHARSETSIZE; i++) { /* for each byte */ + int b = cs[i]; + if (b == 0) { /* is byte empty? */ + if (count > 1) /* was set neither empty nor singleton? */ + return ISet; /* neither full nor empty nor singleton */ + /* else set is still empty or singleton */ + } + else if (b == 0xFF) { /* is byte full? */ + if (count < (i * BITSPERCHAR)) /* was set not full? */ + return ISet; /* neither full nor empty nor singleton */ + else count += BITSPERCHAR; /* set is still full */ + } + else if ((b & (b - 1)) == 0) { /* has byte only one bit? */ + if (count > 0) /* was set not empty? */ + return ISet; /* neither full nor empty nor singleton */ + else { /* set has only one char till now; track it */ + count++; + candidate = i; + } + } + else return ISet; /* byte is neither empty, full, nor singleton */ + } + switch (count) { + case 0: return IFail; /* empty set */ + case 1: { /* singleton; find character bit inside byte */ + int b = cs[candidate]; + *c = candidate * BITSPERCHAR; + if ((b & 0xF0) != 0) { *c += 4; b >>= 4; } + if ((b & 0x0C) != 0) { *c += 2; b >>= 2; } + if ((b & 0x02) != 0) { *c += 1; } + return IChar; + } + default: { + assert(count == CHARSETSIZE * BITSPERCHAR); /* full set */ + return IAny; + } + } +} + + +/* +** A few basic operations on Charsets +*/ +static void cs_complement (Charset *cs) { + loopset(i, cs->cs[i] = ~cs->cs[i]); +} + +static int cs_equal (const byte *cs1, const byte *cs2) { + loopset(i, if (cs1[i] != cs2[i]) return 0); + return 1; +} + +static int cs_disjoint (const Charset *cs1, const Charset *cs2) { + loopset(i, if ((cs1->cs[i] & cs2->cs[i]) != 0) return 0;) + return 1; +} + + +/* +** If 'tree' is a 'char' pattern (TSet, TChar, TAny), convert it into a +** charset and return 1; else return 0. +*/ +int tocharset (TTree *tree, Charset *cs) { + switch (tree->tag) { + case TSet: { /* copy set */ + loopset(i, cs->cs[i] = treebuffer(tree)[i]); + return 1; + } + case TChar: { /* only one char */ + assert(0 <= tree->u.n && tree->u.n <= UCHAR_MAX); + loopset(i, cs->cs[i] = 0); /* erase all chars */ + setchar(cs->cs, tree->u.n); /* add that one */ + return 1; + } + case TAny: { + loopset(i, cs->cs[i] = 0xFF); /* add all characters to the set */ + return 1; + } + default: return 0; + } +} + + +/* +** Visit a TCall node taking care to stop recursion. If node not yet +** visited, return 'f(sib2(tree))', otherwise return 'def' (default +** value) +*/ +static int callrecursive (TTree *tree, int f (TTree *t), int def) { + int key = tree->key; + assert(tree->tag == TCall); + assert(sib2(tree)->tag == TRule); + if (key == 0) /* node already visited? */ + return def; /* return default value */ + else { /* first visit */ + int result; + tree->key = 0; /* mark call as already visited */ + result = f(sib2(tree)); /* go to called rule */ + tree->key = key; /* restore tree */ + return result; + } +} + + +/* +** Check whether a pattern tree has captures +*/ +int hascaptures (TTree *tree) { + tailcall: + switch (tree->tag) { + case TCapture: case TRunTime: + return 1; + case TCall: + return callrecursive(tree, hascaptures, 0); + case TRule: /* do not follow siblings */ + tree = sib1(tree); goto tailcall; + case TOpenCall: assert(0); + default: { + switch (numsiblings[tree->tag]) { + case 1: /* return hascaptures(sib1(tree)); */ + tree = sib1(tree); goto tailcall; + case 2: + if (hascaptures(sib1(tree))) + return 1; + /* else return hascaptures(sib2(tree)); */ + tree = sib2(tree); goto tailcall; + default: assert(numsiblings[tree->tag] == 0); return 0; + } + } + } +} + + +/* +** Checks how a pattern behaves regarding the empty string, +** in one of two different ways: +** A pattern is *nullable* if it can match without consuming any character; +** A pattern is *nofail* if it never fails for any string +** (including the empty string). +** The difference is only for predicates and run-time captures; +** for other patterns, the two properties are equivalent. +** (With predicates, &'a' is nullable but not nofail. Of course, +** nofail => nullable.) +** These functions are all convervative in the following way: +** p is nullable => nullable(p) +** nofail(p) => p cannot fail +** The function assumes that TOpenCall is not nullable; +** this will be checked again when the grammar is fixed. +** Run-time captures can do whatever they want, so the result +** is conservative. +*/ +int checkaux (TTree *tree, int pred) { + tailcall: + switch (tree->tag) { + case TChar: case TSet: case TAny: + case TFalse: case TOpenCall: + return 0; /* not nullable */ + case TRep: case TTrue: + return 1; /* no fail */ + case TNot: case TBehind: /* can match empty, but can fail */ + if (pred == PEnofail) return 0; + else return 1; /* PEnullable */ + case TAnd: /* can match empty; fail iff body does */ + if (pred == PEnullable) return 1; + /* else return checkaux(sib1(tree), pred); */ + tree = sib1(tree); goto tailcall; + case TRunTime: /* can fail; match empty iff body does */ + if (pred == PEnofail) return 0; + /* else return checkaux(sib1(tree), pred); */ + tree = sib1(tree); goto tailcall; + case TSeq: + if (!checkaux(sib1(tree), pred)) return 0; + /* else return checkaux(sib2(tree), pred); */ + tree = sib2(tree); goto tailcall; + case TChoice: + if (checkaux(sib2(tree), pred)) return 1; + /* else return checkaux(sib1(tree), pred); */ + tree = sib1(tree); goto tailcall; + case TCapture: case TGrammar: case TRule: + /* return checkaux(sib1(tree), pred); */ + tree = sib1(tree); goto tailcall; + case TCall: /* return checkaux(sib2(tree), pred); */ + tree = sib2(tree); goto tailcall; + default: assert(0); return 0; + } +} + + +/* +** number of characters to match a pattern (or -1 if variable) +*/ +int fixedlen (TTree *tree) { + int len = 0; /* to accumulate in tail calls */ + tailcall: + switch (tree->tag) { + case TChar: case TSet: case TAny: + return len + 1; + case TFalse: case TTrue: case TNot: case TAnd: case TBehind: + return len; + case TRep: case TRunTime: case TOpenCall: + return -1; + case TCapture: case TRule: case TGrammar: + /* return fixedlen(sib1(tree)); */ + tree = sib1(tree); goto tailcall; + case TCall: { + int n1 = callrecursive(tree, fixedlen, -1); + if (n1 < 0) + return -1; + else + return len + n1; + } + case TSeq: { + int n1 = fixedlen(sib1(tree)); + if (n1 < 0) + return -1; + /* else return fixedlen(sib2(tree)) + len; */ + len += n1; tree = sib2(tree); goto tailcall; + } + case TChoice: { + int n1 = fixedlen(sib1(tree)); + int n2 = fixedlen(sib2(tree)); + if (n1 != n2 || n1 < 0) + return -1; + else + return len + n1; + } + default: assert(0); return 0; + }; +} + + +/* +** Computes the 'first set' of a pattern. +** The result is a conservative aproximation: +** match p ax -> x (for some x) ==> a belongs to first(p) +** or +** a not in first(p) ==> match p ax -> fail (for all x) +** +** The set 'follow' is the first set of what follows the +** pattern (full set if nothing follows it). +** +** The function returns 0 when this resulting set can be used for +** test instructions that avoid the pattern altogether. +** A non-zero return can happen for two reasons: +** 1) match p '' -> '' ==> return has bit 1 set +** (tests cannot be used because they would always fail for an empty input); +** 2) there is a match-time capture ==> return has bit 2 set +** (optimizations should not bypass match-time captures). +*/ +static int getfirst (TTree *tree, const Charset *follow, Charset *firstset) { + tailcall: + switch (tree->tag) { + case TChar: case TSet: case TAny: { + tocharset(tree, firstset); + return 0; + } + case TTrue: { + loopset(i, firstset->cs[i] = follow->cs[i]); + return 1; /* accepts the empty string */ + } + case TFalse: { + loopset(i, firstset->cs[i] = 0); + return 0; + } + case TChoice: { + Charset csaux; + int e1 = getfirst(sib1(tree), follow, firstset); + int e2 = getfirst(sib2(tree), follow, &csaux); + loopset(i, firstset->cs[i] |= csaux.cs[i]); + return e1 | e2; + } + case TSeq: { + if (!nullable(sib1(tree))) { + /* when p1 is not nullable, p2 has nothing to contribute; + return getfirst(sib1(tree), fullset, firstset); */ + tree = sib1(tree); follow = fullset; goto tailcall; + } + else { /* FIRST(p1 p2, fl) = FIRST(p1, FIRST(p2, fl)) */ + Charset csaux; + int e2 = getfirst(sib2(tree), follow, &csaux); + int e1 = getfirst(sib1(tree), &csaux, firstset); + if (e1 == 0) return 0; /* 'e1' ensures that first can be used */ + else if ((e1 | e2) & 2) /* one of the children has a matchtime? */ + return 2; /* pattern has a matchtime capture */ + else return e2; /* else depends on 'e2' */ + } + } + case TRep: { + getfirst(sib1(tree), follow, firstset); + loopset(i, firstset->cs[i] |= follow->cs[i]); + return 1; /* accept the empty string */ + } + case TCapture: case TGrammar: case TRule: { + /* return getfirst(sib1(tree), follow, firstset); */ + tree = sib1(tree); goto tailcall; + } + case TRunTime: { /* function invalidates any follow info. */ + int e = getfirst(sib1(tree), fullset, firstset); + if (e) return 2; /* function is not "protected"? */ + else return 0; /* pattern inside capture ensures first can be used */ + } + case TCall: { + /* return getfirst(sib2(tree), follow, firstset); */ + tree = sib2(tree); goto tailcall; + } + case TAnd: { + int e = getfirst(sib1(tree), follow, firstset); + loopset(i, firstset->cs[i] &= follow->cs[i]); + return e; + } + case TNot: { + if (tocharset(sib1(tree), firstset)) { + cs_complement(firstset); + return 1; + } + /* else go through */ + } + case TBehind: { /* instruction gives no new information */ + /* call 'getfirst' only to check for math-time captures */ + int e = getfirst(sib1(tree), follow, firstset); + loopset(i, firstset->cs[i] = follow->cs[i]); /* uses follow */ + return e | 1; /* always can accept the empty string */ + } + default: assert(0); return 0; + } +} + + +/* +** If 'headfail(tree)' true, then 'tree' can fail only depending on the +** next character of the subject. +*/ +static int headfail (TTree *tree) { + tailcall: + switch (tree->tag) { + case TChar: case TSet: case TAny: case TFalse: + return 1; + case TTrue: case TRep: case TRunTime: case TNot: + case TBehind: + return 0; + case TCapture: case TGrammar: case TRule: case TAnd: + tree = sib1(tree); goto tailcall; /* return headfail(sib1(tree)); */ + case TCall: + tree = sib2(tree); goto tailcall; /* return headfail(sib2(tree)); */ + case TSeq: + if (!nofail(sib2(tree))) return 0; + /* else return headfail(sib1(tree)); */ + tree = sib1(tree); goto tailcall; + case TChoice: + if (!headfail(sib1(tree))) return 0; + /* else return headfail(sib2(tree)); */ + tree = sib2(tree); goto tailcall; + default: assert(0); return 0; + } +} + + +/* +** Check whether the code generation for the given tree can benefit +** from a follow set (to avoid computing the follow set when it is +** not needed) +*/ +static int needfollow (TTree *tree) { + tailcall: + switch (tree->tag) { + case TChar: case TSet: case TAny: + case TFalse: case TTrue: case TAnd: case TNot: + case TRunTime: case TGrammar: case TCall: case TBehind: + return 0; + case TChoice: case TRep: + return 1; + case TCapture: + tree = sib1(tree); goto tailcall; + case TSeq: + tree = sib2(tree); goto tailcall; + default: assert(0); return 0; + } +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** Code generation +** ======================================================= +*/ + + +/* +** size of an instruction +*/ +int sizei (const Instruction *i) { + switch((Opcode)i->i.code) { + case ISet: case ISpan: return CHARSETINSTSIZE; + case ITestSet: return CHARSETINSTSIZE + 1; + case ITestChar: case ITestAny: case IChoice: case IJmp: case ICall: + case IOpenCall: case ICommit: case IPartialCommit: case IBackCommit: + return 2; + default: return 1; + } +} + + +/* +** state for the compiler +*/ +typedef struct CompileState { + Pattern *p; /* pattern being compiled */ + int ncode; /* next position in p->code to be filled */ + lua_State *L; +} CompileState; + + +/* +** code generation is recursive; 'opt' indicates that the code is being +** generated as the last thing inside an optional pattern (so, if that +** code is optional too, it can reuse the 'IChoice' already in place for +** the outer pattern). 'tt' points to a previous test protecting this +** code (or NOINST). 'fl' is the follow set of the pattern. +*/ +static void codegen (CompileState *compst, TTree *tree, int opt, int tt, + const Charset *fl); + + +void realloccode (lua_State *L, Pattern *p, int nsize) { + void *ud; + lua_Alloc f = lua_getallocf(L, &ud); + void *newblock = f(ud, p->code, p->codesize * sizeof(Instruction), + nsize * sizeof(Instruction)); + if (newblock == NULL && nsize > 0) + luaL_error(L, "not enough memory"); + p->code = (Instruction *)newblock; + p->codesize = nsize; +} + + +static int nextinstruction (CompileState *compst) { + int size = compst->p->codesize; + if (compst->ncode >= size) + realloccode(compst->L, compst->p, size * 2); + return compst->ncode++; +} + + +#define getinstr(cs,i) ((cs)->p->code[i]) + + +static int addinstruction (CompileState *compst, Opcode op, int aux) { + int i = nextinstruction(compst); + getinstr(compst, i).i.code = op; + getinstr(compst, i).i.aux = aux; + return i; +} + + +/* +** Add an instruction followed by space for an offset (to be set later) +*/ +static int addoffsetinst (CompileState *compst, Opcode op) { + int i = addinstruction(compst, op, 0); /* instruction */ + addinstruction(compst, (Opcode)0, 0); /* open space for offset */ + assert(op == ITestSet || sizei(&getinstr(compst, i)) == 2); + return i; +} + + +/* +** Set the offset of an instruction +*/ +static void setoffset (CompileState *compst, int instruction, int offset) { + getinstr(compst, instruction + 1).offset = offset; +} + + +/* +** Add a capture instruction: +** 'op' is the capture instruction; 'cap' the capture kind; +** 'key' the key into ktable; 'aux' is the optional capture offset +** +*/ +static int addinstcap (CompileState *compst, Opcode op, int cap, int key, + int aux) { + int i = addinstruction(compst, op, joinkindoff(cap, aux)); + getinstr(compst, i).i.key = key; + return i; +} + + +#define gethere(compst) ((compst)->ncode) + +#define target(code,i) ((i) + code[i + 1].offset) + + +/* +** Patch 'instruction' to jump to 'target' +*/ +static void jumptothere (CompileState *compst, int instruction, int target) { + if (instruction >= 0) + setoffset(compst, instruction, target - instruction); +} + + +/* +** Patch 'instruction' to jump to current position +*/ +static void jumptohere (CompileState *compst, int instruction) { + jumptothere(compst, instruction, gethere(compst)); +} + + +/* +** Code an IChar instruction, or IAny if there is an equivalent +** test dominating it +*/ +static void codechar (CompileState *compst, int c, int tt) { + if (tt >= 0 && getinstr(compst, tt).i.code == ITestChar && + getinstr(compst, tt).i.aux == c) + addinstruction(compst, IAny, 0); + else + addinstruction(compst, IChar, c); +} + + +/* +** Add a charset posfix to an instruction +*/ +static void addcharset (CompileState *compst, const byte *cs) { + int p = gethere(compst); + int i; + for (i = 0; i < (int)CHARSETINSTSIZE - 1; i++) + nextinstruction(compst); /* space for buffer */ + /* fill buffer with charset */ + loopset(j, getinstr(compst, p).buff[j] = cs[j]); +} + + +/* +** code a char set, optimizing unit sets for IChar, "complete" +** sets for IAny, and empty sets for IFail; also use an IAny +** when instruction is dominated by an equivalent test. +*/ +static void codecharset (CompileState *compst, const byte *cs, int tt) { + int c = 0; /* (=) to avoid warnings */ + Opcode op = charsettype(cs, &c); + switch (op) { + case IChar: codechar(compst, c, tt); break; + case ISet: { /* non-trivial set? */ + if (tt >= 0 && getinstr(compst, tt).i.code == ITestSet && + cs_equal(cs, getinstr(compst, tt + 2).buff)) + addinstruction(compst, IAny, 0); + else { + addinstruction(compst, ISet, 0); + addcharset(compst, cs); + } + break; + } + default: addinstruction(compst, op, c); break; + } +} + + +/* +** code a test set, optimizing unit sets for ITestChar, "complete" +** sets for ITestAny, and empty sets for IJmp (always fails). +** 'e' is true iff test should accept the empty string. (Test +** instructions in the current VM never accept the empty string.) +*/ +static int codetestset (CompileState *compst, Charset *cs, int e) { + if (e) return NOINST; /* no test */ + else { + int c = 0; + Opcode op = charsettype(cs->cs, &c); + switch (op) { + case IFail: return addoffsetinst(compst, IJmp); /* always jump */ + case IAny: return addoffsetinst(compst, ITestAny); + case IChar: { + int i = addoffsetinst(compst, ITestChar); + getinstr(compst, i).i.aux = c; + return i; + } + case ISet: { + int i = addoffsetinst(compst, ITestSet); + addcharset(compst, cs->cs); + return i; + } + default: assert(0); return 0; + } + } +} + + +/* +** Find the final destination of a sequence of jumps +*/ +static int finaltarget (Instruction *code, int i) { + while (code[i].i.code == IJmp) + i = target(code, i); + return i; +} + + +/* +** final label (after traversing any jumps) +*/ +static int finallabel (Instruction *code, int i) { + return finaltarget(code, target(code, i)); +} + + +/* +** == behind n;

(where n = fixedlen(p)) +*/ +static void codebehind (CompileState *compst, TTree *tree) { + if (tree->u.n > 0) + addinstruction(compst, IBehind, tree->u.n); + codegen(compst, sib1(tree), 0, NOINST, fullset); +} + + +/* +** Choice; optimizations: +** - when p1 is headfail or +** when first(p1) and first(p2) are disjoint, than +** a character not in first(p1) cannot go to p1, and a character +** in first(p1) cannot go to p2 (at it is not in first(p2)). +** (The optimization is not valid if p1 accepts the empty string, +** as then there is no character at all...) +** - when p2 is empty and opt is true; a IPartialCommit can reuse +** the Choice already active in the stack. +*/ +static void codechoice (CompileState *compst, TTree *p1, TTree *p2, int opt, + const Charset *fl) { + int emptyp2 = (p2->tag == TTrue); + Charset cs1, cs2; + int e1 = getfirst(p1, fullset, &cs1); + if (headfail(p1) || + (!e1 && (getfirst(p2, fl, &cs2), cs_disjoint(&cs1, &cs2)))) { + /* == test (fail(p1)) -> L1 ; p1 ; jmp L2; L1: p2; L2: */ + int test = codetestset(compst, &cs1, 0); + int jmp = NOINST; + codegen(compst, p1, 0, test, fl); + if (!emptyp2) + jmp = addoffsetinst(compst, IJmp); + jumptohere(compst, test); + codegen(compst, p2, opt, NOINST, fl); + jumptohere(compst, jmp); + } + else if (opt && emptyp2) { + /* p1? == IPartialCommit; p1 */ + jumptohere(compst, addoffsetinst(compst, IPartialCommit)); + codegen(compst, p1, 1, NOINST, fullset); + } + else { + /* == + test(first(p1)) -> L1; choice L1; ; commit L2; L1: ; L2: */ + int pcommit; + int test = codetestset(compst, &cs1, e1); + int pchoice = addoffsetinst(compst, IChoice); + codegen(compst, p1, emptyp2, test, fullset); + pcommit = addoffsetinst(compst, ICommit); + jumptohere(compst, pchoice); + jumptohere(compst, test); + codegen(compst, p2, opt, NOINST, fl); + jumptohere(compst, pcommit); + } +} + + +/* +** And predicate +** optimization: fixedlen(p) = n ==> <&p> ==

; behind n +** (valid only when 'p' has no captures) +*/ +static void codeand (CompileState *compst, TTree *tree, int tt) { + int n = fixedlen(tree); + if (n >= 0 && n <= MAXBEHIND && !hascaptures(tree)) { + codegen(compst, tree, 0, tt, fullset); + if (n > 0) + addinstruction(compst, IBehind, n); + } + else { /* default: Choice L1; p1; BackCommit L2; L1: Fail; L2: */ + int pcommit; + int pchoice = addoffsetinst(compst, IChoice); + codegen(compst, tree, 0, tt, fullset); + pcommit = addoffsetinst(compst, IBackCommit); + jumptohere(compst, pchoice); + addinstruction(compst, IFail, 0); + jumptohere(compst, pcommit); + } +} + + +/* +** Captures: if pattern has fixed (and not too big) length, and it +** has no nested captures, use a single IFullCapture instruction +** after the match; otherwise, enclose the pattern with OpenCapture - +** CloseCapture. +*/ +static void codecapture (CompileState *compst, TTree *tree, int tt, + const Charset *fl) { + int len = fixedlen(sib1(tree)); + if (len >= 0 && len <= MAXOFF && !hascaptures(sib1(tree))) { + codegen(compst, sib1(tree), 0, tt, fl); + addinstcap(compst, IFullCapture, tree->cap, tree->key, len); + } + else { + addinstcap(compst, IOpenCapture, tree->cap, tree->key, 0); + codegen(compst, sib1(tree), 0, tt, fl); + addinstcap(compst, ICloseCapture, Cclose, 0, 0); + } +} + + +static void coderuntime (CompileState *compst, TTree *tree, int tt) { + addinstcap(compst, IOpenCapture, Cgroup, tree->key, 0); + codegen(compst, sib1(tree), 0, tt, fullset); + addinstcap(compst, ICloseRunTime, Cclose, 0, 0); +} + + +/* +** Repetion; optimizations: +** When pattern is a charset, can use special instruction ISpan. +** When pattern is head fail, or if it starts with characters that +** are disjoint from what follows the repetions, a simple test +** is enough (a fail inside the repetition would backtrack to fail +** again in the following pattern, so there is no need for a choice). +** When 'opt' is true, the repetion can reuse the Choice already +** active in the stack. +*/ +static void coderep (CompileState *compst, TTree *tree, int opt, + const Charset *fl) { + Charset st; + if (tocharset(tree, &st)) { + addinstruction(compst, ISpan, 0); + addcharset(compst, st.cs); + } + else { + int e1 = getfirst(tree, fullset, &st); + if (headfail(tree) || (!e1 && cs_disjoint(&st, fl))) { + /* L1: test (fail(p1)) -> L2;

; jmp L1; L2: */ + int jmp; + int test = codetestset(compst, &st, 0); + codegen(compst, tree, 0, test, fullset); + jmp = addoffsetinst(compst, IJmp); + jumptohere(compst, test); + jumptothere(compst, jmp, test); + } + else { + /* test(fail(p1)) -> L2; choice L2; L1:

; partialcommit L1; L2: */ + /* or (if 'opt'): partialcommit L1; L1:

; partialcommit L1; */ + int commit, l2; + int test = codetestset(compst, &st, e1); + int pchoice = NOINST; + if (opt) + jumptohere(compst, addoffsetinst(compst, IPartialCommit)); + else + pchoice = addoffsetinst(compst, IChoice); + l2 = gethere(compst); + codegen(compst, tree, 0, NOINST, fullset); + commit = addoffsetinst(compst, IPartialCommit); + jumptothere(compst, commit, l2); + jumptohere(compst, pchoice); + jumptohere(compst, test); + } + } +} + + +/* +** Not predicate; optimizations: +** In any case, if first test fails, 'not' succeeds, so it can jump to +** the end. If pattern is headfail, that is all (it cannot fail +** in other parts); this case includes 'not' of simple sets. Otherwise, +** use the default code (a choice plus a failtwice). +*/ +static void codenot (CompileState *compst, TTree *tree) { + Charset st; + int e = getfirst(tree, fullset, &st); + int test = codetestset(compst, &st, e); + if (headfail(tree)) /* test (fail(p1)) -> L1; fail; L1: */ + addinstruction(compst, IFail, 0); + else { + /* test(fail(p))-> L1; choice L1;

; failtwice; L1: */ + int pchoice = addoffsetinst(compst, IChoice); + codegen(compst, tree, 0, NOINST, fullset); + addinstruction(compst, IFailTwice, 0); + jumptohere(compst, pchoice); + } + jumptohere(compst, test); +} + + +/* +** change open calls to calls, using list 'positions' to find +** correct offsets; also optimize tail calls +*/ +static void correctcalls (CompileState *compst, int *positions, + int from, int to) { + int i; + Instruction *code = compst->p->code; + for (i = from; i < to; i += sizei(&code[i])) { + if (code[i].i.code == IOpenCall) { + int n = code[i].i.key; /* rule number */ + int rule = positions[n]; /* rule position */ + assert(rule == from || code[rule - 1].i.code == IRet); + if (code[finaltarget(code, i + 2)].i.code == IRet) /* call; ret ? */ + code[i].i.code = IJmp; /* tail call */ + else + code[i].i.code = ICall; + jumptothere(compst, i, rule); /* call jumps to respective rule */ + } + } + assert(i == to); +} + + +/* +** Code for a grammar: +** call L1; jmp L2; L1: rule 1; ret; rule 2; ret; ...; L2: +*/ +static void codegrammar (CompileState *compst, TTree *grammar) { + int positions[MAXRULES]; + int rulenumber = 0; + TTree *rule; + int firstcall = addoffsetinst(compst, ICall); /* call initial rule */ + int jumptoend = addoffsetinst(compst, IJmp); /* jump to the end */ + int start = gethere(compst); /* here starts the initial rule */ + jumptohere(compst, firstcall); + for (rule = sib1(grammar); rule->tag == TRule; rule = sib2(rule)) { + positions[rulenumber++] = gethere(compst); /* save rule position */ + codegen(compst, sib1(rule), 0, NOINST, fullset); /* code rule */ + addinstruction(compst, IRet, 0); + } + assert(rule->tag == TTrue); + jumptohere(compst, jumptoend); + correctcalls(compst, positions, start, gethere(compst)); +} + + +static void codecall (CompileState *compst, TTree *call) { + int c = addoffsetinst(compst, IOpenCall); /* to be corrected later */ + getinstr(compst, c).i.key = sib2(call)->cap; /* rule number */ + assert(sib2(call)->tag == TRule); +} + + +/* +** Code first child of a sequence +** (second child is called in-place to allow tail call) +** Return 'tt' for second child +*/ +static int codeseq1 (CompileState *compst, TTree *p1, TTree *p2, + int tt, const Charset *fl) { + if (needfollow(p1)) { + Charset fl1; + getfirst(p2, fl, &fl1); /* p1 follow is p2 first */ + codegen(compst, p1, 0, tt, &fl1); + } + else /* use 'fullset' as follow */ + codegen(compst, p1, 0, tt, fullset); + if (fixedlen(p1) != 0) /* can 'p1' consume anything? */ + return NOINST; /* invalidate test */ + else return tt; /* else 'tt' still protects sib2 */ +} + + +/* +** Main code-generation function: dispatch to auxiliar functions +** according to kind of tree. ('needfollow' should return true +** only for consructions that use 'fl'.) +*/ +static void codegen (CompileState *compst, TTree *tree, int opt, int tt, + const Charset *fl) { + tailcall: + switch (tree->tag) { + case TChar: codechar(compst, tree->u.n, tt); break; + case TAny: addinstruction(compst, IAny, 0); break; + case TSet: codecharset(compst, treebuffer(tree), tt); break; + case TTrue: break; + case TFalse: addinstruction(compst, IFail, 0); break; + case TChoice: codechoice(compst, sib1(tree), sib2(tree), opt, fl); break; + case TRep: coderep(compst, sib1(tree), opt, fl); break; + case TBehind: codebehind(compst, tree); break; + case TNot: codenot(compst, sib1(tree)); break; + case TAnd: codeand(compst, sib1(tree), tt); break; + case TCapture: codecapture(compst, tree, tt, fl); break; + case TRunTime: coderuntime(compst, tree, tt); break; + case TGrammar: codegrammar(compst, tree); break; + case TCall: codecall(compst, tree); break; + case TSeq: { + tt = codeseq1(compst, sib1(tree), sib2(tree), tt, fl); /* code 'p1' */ + /* codegen(compst, p2, opt, tt, fl); */ + tree = sib2(tree); goto tailcall; + } + default: assert(0); + } +} + + +/* +** Optimize jumps and other jump-like instructions. +** * Update labels of instructions with labels to their final +** destinations (e.g., choice L1; ... L1: jmp L2: becomes +** choice L2) +** * Jumps to other instructions that do jumps become those +** instructions (e.g., jump to return becomes a return; jump +** to commit becomes a commit) +*/ +static void peephole (CompileState *compst) { + Instruction *code = compst->p->code; + int i; + for (i = 0; i < compst->ncode; i += sizei(&code[i])) { + redo: + switch (code[i].i.code) { + case IChoice: case ICall: case ICommit: case IPartialCommit: + case IBackCommit: case ITestChar: case ITestSet: + case ITestAny: { /* instructions with labels */ + jumptothere(compst, i, finallabel(code, i)); /* optimize label */ + break; + } + case IJmp: { + int ft = finaltarget(code, i); + switch (code[ft].i.code) { /* jumping to what? */ + case IRet: case IFail: case IFailTwice: + case IEnd: { /* instructions with unconditional implicit jumps */ + code[i] = code[ft]; /* jump becomes that instruction */ + code[i + 1].i.code = IAny; /* 'no-op' for target position */ + break; + } + case ICommit: case IPartialCommit: + case IBackCommit: { /* inst. with unconditional explicit jumps */ + int fft = finallabel(code, ft); + code[i] = code[ft]; /* jump becomes that instruction... */ + jumptothere(compst, i, fft); /* but must correct its offset */ + goto redo; /* reoptimize its label */ + } + default: { + jumptothere(compst, i, ft); /* optimize label */ + break; + } + } + break; + } + default: break; + } + } + assert(code[i - 1].i.code == IEnd); +} + + +/* +** Compile a pattern +*/ +Instruction *compile (lua_State *L, Pattern *p) { + CompileState compst; + compst.p = p; compst.ncode = 0; compst.L = L; + realloccode(L, p, 2); /* minimum initial size */ + codegen(&compst, p->tree, 0, NOINST, fullset); + addinstruction(&compst, IEnd, 0); + realloccode(L, p, compst.ncode); /* set final size */ + peephole(&compst); + return p->code; +} + + +/* }====================================================== */ + diff --git a/luaclib/src/lpeg/lpcode.h b/luaclib/src/lpeg/lpcode.h new file mode 100644 index 00000000..34ee2763 --- /dev/null +++ b/luaclib/src/lpeg/lpcode.h @@ -0,0 +1,40 @@ +/* +** $Id: lpcode.h $ +*/ + +#if !defined(lpcode_h) +#define lpcode_h + +#include "lua.h" + +#include "lptypes.h" +#include "lptree.h" +#include "lpvm.h" + +int tocharset (TTree *tree, Charset *cs); +int checkaux (TTree *tree, int pred); +int fixedlen (TTree *tree); +int hascaptures (TTree *tree); +int lp_gc (lua_State *L); +Instruction *compile (lua_State *L, Pattern *p); +void realloccode (lua_State *L, Pattern *p, int nsize); +int sizei (const Instruction *i); + + +#define PEnullable 0 +#define PEnofail 1 + +/* +** nofail(t) implies that 't' cannot fail with any input +*/ +#define nofail(t) checkaux(t, PEnofail) + +/* +** (not nullable(t)) implies 't' cannot match without consuming +** something +*/ +#define nullable(t) checkaux(t, PEnullable) + + + +#endif diff --git a/luaclib/src/lpeg/lpeg-128.gif b/luaclib/src/lpeg/lpeg-128.gif new file mode 100644 index 0000000000000000000000000000000000000000..bbf5e78b88e71044bd341a2476dcec756c252ae4 GIT binary patch literal 4923 zcmV-B6U6LCNk%w1VSoUD0O$Vz004pj0D}Plga8JE00)Es2!#L%h6@FN4hDe`2Z9p_ zgBS~j4iktF6p0fRixn1(7#EEV9grItj}#!2B_EC`A(0;@k|8FOBPWz3D3vEFmoh4o zEH0HaE0iuTmM}1wGBTPiH=Q&zn>I9^I5?R*I-5N^oj*OFK|Y~7LZduGq((%cN=Kwi zNTp0krbkVwPfn#$P^VK-s8Cm}SXil9S*luEt6N*FU0kkTUa(+av0-4cV`8*qWwB*u zvSw$qXK1u&X|-!>xNL2?b8fYCZ?|u9xN>&8et>;?c)EXqet?31e0sZqgMfW|yn=** zetf-yg@S~JgMfa&g@=TJf53)_g@S;>eS^Y?iiduL#EFZDij0Ybg};S{!G?#zjgO3u zkd26m#E+4Vk&}>%jK`9ck&TVXl$Mi{j>MIhl$My4l#s@knULz#>wW(&B@2g=gP|A&Ckos%;V0`%+1Z^ z&(Y4%($CP(=h4&9(a`A9)X~$`($doD)YjA0*VNS1>(Dk-Y+}P^d z-P+yR>)qen-`nlp;N9Qh-r?Qv+~Dxw;@{oj^5Nv*gVX`^Xcp8>g?(2>-FpI>g??I?eFXD?fCBS?e6dS^6&KU^6&HU_3`uY z^7Qfb^Y`=h^7i!j_4oAn_4@ht`}g_w`uF_!`uF?z{rUU&{QCa-{Q39&|NH&=`2PR- z|Ns2|`~Ls@A^sIZa%Ew3Wn>_CX>@2HRA^-&M@dak04x9i004jhfB*mp{s8|897wRB z!Gj1BDqP60p~Hs|BTAe|v7*I`7&B_z$g!ixk03*e97(dI$&)Bks$9vkrAvX8`q*is z=H9?{l}7E#R?lNQWVLv4P~jVh?hctKNEB_v)Q`AA0{He1AFMltl|F;_};VY9{( zl?>#^V#;+GAcx6`MS^o5VWY+-eH4NZB-lh!2s*%^Q%D=Wpb`p8b@@WsJ{k14+ee}#bO3Jby=YfFj5SY4>pSIg$+7yAlZ_3|IG4ZYXE{+9&0^>gij@c?1K+BVN7L+ zBE8Uo%|6)(0!cRW6;xLjK86S4czYfI0f`*plLsohjDgNS_Sl1qC+O^h7nVis14DTp z_UB)a2aE$!UFv{w2Rn||WsjqsJvIKEv42Q>pl%atELe@uN%~I9w1O zV#cKwqF5Br5C<|5?UMqf)N;9JfW`!>inerBLQX{d@Zv-T1n>!#kN^gl*Z>n8VQoY3 z(2(b=$RW((0nJ{E&n0%)GnFrb*aOi!dZ>EwmKPuA-(diH5QjPo?Sn_Ot)`4{2&4u| zi5>X#vc@|Pm2*V{Hk> zUea4{!V6#lG+X(=U@xX82l*yQ_Mq|()&=on3I1y8a!FpoM(dfd;ENeIK_h^&Gs~rB ze+Z%xxoV7eLF3GDu*IvosxqH_3Q1lJ+7QIgKJX+#E#Y5=#eg~BqQnUWDt8{D=Uq;V z^2y3UfJs5~SVO|AMgOh9Et8kT&lkZ@Og7=1v+8-hPU!OwKSbrD&ZN!j>DnnC<>!z* zG)ODE?Sb1ae)*}z&<;WKbYq0zw1-6kG&yU8&KTf0qz(!|(!R3e%v)Z)l0!8SIgKdz zAl)pm2fqVY01xT-NFUTtyn%Ql1cS4g-;P#54oZsx9Dr8riUWWSgl-_DQ%5fFKm!tx zfCDf{!YSZkrq)%Y7fYDXLewFG+4=8p{#C;r5dUL@Q)B}kNUR4m@IZpgsfz)P9XKO zA#YG24~Fam2W9i0hvI+ zxvPeBB zAhiK7fs~m51VyG|reVGVb`)i)4;g1Z07-MK`%smD0KkAzBE*$Xh(}27fd_t)5lAb= zC^LOTii^Mlc7p^!92R1aC+uQV`1oDm{#LVS<>`m4S?pN58{!Sg z3Yf$bC|+;1>5vMx59`A$vQ)O9%0#-0JDTX}~|6we0k1su%wk%kT$H+31jtRN*_Gd{y0P{w};~v8-U2rC7tb7gCRr(~?620NOrIO7IL?U>1y))@n!)idpEHy-C1^JMKXa zE4pJFr&z(+hC72{ZsH9b*@q9@`@D5u+{v`$06C_3G!Cr*i|QQ@19qX0hr`1izpV0j zPP|P@TxeV;(fuIL1lvk9=ffYQ>oWzl|X^V`!%xrtkwFG@+?~+yfjw zHuiELw@pP90wUt@CPM1_JgNae9izK?pQ=oPeC*>MdItauY=R9R(O@8qfB<5`Qh+1~ zgda0Ya?!frJ+@>5I7Q9`)Dr@aLlii6c85oBQ*TBQ)Fm4nUd+AgF;hJ;f+)zCy?_S( z;doLsSY?EmoMwuQ3oA@+AGKJr=LTT)=u`Uue8)#VHhzG6@1ymtIR!uTfraKOMhMfh zIIR*mZ2u(ObWz8~bogYPA~dClKvcHtz?2Q;`qwWr6>2f4&w-sqtZ1;(;GR0eUnM_^=7# zM10|MJB4F+CN+Q*2LOEc58E($#$kU9CPc2m03<*IUocSh;C(z89!;kZ*$@j4;TgrV z55_k@E(m(;CW9{rg*7-p^_PQThbc6G2ea@Ek`x>J@MQyb01$Q%_%I4)0C)Zip%1vA zgi8oCcn5(Qb^sy}g6MEQFn|yD01f>E1m3U@Y?MF((GH~WU0)c6%AsF(B?y;yEuE1M zEJ$^q7kc8uJTw@BWn_Q&w;V~}4+HTGeqaPJkO9P@0|N17pR^bjuu*K01=P_!zeIP4 z0W|IqRo*sPlQ<8R7(_J?g3xd=9$*>BpaL00jPO7Tx1=0jr4ZPFQxl;G`gRbblYyQ0 zc{$i?Bv>{h5`wi<9JZ%}uJLN;z*VV1DL7D!9P}S_h7j@q49!MLJEIW$;0`8pe0DdD zU6(unFp2qK4*e7s10V|ASRT@}Pw@tVv_&!H;d~VoaoZFOO%PHCq5g*RxEk#TSemmK zBa%Q|g)|f(Nv#(i(!>a~6SPm)1`ScBfzkz>&gLs$`YK?#|FHKjxpQPp|th7G4sm!v=nr*M~extB?J z0Njv+>M(I)!2p(k4~?f@hY?$|!U%gp0&jJHwImiv(0d0l4U`FDEa`aG5S1xocbsK< z^?*d82@gdF59y$l0B{5XQ4ilR3vmz!mN0bsa02qj4B~+R&rnMBunm@g2aEs>Q$Y`u z_795F1yLCw3=j@NrUc0#5~WmMmm?%@*bw_b1ph$@cr*`4{zMexzybnLD+o0J9FQ5K zlY=>k572M|3g8Vn7@8Bg7`(R-qre4-C=uPjHw~x)#liyo*`NODpApa{H}DGUG!Alr zSS+vtN~bUYumESEL-?QzMer2^-~#OV59$Cd0n(EYH3t4A64g_d6?J?tM{xb202?3z z6o3E#ggeCs02AN=9l!uDDi#Uw0UW@YV!;Rvi5I)@1UYpP!=MJ2Nf7lQ4$}s6GdYi{ z<%0f4b%#+LsODL$@uDtB0|_!rOH!Y@p$<=gPj)gr`OpnJ*=;1cY7nSWYUdiomRN(u z04c~8T*?-ZUwjM}J67H^OmHO8{QqTiMR`O^^SKnEsRUcs;gzMv3`G&<>E zVI4_6bT~CA1*rc41L`1K@@G?*UU>__s+(~MR1l}8G7$d24&Cs8(JHh4>Q{8t1@GV}i_}V` z&;-B$Q!CL74RIEy_YKAwi5InW3>X$&@C_1y4TdT*EOCEJun7@?5AZ+@Ohjtt%33r- z0(fw&)HAnpiVQD z;$RFcG*N(8HXeru&`=Ne@VT=^s-{vQ$50WT7@A154YR-qX6HyFSpawx2y#yf+&3m>5 zA-ff!3QZ6RZDzhq@dj$(3>ty6M2HT);9~NN7w7;8OP~mLB)>h|zjhJ~9vZ#2V1@>~ zQ1(!*v*`|<=<;JBG7!h4Gs!GHu(kO!Hdc@Q%Vv4j_w z013X}F-EME>aYut@CLcS8Iw>5ia-gT;62^&23w#6N)QQmAu2e0#n9EWO@Ro4kOz0b t87`~}*f6ke{1|$%8+P%%7keDYf;`BCT*!ue$cUWCioD2-9K8Vn06SzG6$t + + + LPeg - Parsing Expression Grammars For Lua + + + + + + + +

+ +
+ +
LPeg
+
+ Parsing Expression Grammars For Lua, version 1.0 +
+
+ +
+ + + +
+ + +

Introduction

+ +

+LPeg is a new pattern-matching library for Lua, +based on + +Parsing Expression Grammars (PEGs). +This text is a reference manual for the library. +For a more formal treatment of LPeg, +as well as some discussion about its implementation, +see + +A Text Pattern-Matching Tool based on Parsing Expression Grammars. +(You may also be interested in my +talk about LPeg +given at the III Lua Workshop.) +

+ +

+Following the Snobol tradition, +LPeg defines patterns as first-class objects. +That is, patterns are regular Lua values +(represented by userdata). +The library offers several functions to create +and compose patterns. +With the use of metamethods, +several of these functions are provided as infix or prefix +operators. +On the one hand, +the result is usually much more verbose than the typical +encoding of patterns using the so called +regular expressions +(which typically are not regular expressions in the formal sense). +On the other hand, +first-class patterns allow much better documentation +(as it is easy to comment the code, +to break complex definitions in smaller parts, etc.) +and are extensible, +as we can define new functions to create and compose patterns. +

+ +

+For a quick glance of the library, +the following table summarizes its basic operations +for creating patterns: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
OperatorDescription
lpeg.P(string)Matches string literally
lpeg.P(n)Matches exactly n characters
lpeg.S(string)Matches any character in string (Set)
lpeg.R("xy")Matches any character between x and y (Range)
patt^nMatches at least n repetitions of patt
patt^-nMatches at most n repetitions of patt
patt1 * patt2Matches patt1 followed by patt2
patt1 + patt2Matches patt1 or patt2 + (ordered choice)
patt1 - patt2Matches patt1 if patt2 does not match
-pattEquivalent to ("" - patt)
#pattMatches patt but consumes no input
lpeg.B(patt)Matches patt behind the current position, + consuming no input
+ +

As a very simple example, +lpeg.R("09")^1 creates a pattern that +matches a non-empty sequence of digits. +As a not so simple example, +-lpeg.P(1) +(which can be written as lpeg.P(-1), +or simply -1 for operations expecting a pattern) +matches an empty string only if it cannot match a single character; +so, it succeeds only at the end of the subject. +

+ +

+LPeg also offers the re module, +which implements patterns following a regular-expression style +(e.g., [09]+). +(This module is 260 lines of Lua code, +and of course it uses LPeg to parse regular expressions and +translate them to regular LPeg patterns.) +

+ + +

Functions

+ + +

lpeg.match (pattern, subject [, init])

+

+The matching function. +It attempts to match the given pattern against the subject string. +If the match succeeds, +returns the index in the subject of the first character after the match, +or the captured values +(if the pattern captured any value). +

+ +

+An optional numeric argument init makes the match +start at that position in the subject string. +As usual in Lua libraries, +a negative value counts from the end. +

+ +

+Unlike typical pattern-matching functions, +match works only in anchored mode; +that is, it tries to match the pattern with a prefix of +the given subject string (at position init), +not with an arbitrary substring of the subject. +So, if we want to find a pattern anywhere in a string, +we must either write a loop in Lua or write a pattern that +matches anywhere. +This second approach is easy and quite efficient; +see examples. +

+ +

lpeg.type (value)

+

+If the given value is a pattern, +returns the string "pattern". +Otherwise returns nil. +

+ +

lpeg.version ()

+

+Returns a string with the running version of LPeg. +

+ +

lpeg.setmaxstack (max)

+

+Sets a limit for the size of the backtrack stack used by LPeg to +track calls and choices. +(The default limit is 400.) +Most well-written patterns need little backtrack levels and +therefore you seldom need to change this limit; +before changing it you should try to rewrite your +pattern to avoid the need for extra space. +Nevertheless, a few useful patterns may overflow. +Also, with recursive grammars, +subjects with deep recursion may also need larger limits. +

+ + +

Basic Constructions

+ +

+The following operations build patterns. +All operations that expect a pattern as an argument +may receive also strings, tables, numbers, booleans, or functions, +which are translated to patterns according to +the rules of function lpeg.P. +

+ + + +

lpeg.P (value)

+

+Converts the given value into a proper pattern, +according to the following rules: +

+
    + +
  • +If the argument is a pattern, +it is returned unmodified. +

  • + +
  • +If the argument is a string, +it is translated to a pattern that matches the string literally. +

  • + +
  • +If the argument is a non-negative number n, +the result is a pattern that matches exactly n characters. +

  • + +
  • +If the argument is a negative number -n, +the result is a pattern that +succeeds only if the input string has less than n characters left: +lpeg.P(-n) +is equivalent to -lpeg.P(n) +(see the unary minus operation). +

  • + +
  • +If the argument is a boolean, +the result is a pattern that always succeeds or always fails +(according to the boolean value), +without consuming any input. +

  • + +
  • +If the argument is a table, +it is interpreted as a grammar +(see Grammars). +

  • + +
  • +If the argument is a function, +returns a pattern equivalent to a +match-time capture over the empty string. +

  • + +
+ + +

lpeg.B(patt)

+

+Returns a pattern that +matches only if the input string at the current position +is preceded by patt. +Pattern patt must match only strings +with some fixed length, +and it cannot contain captures. +

+ +

+Like the and predicate, +this pattern never consumes any input, +independently of success or failure. +

+ + +

lpeg.R ({range})

+

+Returns a pattern that matches any single character +belonging to one of the given ranges. +Each range is a string xy of length 2, +representing all characters with code +between the codes of x and y +(both inclusive). +

+ +

+As an example, the pattern +lpeg.R("09") matches any digit, +and lpeg.R("az", "AZ") matches any ASCII letter. +

+ + +

lpeg.S (string)

+

+Returns a pattern that matches any single character that +appears in the given string. +(The S stands for Set.) +

+ +

+As an example, the pattern +lpeg.S("+-*/") matches any arithmetic operator. +

+ +

+Note that, if s is a character +(that is, a string of length 1), +then lpeg.P(s) is equivalent to lpeg.S(s) +which is equivalent to lpeg.R(s..s). +Note also that both lpeg.S("") and lpeg.R() +are patterns that always fail. +

+ + +

lpeg.V (v)

+

+This operation creates a non-terminal (a variable) +for a grammar. +The created non-terminal refers to the rule indexed by v +in the enclosing grammar. +(See Grammars for details.) +

+ + +

lpeg.locale ([table])

+

+Returns a table with patterns for matching some character classes +according to the current locale. +The table has fields named +alnum, +alpha, +cntrl, +digit, +graph, +lower, +print, +punct, +space, +upper, and +xdigit, +each one containing a correspondent pattern. +Each pattern matches any single character that belongs to its class. +

+ +

+If called with an argument table, +then it creates those fields inside the given table and +returns that table. +

+ + +

#patt

+

+Returns a pattern that +matches only if the input string matches patt, +but without consuming any input, +independently of success or failure. +(This pattern is called an and predicate +and it is equivalent to +&patt in the original PEG notation.) +

+ + +

+This pattern never produces any capture. +

+ + +

-patt

+

+Returns a pattern that +matches only if the input string does not match patt. +It does not consume any input, +independently of success or failure. +(This pattern is equivalent to +!patt in the original PEG notation.) +

+ +

+As an example, the pattern +-lpeg.P(1) matches only the end of string. +

+ +

+This pattern never produces any captures, +because either patt fails +or -patt fails. +(A failing pattern never produces captures.) +

+ + +

patt1 + patt2

+

+Returns a pattern equivalent to an ordered choice +of patt1 and patt2. +(This is denoted by patt1 / patt2 in the original PEG notation, +not to be confused with the / operation in LPeg.) +It matches either patt1 or patt2, +with no backtracking once one of them succeeds. +The identity element for this operation is the pattern +lpeg.P(false), +which always fails. +

+ +

+If both patt1 and patt2 are +character sets, +this operation is equivalent to set union. +

+
+lower = lpeg.R("az")
+upper = lpeg.R("AZ")
+letter = lower + upper
+
+ + +

patt1 - patt2

+

+Returns a pattern equivalent to !patt2 patt1. +This pattern asserts that the input does not match +patt2 and then matches patt1. +

+ +

+When successful, +this pattern produces all captures from patt1. +It never produces any capture from patt2 +(as either patt2 fails or +patt1 - patt2 fails). +

+ +

+If both patt1 and patt2 are +character sets, +this operation is equivalent to set difference. +Note that -patt is equivalent to "" - patt +(or 0 - patt). +If patt is a character set, +1 - patt is its complement. +

+ + +

patt1 * patt2

+

+Returns a pattern that matches patt1 +and then matches patt2, +starting where patt1 finished. +The identity element for this operation is the +pattern lpeg.P(true), +which always succeeds. +

+ +

+(LPeg uses the * operator +[instead of the more obvious ..] +both because it has +the right priority and because in formal languages it is +common to use a dot for denoting concatenation.) +

+ + +

patt^n

+

+If n is nonnegative, +this pattern is +equivalent to pattn patt*: +It matches n or more occurrences of patt. +

+ +

+Otherwise, when n is negative, +this pattern is equivalent to (patt?)-n: +It matches at most |n| +occurrences of patt. +

+ +

+In particular, patt^0 is equivalent to patt*, +patt^1 is equivalent to patt+, +and patt^-1 is equivalent to patt? +in the original PEG notation. +

+ +

+In all cases, +the resulting pattern is greedy with no backtracking +(also called a possessive repetition). +That is, it matches only the longest possible sequence +of matches for patt. +

+ + + +

Grammars

+ +

+With the use of Lua variables, +it is possible to define patterns incrementally, +with each new pattern using previously defined ones. +However, this technique does not allow the definition of +recursive patterns. +For recursive patterns, +we need real grammars. +

+ +

+LPeg represents grammars with tables, +where each entry is a rule. +

+ +

+The call lpeg.V(v) +creates a pattern that represents the nonterminal +(or variable) with index v in a grammar. +Because the grammar still does not exist when +this function is evaluated, +the result is an open reference to the respective rule. +

+ +

+A table is fixed when it is converted to a pattern +(either by calling lpeg.P or by using it wherein a +pattern is expected). +Then every open reference created by lpeg.V(v) +is corrected to refer to the rule indexed by v in the table. +

+ +

+When a table is fixed, +the result is a pattern that matches its initial rule. +The entry with index 1 in the table defines its initial rule. +If that entry is a string, +it is assumed to be the name of the initial rule. +Otherwise, LPeg assumes that the entry 1 itself is the initial rule. +

+ +

+As an example, +the following grammar matches strings of a's and b's that +have the same number of a's and b's: +

+
+equalcount = lpeg.P{
+  "S";   -- initial rule name
+  S = "a" * lpeg.V"B" + "b" * lpeg.V"A" + "",
+  A = "a" * lpeg.V"S" + "b" * lpeg.V"A" * lpeg.V"A",
+  B = "b" * lpeg.V"S" + "a" * lpeg.V"B" * lpeg.V"B",
+} * -1
+
+

+It is equivalent to the following grammar in standard PEG notation: +

+
+  S <- 'a' B / 'b' A / ''
+  A <- 'a' S / 'b' A A
+  B <- 'b' S / 'a' B B
+
+ + +

Captures

+ +

+A capture is a pattern that produces values +(the so called semantic information) +according to what it matches. +LPeg offers several kinds of captures, +which produces values based on matches and combine these values to +produce new values. +Each capture may produce zero or more values. +

+ +

+The following table summarizes the basic captures: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OperationWhat it Produces
lpeg.C(patt)the match for patt plus all captures + made by patt
lpeg.Carg(n)the value of the nth extra argument to + lpeg.match (matches the empty string)
lpeg.Cb(name)the values produced by the previous + group capture named name + (matches the empty string)
lpeg.Cc(values)the given values (matches the empty string)
lpeg.Cf(patt, func)a folding of the captures from patt
lpeg.Cg(patt [, name])the values produced by patt, + optionally tagged with name
lpeg.Cp()the current position (matches the empty string)
lpeg.Cs(patt)the match for patt + with the values from nested captures replacing their matches
lpeg.Ct(patt)a table with all captures from patt
patt / stringstring, with some marks replaced by captures + of patt
patt / numberthe n-th value captured by patt, +or no value when number is zero.
patt / tabletable[c], where c is the (first) + capture of patt
patt / functionthe returns of function applied to the captures + of patt
lpeg.Cmt(patt, function)the returns of function applied to the captures + of patt; the application is done at match time
+ +

+A capture pattern produces its values only when it succeeds. +For instance, +the pattern lpeg.C(lpeg.P"a"^-1) +produces the empty string when there is no "a" +(because the pattern "a"? succeeds), +while the pattern lpeg.C("a")^-1 +does not produce any value when there is no "a" +(because the pattern "a" fails). +A pattern inside a loop or inside a recursive structure +produces values for each match. +

+ +

+Usually, +LPeg does not specify when (and if) it evaluates its captures. +(As an example, +consider the pattern lpeg.P"a" / func / 0. +Because the "division" by 0 instructs LPeg to throw away the +results from the pattern, +LPeg may or may not call func.) +Therefore, captures should avoid side effects. +Moreover, +most captures cannot affect the way a pattern matches a subject. +The only exception to this rule is the +so-called match-time capture. +When a match-time capture matches, +it forces the immediate evaluation of all its nested captures +and then calls its corresponding function, +which defines whether the match succeeds and also +what values are produced. +

+ +

lpeg.C (patt)

+

+Creates a simple capture, +which captures the substring of the subject that matches patt. +The captured value is a string. +If patt has other captures, +their values are returned after this one. +

+ + +

lpeg.Carg (n)

+

+Creates an argument capture. +This pattern matches the empty string and +produces the value given as the nth extra +argument given in the call to lpeg.match. +

+ + +

lpeg.Cb (name)

+

+Creates a back capture. +This pattern matches the empty string and +produces the values produced by the most recent +group capture named name +(where name can be any Lua value). +

+ +

+Most recent means the last +complete +outermost +group capture with the given name. +A Complete capture means that the entire pattern +corresponding to the capture has matched. +An Outermost capture means that the capture is not inside +another complete capture. +

+ +

+In the same way that LPeg does not specify when it evaluates captures, +it does not specify whether it reuses +values previously produced by the group +or re-evaluates them. +

+ +

lpeg.Cc ([value, ...])

+

+Creates a constant capture. +This pattern matches the empty string and +produces all given values as its captured values. +

+ + +

lpeg.Cf (patt, func)

+

+Creates a fold capture. +If patt produces a list of captures +C1 C2 ... Cn, +this capture will produce the value +func(...func(func(C1, C2), C3)..., + Cn), +that is, it will fold +(or accumulate, or reduce) +the captures from patt using function func. +

+ +

+This capture assumes that patt should produce +at least one capture with at least one value (of any type), +which becomes the initial value of an accumulator. +(If you need a specific initial value, +you may prefix a constant capture to patt.) +For each subsequent capture, +LPeg calls func +with this accumulator as the first argument and all values produced +by the capture as extra arguments; +the first result from this call +becomes the new value for the accumulator. +The final value of the accumulator becomes the captured value. +

+ +

+As an example, +the following pattern matches a list of numbers separated +by commas and returns their addition: +

+
+-- matches a numeral and captures its numerical value
+number = lpeg.R"09"^1 / tonumber
+
+-- matches a list of numbers, capturing their values
+list = number * ("," * number)^0
+
+-- auxiliary function to add two numbers
+function add (acc, newvalue) return acc + newvalue end
+
+-- folds the list of numbers adding them
+sum = lpeg.Cf(list, add)
+
+-- example of use
+print(sum:match("10,30,43"))   --> 83
+
+ + +

lpeg.Cg (patt [, name])

+

+Creates a group capture. +It groups all values returned by patt +into a single capture. +The group may be anonymous (if no name is given) +or named with the given name +(which can be any non-nil Lua value). +

+ +

+An anonymous group serves to join values from several captures into +a single capture. +A named group has a different behavior. +In most situations, a named group returns no values at all. +Its values are only relevant for a following +back capture or when used +inside a table capture. +

+ + +

lpeg.Cp ()

+

+Creates a position capture. +It matches the empty string and +captures the position in the subject where the match occurs. +The captured value is a number. +

+ + +

lpeg.Cs (patt)

+

+Creates a substitution capture, +which captures the substring of the subject that matches patt, +with substitutions. +For any capture inside patt with a value, +the substring that matched the capture is replaced by the capture value +(which should be a string). +The final captured value is the string resulting from +all replacements. +

+ + +

lpeg.Ct (patt)

+

+Creates a table capture. +This capture returns a table with all values from all anonymous captures +made by patt inside this table in successive integer keys, +starting at 1. +Moreover, +for each named capture group created by patt, +the first value of the group is put into the table +with the group name as its key. +The captured value is only the table. +

+ + +

patt / string

+

+Creates a string capture. +It creates a capture string based on string. +The captured value is a copy of string, +except that the character % works as an escape character: +any sequence in string of the form %n, +with n between 1 and 9, +stands for the match of the n-th capture in patt. +The sequence %0 stands for the whole match. +The sequence %% stands for a single %. +

+ + +

patt / number

+

+Creates a numbered capture. +For a non-zero number, +the captured value is the n-th value +captured by patt. +When number is zero, +there are no captured values. +

+ + +

patt / table

+

+Creates a query capture. +It indexes the given table using as key the first value captured by +patt, +or the whole match if patt produced no value. +The value at that index is the final value of the capture. +If the table does not have that key, +there is no captured value. +

+ + +

patt / function

+

+Creates a function capture. +It calls the given function passing all captures made by +patt as arguments, +or the whole match if patt made no capture. +The values returned by the function +are the final values of the capture. +In particular, +if function returns no value, +there is no captured value. +

+ + +

lpeg.Cmt(patt, function)

+

+Creates a match-time capture. +Unlike all other captures, +this one is evaluated immediately when a match occurs +(even if it is part of a larger pattern that fails later). +It forces the immediate evaluation of all its nested captures +and then calls function. +

+ +

+The given function gets as arguments the entire subject, +the current position (after the match of patt), +plus any capture values produced by patt. +

+ +

+The first value returned by function +defines how the match happens. +If the call returns a number, +the match succeeds +and the returned number becomes the new current position. +(Assuming a subject s and current position i, +the returned number must be in the range [i, len(s) + 1].) +If the call returns true, +the match succeeds without consuming any input. +(So, to return true is equivalent to return i.) +If the call returns false, nil, or no value, +the match fails. +

+ +

+Any extra values returned by the function become the +values produced by the capture. +

+ + + + +

Some Examples

+ +

Using a Pattern

+

+This example shows a very simple but complete program +that builds and uses a pattern: +

+
+local lpeg = require "lpeg"
+
+-- matches a word followed by end-of-string
+p = lpeg.R"az"^1 * -1
+
+print(p:match("hello"))        --> 6
+print(lpeg.match(p, "hello"))  --> 6
+print(p:match("1 hello"))      --> nil
+
+

+The pattern is simply a sequence of one or more lower-case letters +followed by the end of string (-1). +The program calls match both as a method +and as a function. +In both sucessful cases, +the match returns +the index of the first character after the match, +which is the string length plus one. +

+ + +

Name-value lists

+

+This example parses a list of name-value pairs and returns a table +with those pairs: +

+
+lpeg.locale(lpeg)   -- adds locale entries into 'lpeg' table
+
+local space = lpeg.space^0
+local name = lpeg.C(lpeg.alpha^1) * space
+local sep = lpeg.S(",;") * space
+local pair = lpeg.Cg(name * "=" * space * name) * sep^-1
+local list = lpeg.Cf(lpeg.Ct("") * pair^0, rawset)
+t = list:match("a=b, c = hi; next = pi")  --> { a = "b", c = "hi", next = "pi" }
+
+

+Each pair has the format name = name followed by +an optional separator (a comma or a semicolon). +The pair pattern encloses the pair in a group pattern, +so that the names become the values of a single capture. +The list pattern then folds these captures. +It starts with an empty table, +created by a table capture matching an empty string; +then for each capture (a pair of names) it applies rawset +over the accumulator (the table) and the capture values (the pair of names). +rawset returns the table itself, +so the accumulator is always the table. +

+ +

Splitting a string

+

+The following code builds a pattern that +splits a string using a given pattern +sep as a separator: +

+
+function split (s, sep)
+  sep = lpeg.P(sep)
+  local elem = lpeg.C((1 - sep)^0)
+  local p = elem * (sep * elem)^0
+  return lpeg.match(p, s)
+end
+
+

+First the function ensures that sep is a proper pattern. +The pattern elem is a repetition of zero of more +arbitrary characters as long as there is not a match against +the separator. +It also captures its match. +The pattern p matches a list of elements separated +by sep. +

+ +

+If the split results in too many values, +it may overflow the maximum number of values +that can be returned by a Lua function. +In this case, +we can collect these values in a table: +

+
+function split (s, sep)
+  sep = lpeg.P(sep)
+  local elem = lpeg.C((1 - sep)^0)
+  local p = lpeg.Ct(elem * (sep * elem)^0)   -- make a table capture
+  return lpeg.match(p, s)
+end
+
+ + +

Searching for a pattern

+

+The primitive match works only in anchored mode. +If we want to find a pattern anywhere in a string, +we must write a pattern that matches anywhere. +

+ +

+Because patterns are composable, +we can write a function that, +given any arbitrary pattern p, +returns a new pattern that searches for p +anywhere in a string. +There are several ways to do the search. +One way is like this: +

+
+function anywhere (p)
+  return lpeg.P{ p + 1 * lpeg.V(1) }
+end
+
+

+This grammar has a straight reading: +it matches p or skips one character and tries again. +

+ +

+If we want to know where the pattern is in the string +(instead of knowing only that it is there somewhere), +we can add position captures to the pattern: +

+
+local I = lpeg.Cp()
+function anywhere (p)
+  return lpeg.P{ I * p * I + 1 * lpeg.V(1) }
+end
+
+print(anywhere("world"):match("hello world!"))   -> 7   12
+
+ +

+Another option for the search is like this: +

+
+local I = lpeg.Cp()
+function anywhere (p)
+  return (1 - lpeg.P(p))^0 * I * p * I
+end
+
+

+Again the pattern has a straight reading: +it skips as many characters as possible while not matching p, +and then matches p (plus appropriate captures). +

+ +

+If we want to look for a pattern only at word boundaries, +we can use the following transformer: +

+ +
+local t = lpeg.locale()
+
+function atwordboundary (p)
+  return lpeg.P{
+    [1] = p + t.alpha^0 * (1 - t.alpha)^1 * lpeg.V(1)
+  }
+end
+
+ + +

Balanced parentheses

+

+The following pattern matches only strings with balanced parentheses: +

+
+b = lpeg.P{ "(" * ((1 - lpeg.S"()") + lpeg.V(1))^0 * ")" }
+
+

+Reading the first (and only) rule of the given grammar, +we have that a balanced string is +an open parenthesis, +followed by zero or more repetitions of either +a non-parenthesis character or +a balanced string (lpeg.V(1)), +followed by a closing parenthesis. +

+ + +

Global substitution

+

+The next example does a job somewhat similar to string.gsub. +It receives a pattern and a replacement value, +and substitutes the replacement value for all occurrences of the pattern +in a given string: +

+
+function gsub (s, patt, repl)
+  patt = lpeg.P(patt)
+  patt = lpeg.Cs((patt / repl + 1)^0)
+  return lpeg.match(patt, s)
+end
+
+

+As in string.gsub, +the replacement value can be a string, +a function, or a table. +

+ + +

Comma-Separated Values (CSV)

+

+This example breaks a string into comma-separated values, +returning all fields: +

+
+local field = '"' * lpeg.Cs(((lpeg.P(1) - '"') + lpeg.P'""' / '"')^0) * '"' +
+                    lpeg.C((1 - lpeg.S',\n"')^0)
+
+local record = field * (',' * field)^0 * (lpeg.P'\n' + -1)
+
+function csv (s)
+  return lpeg.match(record, s)
+end
+
+

+A field is either a quoted field +(which may contain any character except an individual quote, +which may be written as two quotes that are replaced by one) +or an unquoted field +(which cannot contain commas, newlines, or quotes). +A record is a list of fields separated by commas, +ending with a newline or the string end (-1). +

+ +

+As it is, +the previous pattern returns each field as a separated result. +If we add a table capture in the definition of record, +the pattern will return instead a single table +containing all fields: +

+
+local record = lpeg.Ct(field * (',' * field)^0) * (lpeg.P'\n' + -1)
+
+ + +

UTF-8 and Latin 1

+

+It is not difficult to use LPeg to convert a string from +UTF-8 encoding to Latin 1 (ISO 8859-1): +

+ +
+-- convert a two-byte UTF-8 sequence to a Latin 1 character
+local function f2 (s)
+  local c1, c2 = string.byte(s, 1, 2)
+  return string.char(c1 * 64 + c2 - 12416)
+end
+
+local utf8 = lpeg.R("\0\127")
+           + lpeg.R("\194\195") * lpeg.R("\128\191") / f2
+
+local decode_pattern = lpeg.Cs(utf8^0) * -1
+
+

+In this code, +the definition of UTF-8 is already restricted to the +Latin 1 range (from 0 to 255). +Any encoding outside this range (as well as any invalid encoding) +will not match that pattern. +

+ +

+As the definition of decode_pattern demands that +the pattern matches the whole input (because of the -1 at its end), +any invalid string will simply fail to match, +without any useful information about the problem. +We can improve this situation redefining decode_pattern +as follows: +

+
+local function er (_, i) error("invalid encoding at position " .. i) end
+
+local decode_pattern = lpeg.Cs(utf8^0) * (-1 + lpeg.P(er))
+
+

+Now, if the pattern utf8^0 stops +before the end of the string, +an appropriate error function is called. +

+ + +

UTF-8 and Unicode

+

+We can extend the previous patterns to handle all Unicode code points. +Of course, +we cannot translate them to Latin 1 or any other one-byte encoding. +Instead, our translation results in a array with the code points +represented as numbers. +The full code is here: +

+
+-- decode a two-byte UTF-8 sequence
+local function f2 (s)
+  local c1, c2 = string.byte(s, 1, 2)
+  return c1 * 64 + c2 - 12416
+end
+
+-- decode a three-byte UTF-8 sequence
+local function f3 (s)
+  local c1, c2, c3 = string.byte(s, 1, 3)
+  return (c1 * 64 + c2) * 64 + c3 - 925824
+end
+
+-- decode a four-byte UTF-8 sequence
+local function f4 (s)
+  local c1, c2, c3, c4 = string.byte(s, 1, 4)
+  return ((c1 * 64 + c2) * 64 + c3) * 64 + c4 - 63447168
+end
+
+local cont = lpeg.R("\128\191")   -- continuation byte
+
+local utf8 = lpeg.R("\0\127") / string.byte
+           + lpeg.R("\194\223") * cont / f2
+           + lpeg.R("\224\239") * cont * cont / f3
+           + lpeg.R("\240\244") * cont * cont * cont / f4
+
+local decode_pattern = lpeg.Ct(utf8^0) * -1
+
+ + +

Lua's long strings

+

+A long string in Lua starts with the pattern [=*[ +and ends at the first occurrence of ]=*] with +exactly the same number of equal signs. +If the opening brackets are followed by a newline, +this newline is discarded +(that is, it is not part of the string). +

+ +

+To match a long string in Lua, +the pattern must capture the first repetition of equal signs and then, +whenever it finds a candidate for closing the string, +check whether it has the same number of equal signs. +

+ +
+equals = lpeg.P"="^0
+open = "[" * lpeg.Cg(equals, "init") * "[" * lpeg.P"\n"^-1
+close = "]" * lpeg.C(equals) * "]"
+closeeq = lpeg.Cmt(close * lpeg.Cb("init"), function (s, i, a, b) return a == b end)
+string = open * lpeg.C((lpeg.P(1) - closeeq)^0) * close / 1
+
+ +

+The open pattern matches [=*[, +capturing the repetitions of equal signs in a group named init; +it also discharges an optional newline, if present. +The close pattern matches ]=*], +also capturing the repetitions of equal signs. +The closeeq pattern first matches close; +then it uses a back capture to recover the capture made +by the previous open, +which is named init; +finally it uses a match-time capture to check +whether both captures are equal. +The string pattern starts with an open, +then it goes as far as possible until matching closeeq, +and then matches the final close. +The final numbered capture simply discards +the capture made by close. +

+ + +

Arithmetic expressions

+

+This example is a complete parser and evaluator for simple +arithmetic expressions. +We write it in two styles. +The first approach first builds a syntax tree and then +traverses this tree to compute the expression value: +

+
+-- Lexical Elements
+local Space = lpeg.S(" \n\t")^0
+local Number = lpeg.C(lpeg.P"-"^-1 * lpeg.R("09")^1) * Space
+local TermOp = lpeg.C(lpeg.S("+-")) * Space
+local FactorOp = lpeg.C(lpeg.S("*/")) * Space
+local Open = "(" * Space
+local Close = ")" * Space
+
+-- Grammar
+local Exp, Term, Factor = lpeg.V"Exp", lpeg.V"Term", lpeg.V"Factor"
+G = lpeg.P{ Exp,
+  Exp = lpeg.Ct(Term * (TermOp * Term)^0);
+  Term = lpeg.Ct(Factor * (FactorOp * Factor)^0);
+  Factor = Number + Open * Exp * Close;
+}
+
+G = Space * G * -1
+
+-- Evaluator
+function eval (x)
+  if type(x) == "string" then
+    return tonumber(x)
+  else
+    local op1 = eval(x[1])
+    for i = 2, #x, 2 do
+      local op = x[i]
+      local op2 = eval(x[i + 1])
+      if (op == "+") then op1 = op1 + op2
+      elseif (op == "-") then op1 = op1 - op2
+      elseif (op == "*") then op1 = op1 * op2
+      elseif (op == "/") then op1 = op1 / op2
+      end
+    end
+    return op1
+  end
+end
+
+-- Parser/Evaluator
+function evalExp (s)
+  local t = lpeg.match(G, s)
+  if not t then error("syntax error", 2) end
+  return eval(t)
+end
+
+-- small example
+print(evalExp"3 + 5*9 / (1+1) - 12")   --> 13.5
+
+ +

+The second style computes the expression value on the fly, +without building the syntax tree. +The following grammar takes this approach. +(It assumes the same lexical elements as before.) +

+
+-- Auxiliary function
+function eval (v1, op, v2)
+  if (op == "+") then return v1 + v2
+  elseif (op == "-") then return v1 - v2
+  elseif (op == "*") then return v1 * v2
+  elseif (op == "/") then return v1 / v2
+  end
+end
+
+-- Grammar
+local V = lpeg.V
+G = lpeg.P{ "Exp",
+  Exp = lpeg.Cf(V"Term" * lpeg.Cg(TermOp * V"Term")^0, eval);
+  Term = lpeg.Cf(V"Factor" * lpeg.Cg(FactorOp * V"Factor")^0, eval);
+  Factor = Number / tonumber + Open * V"Exp" * Close;
+}
+
+-- small example
+print(lpeg.match(G, "3 + 5*9 / (1+1) - 12"))   --> 13.5
+
+

+Note the use of the fold (accumulator) capture. +To compute the value of an expression, +the accumulator starts with the value of the first term, +and then applies eval over +the accumulator, the operator, +and the new term for each repetition. +

+ + + +

Download

+ +

LPeg +source code.

+ + +

License

+ +

+Copyright © 2007-2019 Lua.org, PUC-Rio. +

+

+Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), +to deal in the Software without restriction, +including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, +and to permit persons to whom the Software is +furnished to do so, +subject to the following conditions: +

+ +

+The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. +

+ +

+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +

+ +
+ +
+ +
+ + + diff --git a/luaclib/src/lpeg/lpprint.c b/luaclib/src/lpeg/lpprint.c new file mode 100644 index 00000000..df62cbee --- /dev/null +++ b/luaclib/src/lpeg/lpprint.c @@ -0,0 +1,244 @@ +/* +** $Id: lpprint.c $ +** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license) +*/ + +#include +#include +#include + + +#include "lptypes.h" +#include "lpprint.h" +#include "lpcode.h" + + +#if defined(LPEG_DEBUG) + +/* +** {====================================================== +** Printing patterns (for debugging) +** ======================================================= +*/ + + +void printcharset (const byte *st) { + int i; + printf("["); + for (i = 0; i <= UCHAR_MAX; i++) { + int first = i; + while (testchar(st, i) && i <= UCHAR_MAX) i++; + if (i - 1 == first) /* unary range? */ + printf("(%02x)", first); + else if (i - 1 > first) /* non-empty range? */ + printf("(%02x-%02x)", first, i - 1); + } + printf("]"); +} + + +static const char *capkind (int kind) { + const char *const modes[] = { + "close", "position", "constant", "backref", + "argument", "simple", "table", "function", + "query", "string", "num", "substitution", "fold", + "runtime", "group"}; + return modes[kind]; +} + + +static void printjmp (const Instruction *op, const Instruction *p) { + printf("-> %d", (int)(p + (p + 1)->offset - op)); +} + + +void printinst (const Instruction *op, const Instruction *p) { + const char *const names[] = { + "any", "char", "set", + "testany", "testchar", "testset", + "span", "behind", + "ret", "end", + "choice", "jmp", "call", "open_call", + "commit", "partial_commit", "back_commit", "failtwice", "fail", "giveup", + "fullcapture", "opencapture", "closecapture", "closeruntime" + }; + printf("%02ld: %s ", (long)(p - op), names[p->i.code]); + switch ((Opcode)p->i.code) { + case IChar: { + printf("'%c'", p->i.aux); + break; + } + case ITestChar: { + printf("'%c'", p->i.aux); printjmp(op, p); + break; + } + case IFullCapture: { + printf("%s (size = %d) (idx = %d)", + capkind(getkind(p)), getoff(p), p->i.key); + break; + } + case IOpenCapture: { + printf("%s (idx = %d)", capkind(getkind(p)), p->i.key); + break; + } + case ISet: { + printcharset((p+1)->buff); + break; + } + case ITestSet: { + printcharset((p+2)->buff); printjmp(op, p); + break; + } + case ISpan: { + printcharset((p+1)->buff); + break; + } + case IOpenCall: { + printf("-> %d", (p + 1)->offset); + break; + } + case IBehind: { + printf("%d", p->i.aux); + break; + } + case IJmp: case ICall: case ICommit: case IChoice: + case IPartialCommit: case IBackCommit: case ITestAny: { + printjmp(op, p); + break; + } + default: break; + } + printf("\n"); +} + + +void printpatt (Instruction *p, int n) { + Instruction *op = p; + while (p < op + n) { + printinst(op, p); + p += sizei(p); + } +} + + +#if defined(LPEG_DEBUG) +static void printcap (Capture *cap) { + printf("%s (idx: %d - size: %d) -> %p\n", + capkind(cap->kind), cap->idx, cap->siz, cap->s); +} + + +void printcaplist (Capture *cap, Capture *limit) { + printf(">======\n"); + for (; cap->s && (limit == NULL || cap < limit); cap++) + printcap(cap); + printf("=======\n"); +} +#endif + +/* }====================================================== */ + + +/* +** {====================================================== +** Printing trees (for debugging) +** ======================================================= +*/ + +static const char *tagnames[] = { + "char", "set", "any", + "true", "false", + "rep", + "seq", "choice", + "not", "and", + "call", "opencall", "rule", "grammar", + "behind", + "capture", "run-time" +}; + + +void printtree (TTree *tree, int ident) { + int i; + for (i = 0; i < ident; i++) printf(" "); + printf("%s", tagnames[tree->tag]); + switch (tree->tag) { + case TChar: { + int c = tree->u.n; + if (isprint(c)) + printf(" '%c'\n", c); + else + printf(" (%02X)\n", c); + break; + } + case TSet: { + printcharset(treebuffer(tree)); + printf("\n"); + break; + } + case TOpenCall: case TCall: { + assert(sib2(tree)->tag == TRule); + printf(" key: %d (rule: %d)\n", tree->key, sib2(tree)->cap); + break; + } + case TBehind: { + printf(" %d\n", tree->u.n); + printtree(sib1(tree), ident + 2); + break; + } + case TCapture: { + printf(" kind: '%s' key: %d\n", capkind(tree->cap), tree->key); + printtree(sib1(tree), ident + 2); + break; + } + case TRule: { + printf(" n: %d key: %d\n", tree->cap, tree->key); + printtree(sib1(tree), ident + 2); + break; /* do not print next rule as a sibling */ + } + case TGrammar: { + TTree *rule = sib1(tree); + printf(" %d\n", tree->u.n); /* number of rules */ + for (i = 0; i < tree->u.n; i++) { + printtree(rule, ident + 2); + rule = sib2(rule); + } + assert(rule->tag == TTrue); /* sentinel */ + break; + } + default: { + int sibs = numsiblings[tree->tag]; + printf("\n"); + if (sibs >= 1) { + printtree(sib1(tree), ident + 2); + if (sibs >= 2) + printtree(sib2(tree), ident + 2); + } + break; + } + } +} + + +void printktable (lua_State *L, int idx) { + int n, i; + lua_getuservalue(L, idx); + if (lua_isnil(L, -1)) /* no ktable? */ + return; + n = lua_rawlen(L, -1); + printf("["); + for (i = 1; i <= n; i++) { + printf("%d = ", i); + lua_rawgeti(L, -1, i); + if (lua_isstring(L, -1)) + printf("%s ", lua_tostring(L, -1)); + else + printf("%s ", lua_typename(L, lua_type(L, -1))); + lua_pop(L, 1); + } + printf("]\n"); + /* leave ktable at the stack */ +} + +/* }====================================================== */ + +#endif diff --git a/luaclib/src/lpeg/lpprint.h b/luaclib/src/lpeg/lpprint.h new file mode 100644 index 00000000..15ef121d --- /dev/null +++ b/luaclib/src/lpeg/lpprint.h @@ -0,0 +1,36 @@ +/* +** $Id: lpprint.h $ +*/ + + +#if !defined(lpprint_h) +#define lpprint_h + + +#include "lptree.h" +#include "lpvm.h" + + +#if defined(LPEG_DEBUG) + +void printpatt (Instruction *p, int n); +void printtree (TTree *tree, int ident); +void printktable (lua_State *L, int idx); +void printcharset (const byte *st); +void printcaplist (Capture *cap, Capture *limit); +void printinst (const Instruction *op, const Instruction *p); + +#else + +#define printktable(L,idx) \ + luaL_error(L, "function only implemented in debug mode") +#define printtree(tree,i) \ + luaL_error(L, "function only implemented in debug mode") +#define printpatt(p,n) \ + luaL_error(L, "function only implemented in debug mode") + +#endif + + +#endif + diff --git a/luaclib/src/lpeg/lptree.c b/luaclib/src/lpeg/lptree.c new file mode 100644 index 00000000..5c8de947 --- /dev/null +++ b/luaclib/src/lpeg/lptree.c @@ -0,0 +1,1305 @@ +/* +** $Id: lptree.c $ +** Copyright 2013, Lua.org & PUC-Rio (see 'lpeg.html' for license) +*/ + +#include +#include +#include + + +#include "lua.h" +#include "lauxlib.h" + +#include "lptypes.h" +#include "lpcap.h" +#include "lpcode.h" +#include "lpprint.h" +#include "lptree.h" + + +/* number of siblings for each tree */ +const byte numsiblings[] = { + 0, 0, 0, /* char, set, any */ + 0, 0, /* true, false */ + 1, /* rep */ + 2, 2, /* seq, choice */ + 1, 1, /* not, and */ + 0, 0, 2, 1, /* call, opencall, rule, grammar */ + 1, /* behind */ + 1, 1 /* capture, runtime capture */ +}; + + +static TTree *newgrammar (lua_State *L, int arg); + + +/* +** returns a reasonable name for value at index 'idx' on the stack +*/ +static const char *val2str (lua_State *L, int idx) { + const char *k = lua_tostring(L, idx); + if (k != NULL) + return lua_pushfstring(L, "%s", k); + else + return lua_pushfstring(L, "(a %s)", luaL_typename(L, idx)); +} + + +/* +** Fix a TOpenCall into a TCall node, using table 'postable' to +** translate a key to its rule address in the tree. Raises an +** error if key does not exist. +*/ +static void fixonecall (lua_State *L, int postable, TTree *g, TTree *t) { + int n; + lua_rawgeti(L, -1, t->key); /* get rule's name */ + lua_gettable(L, postable); /* query name in position table */ + n = lua_tonumber(L, -1); /* get (absolute) position */ + lua_pop(L, 1); /* remove position */ + if (n == 0) { /* no position? */ + lua_rawgeti(L, -1, t->key); /* get rule's name again */ + luaL_error(L, "rule '%s' undefined in given grammar", val2str(L, -1)); + } + t->tag = TCall; + t->u.ps = n - (t - g); /* position relative to node */ + assert(sib2(t)->tag == TRule); + sib2(t)->key = t->key; /* fix rule's key */ +} + + +/* +** Transform left associative constructions into right +** associative ones, for sequence and choice; that is: +** (t11 + t12) + t2 => t11 + (t12 + t2) +** (t11 * t12) * t2 => t11 * (t12 * t2) +** (that is, Op (Op t11 t12) t2 => Op t11 (Op t12 t2)) +*/ +static void correctassociativity (TTree *tree) { + TTree *t1 = sib1(tree); + assert(tree->tag == TChoice || tree->tag == TSeq); + while (t1->tag == tree->tag) { + int n1size = tree->u.ps - 1; /* t1 == Op t11 t12 */ + int n11size = t1->u.ps - 1; + int n12size = n1size - n11size - 1; + memmove(sib1(tree), sib1(t1), n11size * sizeof(TTree)); /* move t11 */ + tree->u.ps = n11size + 1; + sib2(tree)->tag = tree->tag; + sib2(tree)->u.ps = n12size + 1; + } +} + + +/* +** Make final adjustments in a tree. Fix open calls in tree 't', +** making them refer to their respective rules or raising appropriate +** errors (if not inside a grammar). Correct associativity of associative +** constructions (making them right associative). Assume that tree's +** ktable is at the top of the stack (for error messages). +*/ +static void finalfix (lua_State *L, int postable, TTree *g, TTree *t) { + tailcall: + switch (t->tag) { + case TGrammar: /* subgrammars were already fixed */ + return; + case TOpenCall: { + if (g != NULL) /* inside a grammar? */ + fixonecall(L, postable, g, t); + else { /* open call outside grammar */ + lua_rawgeti(L, -1, t->key); + luaL_error(L, "rule '%s' used outside a grammar", val2str(L, -1)); + } + break; + } + case TSeq: case TChoice: + correctassociativity(t); + break; + } + switch (numsiblings[t->tag]) { + case 1: /* finalfix(L, postable, g, sib1(t)); */ + t = sib1(t); goto tailcall; + case 2: + finalfix(L, postable, g, sib1(t)); + t = sib2(t); goto tailcall; /* finalfix(L, postable, g, sib2(t)); */ + default: assert(numsiblings[t->tag] == 0); break; + } +} + + + +/* +** {=================================================================== +** KTable manipulation +** +** - The ktable of a pattern 'p' can be shared by other patterns that +** contain 'p' and no other constants. Because of this sharing, we +** should not add elements to a 'ktable' unless it was freshly created +** for the new pattern. +** +** - The maximum index in a ktable is USHRT_MAX, because trees and +** patterns use unsigned shorts to store those indices. +** ==================================================================== +*/ + +/* +** Create a new 'ktable' to the pattern at the top of the stack. +*/ +static void newktable (lua_State *L, int n) { + lua_createtable(L, n, 0); /* create a fresh table */ + lua_setuservalue(L, -2); /* set it as 'ktable' for pattern */ +} + + +/* +** Add element 'idx' to 'ktable' of pattern at the top of the stack; +** Return index of new element. +** If new element is nil, does not add it to table (as it would be +** useless) and returns 0, as ktable[0] is always nil. +*/ +static int addtoktable (lua_State *L, int idx) { + if (lua_isnil(L, idx)) /* nil value? */ + return 0; + else { + int n; + lua_getuservalue(L, -1); /* get ktable from pattern */ + n = lua_rawlen(L, -1); + if (n >= USHRT_MAX) + luaL_error(L, "too many Lua values in pattern"); + lua_pushvalue(L, idx); /* element to be added */ + lua_rawseti(L, -2, ++n); + lua_pop(L, 1); /* remove 'ktable' */ + return n; + } +} + + +/* +** Return the number of elements in the ktable at 'idx'. +** In Lua 5.2/5.3, default "environment" for patterns is nil, not +** a table. Treat it as an empty table. In Lua 5.1, assumes that +** the environment has no numeric indices (len == 0) +*/ +static int ktablelen (lua_State *L, int idx) { + if (!lua_istable(L, idx)) return 0; + else return lua_rawlen(L, idx); +} + + +/* +** Concatentate the contents of table 'idx1' into table 'idx2'. +** (Assume that both indices are negative.) +** Return the original length of table 'idx2' (or 0, if no +** element was added, as there is no need to correct any index). +*/ +static int concattable (lua_State *L, int idx1, int idx2) { + int i; + int n1 = ktablelen(L, idx1); + int n2 = ktablelen(L, idx2); + if (n1 + n2 > USHRT_MAX) + luaL_error(L, "too many Lua values in pattern"); + if (n1 == 0) return 0; /* nothing to correct */ + for (i = 1; i <= n1; i++) { + lua_rawgeti(L, idx1, i); + lua_rawseti(L, idx2 - 1, n2 + i); /* correct 'idx2' */ + } + return n2; +} + + +/* +** When joining 'ktables', constants from one of the subpatterns must +** be renumbered; 'correctkeys' corrects their indices (adding 'n' +** to each of them) +*/ +static void correctkeys (TTree *tree, int n) { + if (n == 0) return; /* no correction? */ + tailcall: + switch (tree->tag) { + case TOpenCall: case TCall: case TRunTime: case TRule: { + if (tree->key > 0) + tree->key += n; + break; + } + case TCapture: { + if (tree->key > 0 && tree->cap != Carg && tree->cap != Cnum) + tree->key += n; + break; + } + default: break; + } + switch (numsiblings[tree->tag]) { + case 1: /* correctkeys(sib1(tree), n); */ + tree = sib1(tree); goto tailcall; + case 2: + correctkeys(sib1(tree), n); + tree = sib2(tree); goto tailcall; /* correctkeys(sib2(tree), n); */ + default: assert(numsiblings[tree->tag] == 0); break; + } +} + + +/* +** Join the ktables from p1 and p2 the ktable for the new pattern at the +** top of the stack, reusing them when possible. +*/ +static void joinktables (lua_State *L, int p1, TTree *t2, int p2) { + int n1, n2; + lua_getuservalue(L, p1); /* get ktables */ + lua_getuservalue(L, p2); + n1 = ktablelen(L, -2); + n2 = ktablelen(L, -1); + if (n1 == 0 && n2 == 0) /* are both tables empty? */ + lua_pop(L, 2); /* nothing to be done; pop tables */ + else if (n2 == 0 || lp_equal(L, -2, -1)) { /* 2nd table empty or equal? */ + lua_pop(L, 1); /* pop 2nd table */ + lua_setuservalue(L, -2); /* set 1st ktable into new pattern */ + } + else if (n1 == 0) { /* first table is empty? */ + lua_setuservalue(L, -3); /* set 2nd table into new pattern */ + lua_pop(L, 1); /* pop 1st table */ + } + else { + lua_createtable(L, n1 + n2, 0); /* create ktable for new pattern */ + /* stack: new p; ktable p1; ktable p2; new ktable */ + concattable(L, -3, -1); /* from p1 into new ktable */ + concattable(L, -2, -1); /* from p2 into new ktable */ + lua_setuservalue(L, -4); /* new ktable becomes 'p' environment */ + lua_pop(L, 2); /* pop other ktables */ + correctkeys(t2, n1); /* correction for indices from p2 */ + } +} + + +/* +** copy 'ktable' of element 'idx' to new tree (on top of stack) +*/ +static void copyktable (lua_State *L, int idx) { + lua_getuservalue(L, idx); + lua_setuservalue(L, -2); +} + + +/* +** merge 'ktable' from 'stree' at stack index 'idx' into 'ktable' +** from tree at the top of the stack, and correct corresponding +** tree. +*/ +static void mergektable (lua_State *L, int idx, TTree *stree) { + int n; + lua_getuservalue(L, -1); /* get ktables */ + lua_getuservalue(L, idx); + n = concattable(L, -1, -2); + lua_pop(L, 2); /* remove both ktables */ + correctkeys(stree, n); +} + + +/* +** Create a new 'ktable' to the pattern at the top of the stack, adding +** all elements from pattern 'p' (if not 0) plus element 'idx' to it. +** Return index of new element. +*/ +static int addtonewktable (lua_State *L, int p, int idx) { + newktable(L, 1); + if (p) + mergektable(L, p, NULL); + return addtoktable(L, idx); +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Tree generation +** ======================================================= +*/ + +/* +** In 5.2, could use 'luaL_testudata'... +*/ +static int testpattern (lua_State *L, int idx) { + if (lua_touserdata(L, idx)) { /* value is a userdata? */ + if (lua_getmetatable(L, idx)) { /* does it have a metatable? */ + luaL_getmetatable(L, PATTERN_T); + if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */ + lua_pop(L, 2); /* remove both metatables */ + return 1; + } + } + } + return 0; +} + + +static Pattern *getpattern (lua_State *L, int idx) { + return (Pattern *)luaL_checkudata(L, idx, PATTERN_T); +} + + +static int getsize (lua_State *L, int idx) { + return (lua_rawlen(L, idx) - sizeof(Pattern)) / sizeof(TTree) + 1; +} + + +static TTree *gettree (lua_State *L, int idx, int *len) { + Pattern *p = getpattern(L, idx); + if (len) + *len = getsize(L, idx); + return p->tree; +} + + +/* +** create a pattern. Set its uservalue (the 'ktable') equal to its +** metatable. (It could be any empty sequence; the metatable is at +** hand here, so we use it.) +*/ +static TTree *newtree (lua_State *L, int len) { + size_t size = (len - 1) * sizeof(TTree) + sizeof(Pattern); + Pattern *p = (Pattern *)lua_newuserdata(L, size); + luaL_getmetatable(L, PATTERN_T); + lua_pushvalue(L, -1); + lua_setuservalue(L, -3); + lua_setmetatable(L, -2); + p->code = NULL; p->codesize = 0; + return p->tree; +} + + +static TTree *newleaf (lua_State *L, int tag) { + TTree *tree = newtree(L, 1); + tree->tag = tag; + return tree; +} + + +static TTree *newcharset (lua_State *L) { + TTree *tree = newtree(L, bytes2slots(CHARSETSIZE) + 1); + tree->tag = TSet; + loopset(i, treebuffer(tree)[i] = 0); + return tree; +} + + +/* +** add to tree a sequence where first sibling is 'sib' (with size +** 'sibsize'); returns position for second sibling +*/ +static TTree *seqaux (TTree *tree, TTree *sib, int sibsize) { + tree->tag = TSeq; tree->u.ps = sibsize + 1; + memcpy(sib1(tree), sib, sibsize * sizeof(TTree)); + return sib2(tree); +} + + +/* +** Build a sequence of 'n' nodes, each with tag 'tag' and 'u.n' got +** from the array 's' (or 0 if array is NULL). (TSeq is binary, so it +** must build a sequence of sequence of sequence...) +*/ +static void fillseq (TTree *tree, int tag, int n, const char *s) { + int i; + for (i = 0; i < n - 1; i++) { /* initial n-1 copies of Seq tag; Seq ... */ + tree->tag = TSeq; tree->u.ps = 2; + sib1(tree)->tag = tag; + sib1(tree)->u.n = s ? (byte)s[i] : 0; + tree = sib2(tree); + } + tree->tag = tag; /* last one does not need TSeq */ + tree->u.n = s ? (byte)s[i] : 0; +} + + +/* +** Numbers as patterns: +** 0 == true (always match); n == TAny repeated 'n' times; +** -n == not (TAny repeated 'n' times) +*/ +static TTree *numtree (lua_State *L, int n) { + if (n == 0) + return newleaf(L, TTrue); + else { + TTree *tree, *nd; + if (n > 0) + tree = nd = newtree(L, 2 * n - 1); + else { /* negative: code it as !(-n) */ + n = -n; + tree = newtree(L, 2 * n); + tree->tag = TNot; + nd = sib1(tree); + } + fillseq(nd, TAny, n, NULL); /* sequence of 'n' any's */ + return tree; + } +} + + +/* +** Convert value at index 'idx' to a pattern +*/ +static TTree *getpatt (lua_State *L, int idx, int *len) { + TTree *tree; + switch (lua_type(L, idx)) { + case LUA_TSTRING: { + size_t slen; + const char *s = lua_tolstring(L, idx, &slen); /* get string */ + if (slen == 0) /* empty? */ + tree = newleaf(L, TTrue); /* always match */ + else { + tree = newtree(L, 2 * (slen - 1) + 1); + fillseq(tree, TChar, slen, s); /* sequence of 'slen' chars */ + } + break; + } + case LUA_TNUMBER: { + int n = lua_tointeger(L, idx); + tree = numtree(L, n); + break; + } + case LUA_TBOOLEAN: { + tree = (lua_toboolean(L, idx) ? newleaf(L, TTrue) : newleaf(L, TFalse)); + break; + } + case LUA_TTABLE: { + tree = newgrammar(L, idx); + break; + } + case LUA_TFUNCTION: { + tree = newtree(L, 2); + tree->tag = TRunTime; + tree->key = addtonewktable(L, 0, idx); + sib1(tree)->tag = TTrue; + break; + } + default: { + return gettree(L, idx, len); + } + } + lua_replace(L, idx); /* put new tree into 'idx' slot */ + if (len) + *len = getsize(L, idx); + return tree; +} + + +/* +** create a new tree, whith a new root and one sibling. +** Sibling must be on the Lua stack, at index 1. +*/ +static TTree *newroot1sib (lua_State *L, int tag) { + int s1; + TTree *tree1 = getpatt(L, 1, &s1); + TTree *tree = newtree(L, 1 + s1); /* create new tree */ + tree->tag = tag; + memcpy(sib1(tree), tree1, s1 * sizeof(TTree)); + copyktable(L, 1); + return tree; +} + + +/* +** create a new tree, whith a new root and 2 siblings. +** Siblings must be on the Lua stack, first one at index 1. +*/ +static TTree *newroot2sib (lua_State *L, int tag) { + int s1, s2; + TTree *tree1 = getpatt(L, 1, &s1); + TTree *tree2 = getpatt(L, 2, &s2); + TTree *tree = newtree(L, 1 + s1 + s2); /* create new tree */ + tree->tag = tag; + tree->u.ps = 1 + s1; + memcpy(sib1(tree), tree1, s1 * sizeof(TTree)); + memcpy(sib2(tree), tree2, s2 * sizeof(TTree)); + joinktables(L, 1, sib2(tree), 2); + return tree; +} + + +static int lp_P (lua_State *L) { + luaL_checkany(L, 1); + getpatt(L, 1, NULL); + lua_settop(L, 1); + return 1; +} + + +/* +** sequence operator; optimizations: +** false x => false, x true => x, true x => x +** (cannot do x . false => false because x may have runtime captures) +*/ +static int lp_seq (lua_State *L) { + TTree *tree1 = getpatt(L, 1, NULL); + TTree *tree2 = getpatt(L, 2, NULL); + if (tree1->tag == TFalse || tree2->tag == TTrue) + lua_pushvalue(L, 1); /* false . x == false, x . true = x */ + else if (tree1->tag == TTrue) + lua_pushvalue(L, 2); /* true . x = x */ + else + newroot2sib(L, TSeq); + return 1; +} + + +/* +** choice operator; optimizations: +** charset / charset => charset +** true / x => true, x / false => x, false / x => x +** (x / true is not equivalent to true) +*/ +static int lp_choice (lua_State *L) { + Charset st1, st2; + TTree *t1 = getpatt(L, 1, NULL); + TTree *t2 = getpatt(L, 2, NULL); + if (tocharset(t1, &st1) && tocharset(t2, &st2)) { + TTree *t = newcharset(L); + loopset(i, treebuffer(t)[i] = st1.cs[i] | st2.cs[i]); + } + else if (nofail(t1) || t2->tag == TFalse) + lua_pushvalue(L, 1); /* true / x => true, x / false => x */ + else if (t1->tag == TFalse) + lua_pushvalue(L, 2); /* false / x => x */ + else + newroot2sib(L, TChoice); + return 1; +} + + +/* +** p^n +*/ +static int lp_star (lua_State *L) { + int size1; + int n = (int)luaL_checkinteger(L, 2); + TTree *tree1 = getpatt(L, 1, &size1); + if (n >= 0) { /* seq tree1 (seq tree1 ... (seq tree1 (rep tree1))) */ + TTree *tree = newtree(L, (n + 1) * (size1 + 1)); + if (nullable(tree1)) + luaL_error(L, "loop body may accept empty string"); + while (n--) /* repeat 'n' times */ + tree = seqaux(tree, tree1, size1); + tree->tag = TRep; + memcpy(sib1(tree), tree1, size1 * sizeof(TTree)); + } + else { /* choice (seq tree1 ... choice tree1 true ...) true */ + TTree *tree; + n = -n; + /* size = (choice + seq + tree1 + true) * n, but the last has no seq */ + tree = newtree(L, n * (size1 + 3) - 1); + for (; n > 1; n--) { /* repeat (n - 1) times */ + tree->tag = TChoice; tree->u.ps = n * (size1 + 3) - 2; + sib2(tree)->tag = TTrue; + tree = sib1(tree); + tree = seqaux(tree, tree1, size1); + } + tree->tag = TChoice; tree->u.ps = size1 + 1; + sib2(tree)->tag = TTrue; + memcpy(sib1(tree), tree1, size1 * sizeof(TTree)); + } + copyktable(L, 1); + return 1; +} + + +/* +** #p == &p +*/ +static int lp_and (lua_State *L) { + newroot1sib(L, TAnd); + return 1; +} + + +/* +** -p == !p +*/ +static int lp_not (lua_State *L) { + newroot1sib(L, TNot); + return 1; +} + + +/* +** [t1 - t2] == Seq (Not t2) t1 +** If t1 and t2 are charsets, make their difference. +*/ +static int lp_sub (lua_State *L) { + Charset st1, st2; + int s1, s2; + TTree *t1 = getpatt(L, 1, &s1); + TTree *t2 = getpatt(L, 2, &s2); + if (tocharset(t1, &st1) && tocharset(t2, &st2)) { + TTree *t = newcharset(L); + loopset(i, treebuffer(t)[i] = st1.cs[i] & ~st2.cs[i]); + } + else { + TTree *tree = newtree(L, 2 + s1 + s2); + tree->tag = TSeq; /* sequence of... */ + tree->u.ps = 2 + s2; + sib1(tree)->tag = TNot; /* ...not... */ + memcpy(sib1(sib1(tree)), t2, s2 * sizeof(TTree)); /* ...t2 */ + memcpy(sib2(tree), t1, s1 * sizeof(TTree)); /* ... and t1 */ + joinktables(L, 1, sib1(tree), 2); + } + return 1; +} + + +static int lp_set (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + TTree *tree = newcharset(L); + while (l--) { + setchar(treebuffer(tree), (byte)(*s)); + s++; + } + return 1; +} + + +static int lp_range (lua_State *L) { + int arg; + int top = lua_gettop(L); + TTree *tree = newcharset(L); + for (arg = 1; arg <= top; arg++) { + int c; + size_t l; + const char *r = luaL_checklstring(L, arg, &l); + luaL_argcheck(L, l == 2, arg, "range must have two characters"); + for (c = (byte)r[0]; c <= (byte)r[1]; c++) + setchar(treebuffer(tree), c); + } + return 1; +} + + +/* +** Look-behind predicate +*/ +static int lp_behind (lua_State *L) { + TTree *tree; + TTree *tree1 = getpatt(L, 1, NULL); + int n = fixedlen(tree1); + luaL_argcheck(L, n >= 0, 1, "pattern may not have fixed length"); + luaL_argcheck(L, !hascaptures(tree1), 1, "pattern have captures"); + luaL_argcheck(L, n <= MAXBEHIND, 1, "pattern too long to look behind"); + tree = newroot1sib(L, TBehind); + tree->u.n = n; + return 1; +} + + +/* +** Create a non-terminal +*/ +static int lp_V (lua_State *L) { + TTree *tree = newleaf(L, TOpenCall); + luaL_argcheck(L, !lua_isnoneornil(L, 1), 1, "non-nil value expected"); + tree->key = addtonewktable(L, 0, 1); + return 1; +} + + +/* +** Create a tree for a non-empty capture, with a body and +** optionally with an associated Lua value (at index 'labelidx' in the +** stack) +*/ +static int capture_aux (lua_State *L, int cap, int labelidx) { + TTree *tree = newroot1sib(L, TCapture); + tree->cap = cap; + tree->key = (labelidx == 0) ? 0 : addtonewktable(L, 1, labelidx); + return 1; +} + + +/* +** Fill a tree with an empty capture, using an empty (TTrue) sibling. +** (The 'key' field must be filled by the caller to finish the tree.) +*/ +static TTree *auxemptycap (TTree *tree, int cap) { + tree->tag = TCapture; + tree->cap = cap; + sib1(tree)->tag = TTrue; + return tree; +} + + +/* +** Create a tree for an empty capture. +*/ +static TTree *newemptycap (lua_State *L, int cap, int key) { + TTree *tree = auxemptycap(newtree(L, 2), cap); + tree->key = key; + return tree; +} + + +/* +** Create a tree for an empty capture with an associated Lua value. +*/ +static TTree *newemptycapkey (lua_State *L, int cap, int idx) { + TTree *tree = auxemptycap(newtree(L, 2), cap); + tree->key = addtonewktable(L, 0, idx); + return tree; +} + + +/* +** Captures with syntax p / v +** (function capture, query capture, string capture, or number capture) +*/ +static int lp_divcapture (lua_State *L) { + switch (lua_type(L, 2)) { + case LUA_TFUNCTION: return capture_aux(L, Cfunction, 2); + case LUA_TTABLE: return capture_aux(L, Cquery, 2); + case LUA_TSTRING: return capture_aux(L, Cstring, 2); + case LUA_TNUMBER: { + int n = lua_tointeger(L, 2); + TTree *tree = newroot1sib(L, TCapture); + luaL_argcheck(L, 0 <= n && n <= SHRT_MAX, 1, "invalid number"); + tree->cap = Cnum; + tree->key = n; + return 1; + } + default: return luaL_argerror(L, 2, "invalid replacement value"); + } +} + + +static int lp_substcapture (lua_State *L) { + return capture_aux(L, Csubst, 0); +} + + +static int lp_tablecapture (lua_State *L) { + return capture_aux(L, Ctable, 0); +} + + +static int lp_groupcapture (lua_State *L) { + if (lua_isnoneornil(L, 2)) + return capture_aux(L, Cgroup, 0); + else + return capture_aux(L, Cgroup, 2); +} + + +static int lp_foldcapture (lua_State *L) { + luaL_checktype(L, 2, LUA_TFUNCTION); + return capture_aux(L, Cfold, 2); +} + + +static int lp_simplecapture (lua_State *L) { + return capture_aux(L, Csimple, 0); +} + + +static int lp_poscapture (lua_State *L) { + newemptycap(L, Cposition, 0); + return 1; +} + + +static int lp_argcapture (lua_State *L) { + int n = (int)luaL_checkinteger(L, 1); + luaL_argcheck(L, 0 < n && n <= SHRT_MAX, 1, "invalid argument index"); + newemptycap(L, Carg, n); + return 1; +} + + +static int lp_backref (lua_State *L) { + luaL_checkany(L, 1); + newemptycapkey(L, Cbackref, 1); + return 1; +} + + +/* +** Constant capture +*/ +static int lp_constcapture (lua_State *L) { + int i; + int n = lua_gettop(L); /* number of values */ + if (n == 0) /* no values? */ + newleaf(L, TTrue); /* no capture */ + else if (n == 1) + newemptycapkey(L, Cconst, 1); /* single constant capture */ + else { /* create a group capture with all values */ + TTree *tree = newtree(L, 1 + 3 * (n - 1) + 2); + newktable(L, n); /* create a 'ktable' for new tree */ + tree->tag = TCapture; + tree->cap = Cgroup; + tree->key = 0; + tree = sib1(tree); + for (i = 1; i <= n - 1; i++) { + tree->tag = TSeq; + tree->u.ps = 3; /* skip TCapture and its sibling */ + auxemptycap(sib1(tree), Cconst); + sib1(tree)->key = addtoktable(L, i); + tree = sib2(tree); + } + auxemptycap(tree, Cconst); + tree->key = addtoktable(L, i); + } + return 1; +} + + +static int lp_matchtime (lua_State *L) { + TTree *tree; + luaL_checktype(L, 2, LUA_TFUNCTION); + tree = newroot1sib(L, TRunTime); + tree->key = addtonewktable(L, 1, 2); + return 1; +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Grammar - Tree generation +** ======================================================= +*/ + +/* +** push on the stack the index and the pattern for the +** initial rule of grammar at index 'arg' in the stack; +** also add that index into position table. +*/ +static void getfirstrule (lua_State *L, int arg, int postab) { + lua_rawgeti(L, arg, 1); /* access first element */ + if (lua_isstring(L, -1)) { /* is it the name of initial rule? */ + lua_pushvalue(L, -1); /* duplicate it to use as key */ + lua_gettable(L, arg); /* get associated rule */ + } + else { + lua_pushinteger(L, 1); /* key for initial rule */ + lua_insert(L, -2); /* put it before rule */ + } + if (!testpattern(L, -1)) { /* initial rule not a pattern? */ + if (lua_isnil(L, -1)) + luaL_error(L, "grammar has no initial rule"); + else + luaL_error(L, "initial rule '%s' is not a pattern", lua_tostring(L, -2)); + } + lua_pushvalue(L, -2); /* push key */ + lua_pushinteger(L, 1); /* push rule position (after TGrammar) */ + lua_settable(L, postab); /* insert pair at position table */ +} + +/* +** traverse grammar at index 'arg', pushing all its keys and patterns +** into the stack. Create a new table (before all pairs key-pattern) to +** collect all keys and their associated positions in the final tree +** (the "position table"). +** Return the number of rules and (in 'totalsize') the total size +** for the new tree. +*/ +static int collectrules (lua_State *L, int arg, int *totalsize) { + int n = 1; /* to count number of rules */ + int postab = lua_gettop(L) + 1; /* index of position table */ + int size; /* accumulator for total size */ + lua_newtable(L); /* create position table */ + getfirstrule(L, arg, postab); + size = 2 + getsize(L, postab + 2); /* TGrammar + TRule + rule */ + lua_pushnil(L); /* prepare to traverse grammar table */ + while (lua_next(L, arg) != 0) { + if (lua_tonumber(L, -2) == 1 || + lp_equal(L, -2, postab + 1)) { /* initial rule? */ + lua_pop(L, 1); /* remove value (keep key for lua_next) */ + continue; + } + if (!testpattern(L, -1)) /* value is not a pattern? */ + luaL_error(L, "rule '%s' is not a pattern", val2str(L, -2)); + luaL_checkstack(L, LUA_MINSTACK, "grammar has too many rules"); + lua_pushvalue(L, -2); /* push key (to insert into position table) */ + lua_pushinteger(L, size); + lua_settable(L, postab); + size += 1 + getsize(L, -1); /* update size */ + lua_pushvalue(L, -2); /* push key (for next lua_next) */ + n++; + } + *totalsize = size + 1; /* TTrue to finish list of rules */ + return n; +} + + +static void buildgrammar (lua_State *L, TTree *grammar, int frule, int n) { + int i; + TTree *nd = sib1(grammar); /* auxiliary pointer to traverse the tree */ + for (i = 0; i < n; i++) { /* add each rule into new tree */ + int ridx = frule + 2*i + 1; /* index of i-th rule */ + int rulesize; + TTree *rn = gettree(L, ridx, &rulesize); + nd->tag = TRule; + nd->key = 0; /* will be fixed when rule is used */ + nd->cap = i; /* rule number */ + nd->u.ps = rulesize + 1; /* point to next rule */ + memcpy(sib1(nd), rn, rulesize * sizeof(TTree)); /* copy rule */ + mergektable(L, ridx, sib1(nd)); /* merge its ktable into new one */ + nd = sib2(nd); /* move to next rule */ + } + nd->tag = TTrue; /* finish list of rules */ +} + + +/* +** Check whether a tree has potential infinite loops +*/ +static int checkloops (TTree *tree) { + tailcall: + if (tree->tag == TRep && nullable(sib1(tree))) + return 1; + else if (tree->tag == TGrammar) + return 0; /* sub-grammars already checked */ + else { + switch (numsiblings[tree->tag]) { + case 1: /* return checkloops(sib1(tree)); */ + tree = sib1(tree); goto tailcall; + case 2: + if (checkloops(sib1(tree))) return 1; + /* else return checkloops(sib2(tree)); */ + tree = sib2(tree); goto tailcall; + default: assert(numsiblings[tree->tag] == 0); return 0; + } + } +} + + +/* +** Give appropriate error message for 'verifyrule'. If a rule appears +** twice in 'passed', there is path from it back to itself without +** advancing the subject. +*/ +static int verifyerror (lua_State *L, int *passed, int npassed) { + int i, j; + for (i = npassed - 1; i >= 0; i--) { /* search for a repetition */ + for (j = i - 1; j >= 0; j--) { + if (passed[i] == passed[j]) { + lua_rawgeti(L, -1, passed[i]); /* get rule's key */ + return luaL_error(L, "rule '%s' may be left recursive", val2str(L, -1)); + } + } + } + return luaL_error(L, "too many left calls in grammar"); +} + + +/* +** Check whether a rule can be left recursive; raise an error in that +** case; otherwise return 1 iff pattern is nullable. +** The return value is used to check sequences, where the second pattern +** is only relevant if the first is nullable. +** Parameter 'nb' works as an accumulator, to allow tail calls in +** choices. ('nb' true makes function returns true.) +** Parameter 'passed' is a list of already visited rules, 'npassed' +** counts the elements in 'passed'. +** Assume ktable at the top of the stack. +*/ +static int verifyrule (lua_State *L, TTree *tree, int *passed, int npassed, + int nb) { + tailcall: + switch (tree->tag) { + case TChar: case TSet: case TAny: + case TFalse: + return nb; /* cannot pass from here */ + case TTrue: + case TBehind: /* look-behind cannot have calls */ + return 1; + case TNot: case TAnd: case TRep: + /* return verifyrule(L, sib1(tree), passed, npassed, 1); */ + tree = sib1(tree); nb = 1; goto tailcall; + case TCapture: case TRunTime: + /* return verifyrule(L, sib1(tree), passed, npassed, nb); */ + tree = sib1(tree); goto tailcall; + case TCall: + /* return verifyrule(L, sib2(tree), passed, npassed, nb); */ + tree = sib2(tree); goto tailcall; + case TSeq: /* only check 2nd child if first is nb */ + if (!verifyrule(L, sib1(tree), passed, npassed, 0)) + return nb; + /* else return verifyrule(L, sib2(tree), passed, npassed, nb); */ + tree = sib2(tree); goto tailcall; + case TChoice: /* must check both children */ + nb = verifyrule(L, sib1(tree), passed, npassed, nb); + /* return verifyrule(L, sib2(tree), passed, npassed, nb); */ + tree = sib2(tree); goto tailcall; + case TRule: + if (npassed >= MAXRULES) + return verifyerror(L, passed, npassed); + else { + passed[npassed++] = tree->key; + /* return verifyrule(L, sib1(tree), passed, npassed); */ + tree = sib1(tree); goto tailcall; + } + case TGrammar: + return nullable(tree); /* sub-grammar cannot be left recursive */ + default: assert(0); return 0; + } +} + + +static void verifygrammar (lua_State *L, TTree *grammar) { + int passed[MAXRULES]; + TTree *rule; + /* check left-recursive rules */ + for (rule = sib1(grammar); rule->tag == TRule; rule = sib2(rule)) { + if (rule->key == 0) continue; /* unused rule */ + verifyrule(L, sib1(rule), passed, 0, 0); + } + assert(rule->tag == TTrue); + /* check infinite loops inside rules */ + for (rule = sib1(grammar); rule->tag == TRule; rule = sib2(rule)) { + if (rule->key == 0) continue; /* unused rule */ + if (checkloops(sib1(rule))) { + lua_rawgeti(L, -1, rule->key); /* get rule's key */ + luaL_error(L, "empty loop in rule '%s'", val2str(L, -1)); + } + } + assert(rule->tag == TTrue); +} + + +/* +** Give a name for the initial rule if it is not referenced +*/ +static void initialrulename (lua_State *L, TTree *grammar, int frule) { + if (sib1(grammar)->key == 0) { /* initial rule is not referenced? */ + int n = lua_rawlen(L, -1) + 1; /* index for name */ + lua_pushvalue(L, frule); /* rule's name */ + lua_rawseti(L, -2, n); /* ktable was on the top of the stack */ + sib1(grammar)->key = n; + } +} + + +static TTree *newgrammar (lua_State *L, int arg) { + int treesize; + int frule = lua_gettop(L) + 2; /* position of first rule's key */ + int n = collectrules(L, arg, &treesize); + TTree *g = newtree(L, treesize); + luaL_argcheck(L, n <= MAXRULES, arg, "grammar has too many rules"); + g->tag = TGrammar; g->u.n = n; + lua_newtable(L); /* create 'ktable' */ + lua_setuservalue(L, -2); + buildgrammar(L, g, frule, n); + lua_getuservalue(L, -1); /* get 'ktable' for new tree */ + finalfix(L, frule - 1, g, sib1(g)); + initialrulename(L, g, frule); + verifygrammar(L, g); + lua_pop(L, 1); /* remove 'ktable' */ + lua_insert(L, -(n * 2 + 2)); /* move new table to proper position */ + lua_pop(L, n * 2 + 1); /* remove position table + rule pairs */ + return g; /* new table at the top of the stack */ +} + +/* }====================================================== */ + + +static Instruction *prepcompile (lua_State *L, Pattern *p, int idx) { + lua_getuservalue(L, idx); /* push 'ktable' (may be used by 'finalfix') */ + finalfix(L, 0, NULL, p->tree); + lua_pop(L, 1); /* remove 'ktable' */ + return compile(L, p); +} + + +static int lp_printtree (lua_State *L) { + TTree *tree = getpatt(L, 1, NULL); + int c = lua_toboolean(L, 2); + if (c) { + lua_getuservalue(L, 1); /* push 'ktable' (may be used by 'finalfix') */ + finalfix(L, 0, NULL, tree); + lua_pop(L, 1); /* remove 'ktable' */ + } + printktable(L, 1); + printtree(tree, 0); + return 0; +} + + +static int lp_printcode (lua_State *L) { + Pattern *p = getpattern(L, 1); + printktable(L, 1); + if (p->code == NULL) /* not compiled yet? */ + prepcompile(L, p, 1); + printpatt(p->code, p->codesize); + return 0; +} + + +/* +** Get the initial position for the match, interpreting negative +** values from the end of the subject +*/ +static size_t initposition (lua_State *L, size_t len) { + lua_Integer ii = luaL_optinteger(L, 3, 1); + if (ii > 0) { /* positive index? */ + if ((size_t)ii <= len) /* inside the string? */ + return (size_t)ii - 1; /* return it (corrected to 0-base) */ + else return len; /* crop at the end */ + } + else { /* negative index */ + if ((size_t)(-ii) <= len) /* inside the string? */ + return len - ((size_t)(-ii)); /* return position from the end */ + else return 0; /* crop at the beginning */ + } +} + + +/* +** Main match function +*/ +static int lp_match (lua_State *L) { + Capture capture[INITCAPSIZE]; + const char *r; + size_t l; + Pattern *p = (getpatt(L, 1, NULL), getpattern(L, 1)); + Instruction *code = (p->code != NULL) ? p->code : prepcompile(L, p, 1); + const char *s = luaL_checklstring(L, SUBJIDX, &l); + size_t i = initposition(L, l); + int ptop = lua_gettop(L); + lua_pushnil(L); /* initialize subscache */ + lua_pushlightuserdata(L, capture); /* initialize caplistidx */ + lua_getuservalue(L, 1); /* initialize penvidx */ + r = match(L, s, s + i, s + l, code, capture, ptop); + if (r == NULL) { + lua_pushnil(L); + return 1; + } + return getcaptures(L, s, r, ptop); +} + + + +/* +** {====================================================== +** Library creation and functions not related to matching +** ======================================================= +*/ + +/* maximum limit for stack size */ +#define MAXLIM (INT_MAX / 100) + +static int lp_setmax (lua_State *L) { + lua_Integer lim = luaL_checkinteger(L, 1); + luaL_argcheck(L, 0 < lim && lim <= MAXLIM, 1, "out of range"); + lua_settop(L, 1); + lua_setfield(L, LUA_REGISTRYINDEX, MAXSTACKIDX); + return 0; +} + + +static int lp_version (lua_State *L) { + lua_pushstring(L, VERSION); + return 1; +} + + +static int lp_type (lua_State *L) { + if (testpattern(L, 1)) + lua_pushliteral(L, "pattern"); + else + lua_pushnil(L); + return 1; +} + + +int lp_gc (lua_State *L) { + Pattern *p = getpattern(L, 1); + realloccode(L, p, 0); /* delete code block */ + return 0; +} + + +static void createcat (lua_State *L, const char *catname, int (catf) (int)) { + TTree *t = newcharset(L); + int i; + for (i = 0; i <= UCHAR_MAX; i++) + if (catf(i)) setchar(treebuffer(t), i); + lua_setfield(L, -2, catname); +} + + +static int lp_locale (lua_State *L) { + if (lua_isnoneornil(L, 1)) { + lua_settop(L, 0); + lua_createtable(L, 0, 12); + } + else { + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 1); + } + createcat(L, "alnum", isalnum); + createcat(L, "alpha", isalpha); + createcat(L, "cntrl", iscntrl); + createcat(L, "digit", isdigit); + createcat(L, "graph", isgraph); + createcat(L, "lower", islower); + createcat(L, "print", isprint); + createcat(L, "punct", ispunct); + createcat(L, "space", isspace); + createcat(L, "upper", isupper); + createcat(L, "xdigit", isxdigit); + return 1; +} + + +static struct luaL_Reg pattreg[] = { + {"ptree", lp_printtree}, + {"pcode", lp_printcode}, + {"match", lp_match}, + {"B", lp_behind}, + {"V", lp_V}, + {"C", lp_simplecapture}, + {"Cc", lp_constcapture}, + {"Cmt", lp_matchtime}, + {"Cb", lp_backref}, + {"Carg", lp_argcapture}, + {"Cp", lp_poscapture}, + {"Cs", lp_substcapture}, + {"Ct", lp_tablecapture}, + {"Cf", lp_foldcapture}, + {"Cg", lp_groupcapture}, + {"P", lp_P}, + {"S", lp_set}, + {"R", lp_range}, + {"locale", lp_locale}, + {"version", lp_version}, + {"setmaxstack", lp_setmax}, + {"type", lp_type}, + {NULL, NULL} +}; + + +static struct luaL_Reg metareg[] = { + {"__mul", lp_seq}, + {"__add", lp_choice}, + {"__pow", lp_star}, + {"__gc", lp_gc}, + {"__len", lp_and}, + {"__div", lp_divcapture}, + {"__unm", lp_not}, + {"__sub", lp_sub}, + {NULL, NULL} +}; + + +int luaopen_lpeg (lua_State *L); +int luaopen_lpeg (lua_State *L) { + luaL_newmetatable(L, PATTERN_T); + lua_pushnumber(L, MAXBACK); /* initialize maximum backtracking */ + lua_setfield(L, LUA_REGISTRYINDEX, MAXSTACKIDX); + luaL_setfuncs(L, metareg, 0); + luaL_newlib(L, pattreg); + lua_pushvalue(L, -1); + lua_setfield(L, -3, "__index"); + return 1; +} + +/* }====================================================== */ diff --git a/luaclib/src/lpeg/lptree.h b/luaclib/src/lpeg/lptree.h new file mode 100644 index 00000000..25906d5f --- /dev/null +++ b/luaclib/src/lpeg/lptree.h @@ -0,0 +1,82 @@ +/* +** $Id: lptree.h $ +*/ + +#if !defined(lptree_h) +#define lptree_h + + +#include "lptypes.h" + + +/* +** types of trees +*/ +typedef enum TTag { + TChar = 0, /* 'n' = char */ + TSet, /* the set is stored in next CHARSETSIZE bytes */ + TAny, + TTrue, + TFalse, + TRep, /* 'sib1'* */ + TSeq, /* 'sib1' 'sib2' */ + TChoice, /* 'sib1' / 'sib2' */ + TNot, /* !'sib1' */ + TAnd, /* &'sib1' */ + TCall, /* ktable[key] is rule's key; 'sib2' is rule being called */ + TOpenCall, /* ktable[key] is rule's key */ + TRule, /* ktable[key] is rule's key (but key == 0 for unused rules); + 'sib1' is rule's pattern; + 'sib2' is next rule; 'cap' is rule's sequential number */ + TGrammar, /* 'sib1' is initial (and first) rule */ + TBehind, /* 'sib1' is pattern, 'n' is how much to go back */ + TCapture, /* captures: 'cap' is kind of capture (enum 'CapKind'); + ktable[key] is Lua value associated with capture; + 'sib1' is capture body */ + TRunTime /* run-time capture: 'key' is Lua function; + 'sib1' is capture body */ +} TTag; + + +/* +** Tree trees +** The first child of a tree (if there is one) is immediately after +** the tree. A reference to a second child (ps) is its position +** relative to the position of the tree itself. +*/ +typedef struct TTree { + byte tag; + byte cap; /* kind of capture (if it is a capture) */ + unsigned short key; /* key in ktable for Lua data (0 if no key) */ + union { + int ps; /* occasional second child */ + int n; /* occasional counter */ + } u; +} TTree; + + +/* +** A complete pattern has its tree plus, if already compiled, +** its corresponding code +*/ +typedef struct Pattern { + union Instruction *code; + int codesize; + TTree tree[1]; +} Pattern; + + +/* number of children for each tree */ +extern const byte numsiblings[]; + +/* access to children */ +#define sib1(t) ((t) + 1) +#define sib2(t) ((t) + (t)->u.ps) + + + + + + +#endif + diff --git a/luaclib/src/lpeg/lptypes.h b/luaclib/src/lpeg/lptypes.h new file mode 100644 index 00000000..1d9d59f6 --- /dev/null +++ b/luaclib/src/lpeg/lptypes.h @@ -0,0 +1,145 @@ +/* +** $Id: lptypes.h $ +** LPeg - PEG pattern matching for Lua +** Copyright 2007-2019, Lua.org & PUC-Rio (see 'lpeg.html' for license) +** written by Roberto Ierusalimschy +*/ + +#if !defined(lptypes_h) +#define lptypes_h + + +#include +#include + +#include "lua.h" + + +#define VERSION "1.0.2" + + +#define PATTERN_T "lpeg-pattern" +#define MAXSTACKIDX "lpeg-maxstack" + + +/* +** compatibility with Lua 5.1 +*/ +#if (LUA_VERSION_NUM == 501) + +#define lp_equal lua_equal + +#define lua_getuservalue lua_getfenv +#define lua_setuservalue lua_setfenv + +#define lua_rawlen lua_objlen + +#define luaL_setfuncs(L,f,n) luaL_register(L,NULL,f) +#define luaL_newlib(L,f) luaL_register(L,"lpeg",f) + +#endif + + +#if !defined(lp_equal) +#define lp_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ) +#endif + + +/* default maximum size for call/backtrack stack */ +#if !defined(MAXBACK) +#define MAXBACK 400 +#endif + + +/* maximum number of rules in a grammar (limited by 'unsigned char') */ +#if !defined(MAXRULES) +#define MAXRULES 250 +#endif + + + +/* initial size for capture's list */ +#define INITCAPSIZE 32 + + +/* index, on Lua stack, for subject */ +#define SUBJIDX 2 + +/* number of fixed arguments to 'match' (before capture arguments) */ +#define FIXEDARGS 3 + +/* index, on Lua stack, for capture list */ +#define caplistidx(ptop) ((ptop) + 2) + +/* index, on Lua stack, for pattern's ktable */ +#define ktableidx(ptop) ((ptop) + 3) + +/* index, on Lua stack, for backtracking stack */ +#define stackidx(ptop) ((ptop) + 4) + + + +typedef unsigned char byte; + + +#define BITSPERCHAR 8 + +#define CHARSETSIZE ((UCHAR_MAX/BITSPERCHAR) + 1) + + + +typedef struct Charset { + byte cs[CHARSETSIZE]; +} Charset; + + + +#define loopset(v,b) { int v; for (v = 0; v < CHARSETSIZE; v++) {b;} } + +/* access to charset */ +#define treebuffer(t) ((byte *)((t) + 1)) + +/* number of slots needed for 'n' bytes */ +#define bytes2slots(n) (((n) - 1) / sizeof(TTree) + 1) + +/* set 'b' bit in charset 'cs' */ +#define setchar(cs,b) ((cs)[(b) >> 3] |= (1 << ((b) & 7))) + + +/* +** in capture instructions, 'kind' of capture and its offset are +** packed in field 'aux', 4 bits for each +*/ +#define getkind(op) ((op)->i.aux & 0xF) +#define getoff(op) (((op)->i.aux >> 4) & 0xF) +#define joinkindoff(k,o) ((k) | ((o) << 4)) + +#define MAXOFF 0xF +#define MAXAUX 0xFF + + +/* maximum number of bytes to look behind */ +#define MAXBEHIND MAXAUX + + +/* maximum size (in elements) for a pattern */ +#define MAXPATTSIZE (SHRT_MAX - 10) + + +/* size (in elements) for an instruction plus extra l bytes */ +#define instsize(l) (((l) + sizeof(Instruction) - 1)/sizeof(Instruction) + 1) + + +/* size (in elements) for a ISet instruction */ +#define CHARSETINSTSIZE instsize(CHARSETSIZE) + +/* size (in elements) for a IFunc instruction */ +#define funcinstsize(p) ((p)->i.aux + 2) + + + +#define testchar(st,c) (((int)(st)[((c) >> 3)] & (1 << ((c) & 7)))) + + +#endif + diff --git a/luaclib/src/lpeg/lpvm.c b/luaclib/src/lpeg/lpvm.c new file mode 100644 index 00000000..737418c4 --- /dev/null +++ b/luaclib/src/lpeg/lpvm.c @@ -0,0 +1,374 @@ +/* +** $Id: lpvm.c $ +** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license) +*/ + +#include +#include + + +#include "lua.h" +#include "lauxlib.h" + +#include "lpcap.h" +#include "lptypes.h" +#include "lpvm.h" +#include "lpprint.h" + + +/* initial size for call/backtrack stack */ +#if !defined(INITBACK) +#define INITBACK MAXBACK +#endif + + +#define getoffset(p) (((p) + 1)->offset) + +static const Instruction giveup = {{IGiveup, 0, 0}}; + + +/* +** {====================================================== +** Virtual Machine +** ======================================================= +*/ + + +typedef struct Stack { + const char *s; /* saved position (or NULL for calls) */ + const Instruction *p; /* next instruction */ + int caplevel; +} Stack; + + +#define getstackbase(L, ptop) ((Stack *)lua_touserdata(L, stackidx(ptop))) + + +/* +** Ensures the size of array 'capture' (with size '*capsize' and +** 'captop' elements being used) is enough to accomodate 'n' extra +** elements plus one. (Because several opcodes add stuff to the capture +** array, it is simpler to ensure the array always has at least one free +** slot upfront and check its size later.) +*/ +static Capture *growcap (lua_State *L, Capture *capture, int *capsize, + int captop, int n, int ptop) { + if (*capsize - captop > n) + return capture; /* no need to grow array */ + else { /* must grow */ + Capture *newc; + int newsize = captop + n + 1; /* minimum size needed */ + if (newsize < INT_MAX/((int)sizeof(Capture) * 2)) + newsize *= 2; /* twice that size, if not too big */ + else if (newsize >= INT_MAX/((int)sizeof(Capture))) + luaL_error(L, "too many captures"); + newc = (Capture *)lua_newuserdata(L, newsize * sizeof(Capture)); + memcpy(newc, capture, captop * sizeof(Capture)); + *capsize = newsize; + lua_replace(L, caplistidx(ptop)); + return newc; + } +} + + +/* +** Double the size of the stack +*/ +static Stack *doublestack (lua_State *L, Stack **stacklimit, int ptop) { + Stack *stack = getstackbase(L, ptop); + Stack *newstack; + int n = *stacklimit - stack; /* current stack size */ + int max, newn; + lua_getfield(L, LUA_REGISTRYINDEX, MAXSTACKIDX); + max = lua_tointeger(L, -1); /* maximum allowed size */ + lua_pop(L, 1); + if (n >= max) /* already at maximum size? */ + luaL_error(L, "backtrack stack overflow (current limit is %d)", max); + newn = 2 * n; /* new size */ + if (newn > max) newn = max; + newstack = (Stack *)lua_newuserdata(L, newn * sizeof(Stack)); + memcpy(newstack, stack, n * sizeof(Stack)); + lua_replace(L, stackidx(ptop)); + *stacklimit = newstack + newn; + return newstack + n; /* return next position */ +} + + +/* +** Interpret the result of a dynamic capture: false -> fail; +** true -> keep current position; number -> next position. +** Return new subject position. 'fr' is stack index where +** is the result; 'curr' is current subject position; 'limit' +** is subject's size. +*/ +static int resdyncaptures (lua_State *L, int fr, int curr, int limit) { + lua_Integer res; + if (!lua_toboolean(L, fr)) { /* false value? */ + lua_settop(L, fr - 1); /* remove results */ + return -1; /* and fail */ + } + else if (lua_isboolean(L, fr)) /* true? */ + res = curr; /* keep current position */ + else { + res = lua_tointeger(L, fr) - 1; /* new position */ + if (res < curr || res > limit) + luaL_error(L, "invalid position returned by match-time capture"); + } + lua_remove(L, fr); /* remove first result (offset) */ + return res; +} + + +/* +** Add capture values returned by a dynamic capture to the list +** 'capture', nested inside a group. 'fd' indexes the first capture +** value, 'n' is the number of values (at least 1). The open group +** capture is already in 'capture', before the place for the new entries. +*/ +static void adddyncaptures (const char *s, Capture *capture, int n, int fd) { + int i; + assert(capture[-1].kind == Cgroup && capture[-1].siz == 0); + capture[-1].idx = 0; /* make group capture an anonymous group */ + for (i = 0; i < n; i++) { /* add runtime captures */ + capture[i].kind = Cruntime; + capture[i].siz = 1; /* mark it as closed */ + capture[i].idx = fd + i; /* stack index of capture value */ + capture[i].s = s; + } + capture[n].kind = Cclose; /* close group */ + capture[n].siz = 1; + capture[n].s = s; +} + + +/* +** Remove dynamic captures from the Lua stack (called in case of failure) +*/ +static int removedyncap (lua_State *L, Capture *capture, + int level, int last) { + int id = finddyncap(capture + level, capture + last); /* index of 1st cap. */ + int top = lua_gettop(L); + if (id == 0) return 0; /* no dynamic captures? */ + lua_settop(L, id - 1); /* remove captures */ + return top - id + 1; /* number of values removed */ +} + + +/* +** Opcode interpreter +*/ +const char *match (lua_State *L, const char *o, const char *s, const char *e, + Instruction *op, Capture *capture, int ptop) { + Stack stackbase[INITBACK]; + Stack *stacklimit = stackbase + INITBACK; + Stack *stack = stackbase; /* point to first empty slot in stack */ + int capsize = INITCAPSIZE; + int captop = 0; /* point to first empty slot in captures */ + int ndyncap = 0; /* number of dynamic captures (in Lua stack) */ + const Instruction *p = op; /* current instruction */ + stack->p = &giveup; stack->s = s; stack->caplevel = 0; stack++; + lua_pushlightuserdata(L, stackbase); + for (;;) { +#if defined(DEBUG) + printf("-------------------------------------\n"); + printcaplist(capture, capture + captop); + printf("s: |%s| stck:%d, dyncaps:%d, caps:%d ", + s, (int)(stack - getstackbase(L, ptop)), ndyncap, captop); + printinst(op, p); +#endif + assert(stackidx(ptop) + ndyncap == lua_gettop(L) && ndyncap <= captop); + switch ((Opcode)p->i.code) { + case IEnd: { + assert(stack == getstackbase(L, ptop) + 1); + capture[captop].kind = Cclose; + capture[captop].s = NULL; + return s; + } + case IGiveup: { + assert(stack == getstackbase(L, ptop)); + return NULL; + } + case IRet: { + assert(stack > getstackbase(L, ptop) && (stack - 1)->s == NULL); + p = (--stack)->p; + continue; + } + case IAny: { + if (s < e) { p++; s++; } + else goto fail; + continue; + } + case ITestAny: { + if (s < e) p += 2; + else p += getoffset(p); + continue; + } + case IChar: { + if ((byte)*s == p->i.aux && s < e) { p++; s++; } + else goto fail; + continue; + } + case ITestChar: { + if ((byte)*s == p->i.aux && s < e) p += 2; + else p += getoffset(p); + continue; + } + case ISet: { + int c = (byte)*s; + if (testchar((p+1)->buff, c) && s < e) + { p += CHARSETINSTSIZE; s++; } + else goto fail; + continue; + } + case ITestSet: { + int c = (byte)*s; + if (testchar((p + 2)->buff, c) && s < e) + p += 1 + CHARSETINSTSIZE; + else p += getoffset(p); + continue; + } + case IBehind: { + int n = p->i.aux; + if (n > s - o) goto fail; + s -= n; p++; + continue; + } + case ISpan: { + for (; s < e; s++) { + int c = (byte)*s; + if (!testchar((p+1)->buff, c)) break; + } + p += CHARSETINSTSIZE; + continue; + } + case IJmp: { + p += getoffset(p); + continue; + } + case IChoice: { + if (stack == stacklimit) + stack = doublestack(L, &stacklimit, ptop); + stack->p = p + getoffset(p); + stack->s = s; + stack->caplevel = captop; + stack++; + p += 2; + continue; + } + case ICall: { + if (stack == stacklimit) + stack = doublestack(L, &stacklimit, ptop); + stack->s = NULL; + stack->p = p + 2; /* save return address */ + stack++; + p += getoffset(p); + continue; + } + case ICommit: { + assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL); + stack--; + p += getoffset(p); + continue; + } + case IPartialCommit: { + assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL); + (stack - 1)->s = s; + (stack - 1)->caplevel = captop; + p += getoffset(p); + continue; + } + case IBackCommit: { + assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL); + s = (--stack)->s; + captop = stack->caplevel; + p += getoffset(p); + continue; + } + case IFailTwice: + assert(stack > getstackbase(L, ptop)); + stack--; + /* go through */ + case IFail: + fail: { /* pattern failed: try to backtrack */ + do { /* remove pending calls */ + assert(stack > getstackbase(L, ptop)); + s = (--stack)->s; + } while (s == NULL); + if (ndyncap > 0) /* is there matchtime captures? */ + ndyncap -= removedyncap(L, capture, stack->caplevel, captop); + captop = stack->caplevel; + p = stack->p; +#if defined(DEBUG) + printf("**FAIL**\n"); +#endif + continue; + } + case ICloseRunTime: { + CapState cs; + int rem, res, n; + int fr = lua_gettop(L) + 1; /* stack index of first result */ + cs.reclevel = 0; cs.L = L; + cs.s = o; cs.ocap = capture; cs.ptop = ptop; + n = runtimecap(&cs, capture + captop, s, &rem); /* call function */ + captop -= n; /* remove nested captures */ + ndyncap -= rem; /* update number of dynamic captures */ + fr -= rem; /* 'rem' items were popped from Lua stack */ + res = resdyncaptures(L, fr, s - o, e - o); /* get result */ + if (res == -1) /* fail? */ + goto fail; + s = o + res; /* else update current position */ + n = lua_gettop(L) - fr + 1; /* number of new captures */ + ndyncap += n; /* update number of dynamic captures */ + if (n == 0) /* no new captures? */ + captop--; /* remove open group */ + else { /* new captures; keep original open group */ + if (fr + n >= SHRT_MAX) + luaL_error(L, "too many results in match-time capture"); + /* add new captures + close group to 'capture' list */ + capture = growcap(L, capture, &capsize, captop, n + 1, ptop); + adddyncaptures(s, capture + captop, n, fr); + captop += n + 1; /* new captures + close group */ + } + p++; + continue; + } + case ICloseCapture: { + const char *s1 = s; + assert(captop > 0); + /* if possible, turn capture into a full capture */ + if (capture[captop - 1].siz == 0 && + s1 - capture[captop - 1].s < UCHAR_MAX) { + capture[captop - 1].siz = s1 - capture[captop - 1].s + 1; + p++; + continue; + } + else { + capture[captop].siz = 1; /* mark entry as closed */ + capture[captop].s = s; + goto pushcapture; + } + } + case IOpenCapture: + capture[captop].siz = 0; /* mark entry as open */ + capture[captop].s = s; + goto pushcapture; + case IFullCapture: + capture[captop].siz = getoff(p) + 1; /* save capture size */ + capture[captop].s = s - getoff(p); + /* goto pushcapture; */ + pushcapture: { + capture[captop].idx = p->i.key; + capture[captop].kind = getkind(p); + captop++; + capture = growcap(L, capture, &capsize, captop, 0, ptop); + p++; + continue; + } + default: assert(0); return NULL; + } + } +} + +/* }====================================================== */ + + diff --git a/luaclib/src/lpeg/lpvm.h b/luaclib/src/lpeg/lpvm.h new file mode 100644 index 00000000..69ec33dc --- /dev/null +++ b/luaclib/src/lpeg/lpvm.h @@ -0,0 +1,58 @@ +/* +** $Id: lpvm.h $ +*/ + +#if !defined(lpvm_h) +#define lpvm_h + +#include "lpcap.h" + + +/* Virtual Machine's instructions */ +typedef enum Opcode { + IAny, /* if no char, fail */ + IChar, /* if char != aux, fail */ + ISet, /* if char not in buff, fail */ + ITestAny, /* in no char, jump to 'offset' */ + ITestChar, /* if char != aux, jump to 'offset' */ + ITestSet, /* if char not in buff, jump to 'offset' */ + ISpan, /* read a span of chars in buff */ + IBehind, /* walk back 'aux' characters (fail if not possible) */ + IRet, /* return from a rule */ + IEnd, /* end of pattern */ + IChoice, /* stack a choice; next fail will jump to 'offset' */ + IJmp, /* jump to 'offset' */ + ICall, /* call rule at 'offset' */ + IOpenCall, /* call rule number 'key' (must be closed to a ICall) */ + ICommit, /* pop choice and jump to 'offset' */ + IPartialCommit, /* update top choice to current position and jump */ + IBackCommit, /* "fails" but jump to its own 'offset' */ + IFailTwice, /* pop one choice and then fail */ + IFail, /* go back to saved state on choice and jump to saved offset */ + IGiveup, /* internal use */ + IFullCapture, /* complete capture of last 'off' chars */ + IOpenCapture, /* start a capture */ + ICloseCapture, + ICloseRunTime +} Opcode; + + + +typedef union Instruction { + struct Inst { + byte code; + byte aux; + short key; + } i; + int offset; + byte buff[1]; +} Instruction; + + +void printpatt (Instruction *p, int n); +const char *match (lua_State *L, const char *o, const char *s, const char *e, + Instruction *op, Capture *capture, int ptop); + + +#endif + diff --git a/luaclib/src/lpeg/makefile b/luaclib/src/lpeg/makefile new file mode 100644 index 00000000..8a5d4061 --- /dev/null +++ b/luaclib/src/lpeg/makefile @@ -0,0 +1,66 @@ +LIBNAME = lpeg +LUADIR = ../lua/ + +COPT = -O2 -DNDEBUG +# COPT = -g + +CWARNS = -Wall -Wextra -pedantic \ + -Waggregate-return \ + -Wcast-align \ + -Wcast-qual \ + -Wdisabled-optimization \ + -Wpointer-arith \ + -Wshadow \ + -Wsign-compare \ + -Wundef \ + -Wwrite-strings \ + -Wbad-function-cast \ + -Wdeclaration-after-statement \ + -Wmissing-prototypes \ + -Wnested-externs \ + -Wstrict-prototypes \ +# -Wunreachable-code \ + + +CFLAGS = $(CWARNS) $(COPT) -std=c99 -I$(LUADIR) -fPIC +CC = gcc + +FILES = lpvm.o lpcap.o lptree.o lpcode.o lpprint.o + +# For Linux +linux: + $(MAKE) lpeg.so "DLLFLAGS = -shared -fPIC" + +# For Mac OS +macosx: + $(MAKE) lpeg.so "DLLFLAGS = -bundle -undefined dynamic_lookup" + +build: $(FILES) + CC -o lpeg.so $(FILES) -shared -fPIC -lcore + mv *.so ../../ + rm *.o *.so + + +rebuild: $(FILES) + CC -o lpeg.so $(FILES) -shared -fPIC -lcore + mv *.so ../../ + rm *.o *.so + + +lpeg.so: $(FILES) + env $(CC) $(DLLFLAGS) $(FILES) -o lpeg.so + +$(FILES): makefile + +test: test.lua re.lua lpeg.so + ./test.lua + +clean: + rm -f $(FILES) lpeg.so + + +lpcap.o: lpcap.c lpcap.h lptypes.h +lpcode.o: lpcode.c lptypes.h lpcode.h lptree.h lpvm.h lpcap.h +lpprint.o: lpprint.c lptypes.h lpprint.h lptree.h lpvm.h lpcap.h +lptree.o: lptree.c lptypes.h lpcap.h lpcode.h lptree.h lpvm.h lpprint.h +lpvm.o: lpvm.c lpcap.h lptypes.h lpvm.h lpprint.h lptree.h diff --git a/luaclib/src/lpeg/re.html b/luaclib/src/lpeg/re.html new file mode 100644 index 00000000..ad60d509 --- /dev/null +++ b/luaclib/src/lpeg/re.html @@ -0,0 +1,494 @@ + + + + LPeg.re - Regex syntax for LPEG + + + + + + + +
+ +
+ +
LPeg.re
+
+ Regex syntax for LPEG +
+
+ +
+ + + +
+ +

The re Module

+ +

+The re module +(provided by file re.lua in the distribution) +supports a somewhat conventional regex syntax +for pattern usage within LPeg. +

+ +

+The next table summarizes re's syntax. +A p represents an arbitrary pattern; +num represents a number ([0-9]+); +name represents an identifier +([a-zA-Z][a-zA-Z0-9_]*). +Constructions are listed in order of decreasing precedence. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SyntaxDescription
( p ) grouping
'string' literal string
"string" literal string
[class] character class
. any character
%namepattern defs[name] or a pre-defined pattern
namenon terminal
<name>non terminal
{} position capture
{ p } simple capture
{: p :} anonymous group capture
{:name: p :} named group capture
{~ p ~} substitution capture
{| p |} table capture
=name back reference +
p ? optional match
p * zero or more repetitions
p + one or more repetitions
p^num exactly n repetitions
p^+numat least n repetitions
p^-numat most n repetitions
p -> 'string' string capture
p -> "string" string capture
p -> num numbered capture
p -> name function/query/string capture +equivalent to p / defs[name]
p => name match-time capture +equivalent to lpeg.Cmt(p, defs[name])
p ~> name fold capture +equivalent to lpeg.Cf(p, defs[name])
& p and predicate
! p not predicate
p1 p2 concatenation
p1 / p2 ordered choice
(name <- p)+ grammar
+

+Any space appearing in a syntax description can be +replaced by zero or more space characters and Lua-style comments +(-- until end of line). +

+ +

+Character classes define sets of characters. +An initial ^ complements the resulting set. +A range x-y includes in the set +all characters with codes between the codes of x and y. +A pre-defined class %name includes all +characters of that class. +A simple character includes itself in the set. +The only special characters inside a class are ^ +(special only if it is the first character); +] +(can be included in the set as the first character, +after the optional ^); +% (special only if followed by a letter); +and - +(can be included in the set as the first or the last character). +

+ +

+Currently the pre-defined classes are similar to those from the +Lua's string library +(%a for letters, +%A for non letters, etc.). +There is also a class %nl +containing only the newline character, +which is particularly handy for grammars written inside long strings, +as long strings do not interpret escape sequences like \n. +

+ + +

Functions

+ +

re.compile (string, [, defs])

+

+Compiles the given string and +returns an equivalent LPeg pattern. +The given string may define either an expression or a grammar. +The optional defs table provides extra Lua values +to be used by the pattern. +

+ +

re.find (subject, pattern [, init])

+

+Searches the given pattern in the given subject. +If it finds a match, +returns the index where this occurrence starts and +the index where it ends. +Otherwise, returns nil. +

+ +

+An optional numeric argument init makes the search +starts at that position in the subject string. +As usual in Lua libraries, +a negative value counts from the end. +

+ +

re.gsub (subject, pattern, replacement)

+

+Does a global substitution, +replacing all occurrences of pattern +in the given subject by replacement. + +

re.match (subject, pattern)

+

+Matches the given pattern against the given subject, +returning all captures. +

+ +

re.updatelocale ()

+

+Updates the pre-defined character classes to the current locale. +

+ + +

Some Examples

+ +

A complete simple program

+

+The next code shows a simple complete Lua program using +the re module: +

+
+local re = require"re"
+
+-- find the position of the first numeral in a string
+print(re.find("the number 423 is odd", "[0-9]+"))  --> 12    14
+
+-- returns all words in a string
+print(re.match("the number 423 is odd", "({%a+} / .)*"))
+--> the    number    is    odd
+
+-- returns the first numeral in a string
+print(re.match("the number 423 is odd", "s <- {%d+} / . s"))
+--> 423
+
+print(re.gsub("hello World", "[aeiou]", "."))
+--> h.ll. W.rld
+
+ + +

Balanced parentheses

+

+The following call will produce the same pattern produced by the +Lua expression in the +balanced parentheses example: +

+
+b = re.compile[[  balanced <- "(" ([^()] / balanced)* ")"  ]]
+
+ +

String reversal

+

+The next example reverses a string: +

+
+rev = re.compile[[ R <- (!.) -> '' / ({.} R) -> '%2%1']]
+print(rev:match"0123456789")   --> 9876543210
+
+ +

CSV decoder

+

+The next example replicates the CSV decoder: +

+
+record = re.compile[[
+  record <- {| field (',' field)* |} (%nl / !.)
+  field <- escaped / nonescaped
+  nonescaped <- { [^,"%nl]* }
+  escaped <- '"' {~ ([^"] / '""' -> '"')* ~} '"'
+]]
+
+ +

Lua's long strings

+

+The next example matches Lua long strings: +

+
+c = re.compile([[
+  longstring <- ('[' {:eq: '='* :} '[' close)
+  close <- ']' =eq ']' / . close
+]])
+
+print(c:match'[==[]]===]]]]==]===[]')   --> 17
+
+ +

Abstract Syntax Trees

+

+This example shows a simple way to build an +abstract syntax tree (AST) for a given grammar. +To keep our example simple, +let us consider the following grammar +for lists of names: +

+
+p = re.compile[[
+      listname <- (name s)*
+      name <- [a-z][a-z]*
+      s <- %s*
+]]
+
+

+Now, we will add captures to build a corresponding AST. +As a first step, the pattern will build a table to +represent each non terminal; +terminals will be represented by their corresponding strings: +

+
+c = re.compile[[
+      listname <- {| (name s)* |}
+      name <- {| {[a-z][a-z]*} |}
+      s <- %s*
+]]
+
+

+Now, a match against "hi hello bye" +results in the table +{{"hi"}, {"hello"}, {"bye"}}. +

+

+For such a simple grammar, +this AST is more than enough; +actually, the tables around each single name +are already overkilling. +More complex grammars, +however, may need some more structure. +Specifically, +it would be useful if each table had +a tag field telling what non terminal +that table represents. +We can add such a tag using +named group captures: +

+
+x = re.compile[[
+      listname <- {| {:tag: '' -> 'list':} (name s)* |}
+      name <- {| {:tag: '' -> 'id':} {[a-z][a-z]*} |}
+      s <- ' '*
+]]
+
+

+With these group captures, +a match against "hi hello bye" +results in the following table: +

+
+{tag="list",
+  {tag="id", "hi"},
+  {tag="id", "hello"},
+  {tag="id", "bye"}
+}
+
+ + +

Indented blocks

+

+This example breaks indented blocks into tables, +respecting the indentation: +

+
+p = re.compile[[
+  block <- {| {:ident:' '*:} line
+           ((=ident !' ' line) / &(=ident ' ') block)* |}
+  line <- {[^%nl]*} %nl
+]]
+
+

+As an example, +consider the following text: +

+
+t = p:match[[
+first line
+  subline 1
+  subline 2
+second line
+third line
+  subline 3.1
+    subline 3.1.1
+  subline 3.2
+]]
+
+

+The resulting table t will be like this: +

+
+   {'first line'; {'subline 1'; 'subline 2'; ident = '  '};
+    'second line';
+    'third line'; { 'subline 3.1'; {'subline 3.1.1'; ident = '    '};
+                    'subline 3.2'; ident = '  '};
+    ident = ''}
+
+ +

Macro expander

+

+This example implements a simple macro expander. +Macros must be defined as part of the pattern, +following some simple rules: +

+
+p = re.compile[[
+      text <- {~ item* ~}
+      item <- macro / [^()] / '(' item* ')'
+      arg <- ' '* {~ (!',' item)* ~}
+      args <- '(' arg (',' arg)* ')'
+      -- now we define some macros
+      macro <- ('apply' args) -> '%1(%2)'
+             / ('add' args) -> '%1 + %2'
+             / ('mul' args) -> '%1 * %2'
+]]
+
+print(p:match"add(mul(a,b), apply(f,x))")   --> a * b + f(x)
+
+

+A text is a sequence of items, +wherein we apply a substitution capture to expand any macros. +An item is either a macro, +any character different from parentheses, +or a parenthesized expression. +A macro argument (arg) is a sequence +of items different from a comma. +(Note that a comma may appear inside an item, +e.g., inside a parenthesized expression.) +Again we do a substitution capture to expand any macro +in the argument before expanding the outer macro. +args is a list of arguments separated by commas. +Finally we define the macros. +Each macro is a string substitution; +it replaces the macro name and its arguments by its corresponding string, +with each %n replaced by the n-th argument. +

+ +

Patterns

+

+This example shows the complete syntax +of patterns accepted by re. +

+
+p = [=[
+
+pattern         <- exp !.
+exp             <- S (grammar / alternative)
+
+alternative     <- seq ('/' S seq)*
+seq             <- prefix*
+prefix          <- '&' S prefix / '!' S prefix / suffix
+suffix          <- primary S (([+*?]
+                            / '^' [+-]? num
+                            / '->' S (string / '{}' / name)
+                            / '=>' S name) S)*
+
+primary         <- '(' exp ')' / string / class / defined
+                 / '{:' (name ':')? exp ':}'
+                 / '=' name
+                 / '{}'
+                 / '{~' exp '~}'
+                 / '{' exp '}'
+                 / '.'
+                 / name S !arrow
+                 / '<' name '>'          -- old-style non terminals
+
+grammar         <- definition+
+definition      <- name S arrow exp
+
+class           <- '[' '^'? item (!']' item)* ']'
+item            <- defined / range / .
+range           <- . '-' [^]]
+
+S               <- (%s / '--' [^%nl]*)*   -- spaces and comments
+name            <- [A-Za-z][A-Za-z0-9_]*
+arrow           <- '<-'
+num             <- [0-9]+
+string          <- '"' [^"]* '"' / "'" [^']* "'"
+defined         <- '%' name
+
+]=]
+
+print(re.match(p, p))   -- a self description must match itself
+
+ + + +

License

+ +

+Copyright © 2008-2015 Lua.org, PUC-Rio. +

+

+Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), +to deal in the Software without restriction, +including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, +and to permit persons to whom the Software is +furnished to do so, +subject to the following conditions: +

+ +

+The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. +

+ +

+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +

+ +
+ +
+ +
+ + + diff --git a/luaclib/src/lpeg/re.lua b/luaclib/src/lpeg/re.lua new file mode 100644 index 00000000..3bb8af7d --- /dev/null +++ b/luaclib/src/lpeg/re.lua @@ -0,0 +1,267 @@ +-- $Id: re.lua $ + +-- imported functions and modules +local tonumber, type, print, error = tonumber, type, print, error +local setmetatable = setmetatable +local m = require"lpeg" + +-- 'm' will be used to parse expressions, and 'mm' will be used to +-- create expressions; that is, 're' runs on 'm', creating patterns +-- on 'mm' +local mm = m + +-- pattern's metatable +local mt = getmetatable(mm.P(0)) + + + +-- No more global accesses after this point +local version = _VERSION +if version == "Lua 5.2" then _ENV = nil end + + +local any = m.P(1) + + +-- Pre-defined names +local Predef = { nl = m.P"\n" } + + +local mem +local fmem +local gmem + + +local function updatelocale () + mm.locale(Predef) + Predef.a = Predef.alpha + Predef.c = Predef.cntrl + Predef.d = Predef.digit + Predef.g = Predef.graph + Predef.l = Predef.lower + Predef.p = Predef.punct + Predef.s = Predef.space + Predef.u = Predef.upper + Predef.w = Predef.alnum + Predef.x = Predef.xdigit + Predef.A = any - Predef.a + Predef.C = any - Predef.c + Predef.D = any - Predef.d + Predef.G = any - Predef.g + Predef.L = any - Predef.l + Predef.P = any - Predef.p + Predef.S = any - Predef.s + Predef.U = any - Predef.u + Predef.W = any - Predef.w + Predef.X = any - Predef.x + mem = {} -- restart memoization + fmem = {} + gmem = {} + local mt = {__mode = "v"} + setmetatable(mem, mt) + setmetatable(fmem, mt) + setmetatable(gmem, mt) +end + + +updatelocale() + + + +local I = m.P(function (s,i) print(i, s:sub(1, i-1)); return i end) + + +local function patt_error (s, i) + local msg = (#s < i + 20) and s:sub(i) + or s:sub(i,i+20) .. "..." + msg = ("pattern error near '%s'"):format(msg) + error(msg, 2) +end + +local function mult (p, n) + local np = mm.P(true) + while n >= 1 do + if n%2 >= 1 then np = np * p end + p = p * p + n = n/2 + end + return np +end + +local function equalcap (s, i, c) + if type(c) ~= "string" then return nil end + local e = #c + i + if s:sub(i, e - 1) == c then return e else return nil end +end + + +local S = (Predef.space + "--" * (any - Predef.nl)^0)^0 + +local name = m.R("AZ", "az", "__") * m.R("AZ", "az", "__", "09")^0 + +local arrow = S * "<-" + +local seq_follow = m.P"/" + ")" + "}" + ":}" + "~}" + "|}" + (name * arrow) + -1 + +name = m.C(name) + + +-- a defined name only have meaning in a given environment +local Def = name * m.Carg(1) + + +local function getdef (id, defs) + local c = defs and defs[id] + if not c then error("undefined name: " .. id) end + return c +end + +-- match a name and return a group of its corresponding definition +-- and 'f' (to be folded in 'Suffix') +local function defwithfunc (f) + return m.Cg(Def / getdef * m.Cc(f)) +end + + +local num = m.C(m.R"09"^1) * S / tonumber + +local String = "'" * m.C((any - "'")^0) * "'" + + '"' * m.C((any - '"')^0) * '"' + + +local defined = "%" * Def / function (c,Defs) + local cat = Defs and Defs[c] or Predef[c] + if not cat then error ("name '" .. c .. "' undefined") end + return cat +end + +local Range = m.Cs(any * (m.P"-"/"") * (any - "]")) / mm.R + +local item = (defined + Range + m.C(any)) / m.P + +local Class = + "[" + * (m.C(m.P"^"^-1)) -- optional complement symbol + * m.Cf(item * (item - "]")^0, mt.__add) / + function (c, p) return c == "^" and any - p or p end + * "]" + +local function adddef (t, k, exp) + if t[k] then + error("'"..k.."' already defined as a rule") + else + t[k] = exp + end + return t +end + +local function firstdef (n, r) return adddef({n}, n, r) end + + +local function NT (n, b) + if not b then + error("rule '"..n.."' used outside a grammar") + else return mm.V(n) + end +end + + +local exp = m.P{ "Exp", + Exp = S * ( m.V"Grammar" + + m.Cf(m.V"Seq" * ("/" * S * m.V"Seq")^0, mt.__add) ); + Seq = m.Cf(m.Cc(m.P"") * m.V"Prefix"^0 , mt.__mul) + * (#seq_follow + patt_error); + Prefix = "&" * S * m.V"Prefix" / mt.__len + + "!" * S * m.V"Prefix" / mt.__unm + + m.V"Suffix"; + Suffix = m.Cf(m.V"Primary" * S * + ( ( m.P"+" * m.Cc(1, mt.__pow) + + m.P"*" * m.Cc(0, mt.__pow) + + m.P"?" * m.Cc(-1, mt.__pow) + + "^" * ( m.Cg(num * m.Cc(mult)) + + m.Cg(m.C(m.S"+-" * m.R"09"^1) * m.Cc(mt.__pow)) + ) + + "->" * S * ( m.Cg((String + num) * m.Cc(mt.__div)) + + m.P"{}" * m.Cc(nil, m.Ct) + + defwithfunc(mt.__div) + ) + + "=>" * S * defwithfunc(m.Cmt) + + "~>" * S * defwithfunc(m.Cf) + ) * S + )^0, function (a,b,f) return f(a,b) end ); + Primary = "(" * m.V"Exp" * ")" + + String / mm.P + + Class + + defined + + "{:" * (name * ":" + m.Cc(nil)) * m.V"Exp" * ":}" / + function (n, p) return mm.Cg(p, n) end + + "=" * name / function (n) return mm.Cmt(mm.Cb(n), equalcap) end + + m.P"{}" / mm.Cp + + "{~" * m.V"Exp" * "~}" / mm.Cs + + "{|" * m.V"Exp" * "|}" / mm.Ct + + "{" * m.V"Exp" * "}" / mm.C + + m.P"." * m.Cc(any) + + (name * -arrow + "<" * name * ">") * m.Cb("G") / NT; + Definition = name * arrow * m.V"Exp"; + Grammar = m.Cg(m.Cc(true), "G") * + m.Cf(m.V"Definition" / firstdef * m.Cg(m.V"Definition")^0, + adddef) / mm.P +} + +local pattern = S * m.Cg(m.Cc(false), "G") * exp / mm.P * (-any + patt_error) + + +local function compile (p, defs) + if mm.type(p) == "pattern" then return p end -- already compiled + local cp = pattern:match(p, 1, defs) + if not cp then error("incorrect pattern", 3) end + return cp +end + +local function match (s, p, i) + local cp = mem[p] + if not cp then + cp = compile(p) + mem[p] = cp + end + return cp:match(s, i or 1) +end + +local function find (s, p, i) + local cp = fmem[p] + if not cp then + cp = compile(p) / 0 + cp = mm.P{ mm.Cp() * cp * mm.Cp() + 1 * mm.V(1) } + fmem[p] = cp + end + local i, e = cp:match(s, i or 1) + if i then return i, e - 1 + else return i + end +end + +local function gsub (s, p, rep) + local g = gmem[p] or {} -- ensure gmem[p] is not collected while here + gmem[p] = g + local cp = g[rep] + if not cp then + cp = compile(p) + cp = mm.Cs((cp / rep + 1)^0) + g[rep] = cp + end + return cp:match(s) +end + + +-- exported names +local re = { + compile = compile, + match = match, + find = find, + gsub = gsub, + updatelocale = updatelocale, +} + +if version == "Lua 5.1" then _G.re = re end + +return re diff --git a/luaclib/src/lpeg/test.lua b/luaclib/src/lpeg/test.lua new file mode 100755 index 00000000..8f9f5745 --- /dev/null +++ b/luaclib/src/lpeg/test.lua @@ -0,0 +1,1523 @@ +#!/usr/bin/env lua + +-- $Id: test.lua $ + +-- require"strict" -- just to be pedantic + +local m = require"lpeg" + + +-- for general use +local a, b, c, d, e, f, g, p, t + + +-- compatibility with Lua 5.2 +local unpack = rawget(table, "unpack") or unpack +local loadstring = rawget(_G, "loadstring") or load + + +local any = m.P(1) +local space = m.S" \t\n"^0 + +local function checkeq (x, y, p) +if p then print(x,y) end + if type(x) ~= "table" then assert(x == y) + else + for k,v in pairs(x) do checkeq(v, y[k], p) end + for k,v in pairs(y) do checkeq(v, x[k], p) end + end +end + + +local mt = getmetatable(m.P(1)) + + +local allchar = {} +for i=0,255 do allchar[i + 1] = i end +allchar = string.char(unpack(allchar)) +assert(#allchar == 256) + +local function cs2str (c) + return m.match(m.Cs((c + m.P(1)/"")^0), allchar) +end + +local function eqcharset (c1, c2) + assert(cs2str(c1) == cs2str(c2)) +end + + +print"General tests for LPeg library" + +assert(type(m.version()) == "string") +print("version " .. m.version()) +assert(m.type("alo") ~= "pattern") +assert(m.type(io.input) ~= "pattern") +assert(m.type(m.P"alo") == "pattern") + +-- tests for some basic optimizations +assert(m.match(m.P(false) + "a", "a") == 2) +assert(m.match(m.P(true) + "a", "a") == 1) +assert(m.match("a" + m.P(false), "b") == nil) +assert(m.match("a" + m.P(true), "b") == 1) + +assert(m.match(m.P(false) * "a", "a") == nil) +assert(m.match(m.P(true) * "a", "a") == 2) +assert(m.match("a" * m.P(false), "a") == nil) +assert(m.match("a" * m.P(true), "a") == 2) + +assert(m.match(#m.P(false) * "a", "a") == nil) +assert(m.match(#m.P(true) * "a", "a") == 2) +assert(m.match("a" * #m.P(false), "a") == nil) +assert(m.match("a" * #m.P(true), "a") == 2) + + +-- tests for locale +do + assert(m.locale(m) == m) + local t = {} + assert(m.locale(t, m) == t) + local x = m.locale() + for n,v in pairs(x) do + assert(type(n) == "string") + eqcharset(v, m[n]) + end +end + + +assert(m.match(3, "aaaa")) +assert(m.match(4, "aaaa")) +assert(not m.match(5, "aaaa")) +assert(m.match(-3, "aa")) +assert(not m.match(-3, "aaa")) +assert(not m.match(-3, "aaaa")) +assert(not m.match(-4, "aaaa")) +assert(m.P(-5):match"aaaa") + +assert(m.match("a", "alo") == 2) +assert(m.match("al", "alo") == 3) +assert(not m.match("alu", "alo")) +assert(m.match(true, "") == 1) + +local digit = m.S"0123456789" +local upper = m.S"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +local lower = m.S"abcdefghijklmnopqrstuvwxyz" +local letter = m.S"" + upper + lower +local alpha = letter + digit + m.R() + +eqcharset(m.S"", m.P(false)) +eqcharset(upper, m.R("AZ")) +eqcharset(lower, m.R("az")) +eqcharset(upper + lower, m.R("AZ", "az")) +eqcharset(upper + lower, m.R("AZ", "cz", "aa", "bb", "90")) +eqcharset(digit, m.S"01234567" + "8" + "9") +eqcharset(upper, letter - lower) +eqcharset(m.S(""), m.R()) +assert(cs2str(m.S("")) == "") + +eqcharset(m.S"\0", "\0") +eqcharset(m.S"\1\0\2", m.R"\0\2") +eqcharset(m.S"\1\0\2", m.R"\1\2" + "\0") +eqcharset(m.S"\1\0\2" - "\0", m.R"\1\2") + +local word = alpha^1 * (1 - alpha)^0 + +assert((word^0 * -1):match"alo alo") +assert(m.match(word^1 * -1, "alo alo")) +assert(m.match(word^2 * -1, "alo alo")) +assert(not m.match(word^3 * -1, "alo alo")) + +assert(not m.match(word^-1 * -1, "alo alo")) +assert(m.match(word^-2 * -1, "alo alo")) +assert(m.match(word^-3 * -1, "alo alo")) + +local eos = m.P(-1) + +assert(m.match(digit^0 * letter * digit * eos, "1298a1")) +assert(not m.match(digit^0 * letter * eos, "1257a1")) + +b = { + [1] = "(" * (((1 - m.S"()") + #m.P"(" * m.V(1))^0) * ")" +} + +assert(m.match(b, "(al())()")) +assert(not m.match(b * eos, "(al())()")) +assert(m.match(b * eos, "((al())()(é))")) +assert(not m.match(b, "(al()()")) + +assert(not m.match(letter^1 - "for", "foreach")) +assert(m.match(letter^1 - ("for" * eos), "foreach")) +assert(not m.match(letter^1 - ("for" * eos), "for")) + +function basiclookfor (p) + return m.P { + [1] = p + (1 * m.V(1)) + } +end + +function caplookfor (p) + return basiclookfor(p:C()) +end + +assert(m.match(caplookfor(letter^1), " 4achou123...") == "achou") +a = {m.match(caplookfor(letter^1)^0, " two words, one more ")} +checkeq(a, {"two", "words", "one", "more"}) + +assert(m.match( basiclookfor((#m.P(b) * 1) * m.Cp()), " ( (a)") == 7) + +a = {m.match(m.C(digit^1 * m.Cc"d") + m.C(letter^1 * m.Cc"l"), "123")} +checkeq(a, {"123", "d"}) + +-- bug in LPeg 0.12 (nil value does not create a 'ktable') +assert(m.match(m.Cc(nil), "") == nil) + +a = {m.match(m.C(digit^1 * m.Cc"d") + m.C(letter^1 * m.Cc"l"), "abcd")} +checkeq(a, {"abcd", "l"}) + +a = {m.match(m.Cc(10,20,30) * 'a' * m.Cp(), 'aaa')} +checkeq(a, {10,20,30,2}) +a = {m.match(m.Cp() * m.Cc(10,20,30) * 'a' * m.Cp(), 'aaa')} +checkeq(a, {1,10,20,30,2}) +a = m.match(m.Ct(m.Cp() * m.Cc(10,20,30) * 'a' * m.Cp()), 'aaa') +checkeq(a, {1,10,20,30,2}) +a = m.match(m.Ct(m.Cp() * m.Cc(7,8) * m.Cc(10,20,30) * 'a' * m.Cp()), 'aaa') +checkeq(a, {1,7,8,10,20,30,2}) +a = {m.match(m.Cc() * m.Cc() * m.Cc(1) * m.Cc(2,3,4) * m.Cc() * 'a', 'aaa')} +checkeq(a, {1,2,3,4}) + +a = {m.match(m.Cp() * letter^1 * m.Cp(), "abcd")} +checkeq(a, {1, 5}) + + +t = {m.match({[1] = m.C(m.C(1) * m.V(1) + -1)}, "abc")} +checkeq(t, {"abc", "a", "bc", "b", "c", "c", ""}) + +-- bug in 0.12 ('hascapture' did not check for captures inside a rule) +do + local pat = m.P{ + 'S'; + S1 = m.C('abc') + 3, + S = #m.V('S1') -- rule has capture, but '#' must ignore it + } + assert(pat:match'abc' == 1) +end + + +-- bug: loop in 'hascaptures' +do + local p = m.C(-m.P{m.P'x' * m.V(1) + m.P'y'}) + assert(p:match("xxx") == "") +end + + + +-- test for small capture boundary +for i = 250,260 do + assert(#m.match(m.C(i), string.rep('a', i)) == i) + assert(#m.match(m.C(m.C(i)), string.rep('a', i)) == i) +end + +-- tests for any*n and any*-n +for n = 1, 550, 13 do + local x_1 = string.rep('x', n - 1) + local x = x_1 .. 'a' + assert(not m.P(n):match(x_1)) + assert(m.P(n):match(x) == n + 1) + assert(n < 4 or m.match(m.P(n) + "xxx", x_1) == 4) + assert(m.C(n):match(x) == x) + assert(m.C(m.C(n)):match(x) == x) + assert(m.P(-n):match(x_1) == 1) + assert(not m.P(-n):match(x)) + assert(n < 13 or m.match(m.Cc(20) * ((n - 13) * m.P(10)) * 3, x) == 20) + local n3 = math.floor(n/3) + assert(m.match(n3 * m.Cp() * n3 * n3, x) == n3 + 1) +end + +-- true values +assert(m.P(0):match("x") == 1) +assert(m.P(0):match("") == 1) +assert(m.C(0):match("x") == "") + +assert(m.match(m.Cc(0) * m.P(10) + m.Cc(1) * "xuxu", "xuxu") == 1) +assert(m.match(m.Cc(0) * m.P(10) + m.Cc(1) * "xuxu", "xuxuxuxuxu") == 0) +assert(m.match(m.C(m.P(2)^1), "abcde") == "abcd") +p = m.Cc(0) * 1 + m.Cc(1) * 2 + m.Cc(2) * 3 + m.Cc(3) * 4 + + +-- test for alternation optimization +assert(m.match(m.P"a"^1 + "ab" + m.P"x"^0, "ab") == 2) +assert(m.match((m.P"a"^1 + "ab" + m.P"x"^0 * 1)^0, "ab") == 3) +assert(m.match(m.P"ab" + "cd" + "" + "cy" + "ak", "98") == 1) +assert(m.match(m.P"ab" + "cd" + "ax" + "cy", "ax") == 3) +assert(m.match("a" * m.P"b"^0 * "c" + "cd" + "ax" + "cy", "ax") == 3) +assert(m.match((m.P"ab" + "cd" + "ax" + "cy")^0, "ax") == 3) +assert(m.match(m.P(1) * "x" + m.S"" * "xu" + "ay", "ay") == 3) +assert(m.match(m.P"abc" + "cde" + "aka", "aka") == 4) +assert(m.match(m.S"abc" * "x" + "cde" + "aka", "ax") == 3) +assert(m.match(m.S"abc" * "x" + "cde" + "aka", "aka") == 4) +assert(m.match(m.S"abc" * "x" + "cde" + "aka", "cde") == 4) +assert(m.match(m.S"abc" * "x" + "ide" + m.S"ab" * "ka", "aka") == 4) +assert(m.match("ab" + m.S"abc" * m.P"y"^0 * "x" + "cde" + "aka", "ax") == 3) +assert(m.match("ab" + m.S"abc" * m.P"y"^0 * "x" + "cde" + "aka", "aka") == 4) +assert(m.match("ab" + m.S"abc" * m.P"y"^0 * "x" + "cde" + "aka", "cde") == 4) +assert(m.match("ab" + m.S"abc" * m.P"y"^0 * "x" + "ide" + m.S"ab" * "ka", "aka") == 4) +assert(m.match("ab" + m.S"abc" * m.P"y"^0 * "x" + "ide" + m.S"ab" * "ka", "ax") == 3) +assert(m.match(m.P(1) * "x" + "cde" + m.S"ab" * "ka", "aka") == 4) +assert(m.match(m.P(1) * "x" + "cde" + m.P(1) * "ka", "aka") == 4) +assert(m.match(m.P(1) * "x" + "cde" + m.P(1) * "ka", "cde") == 4) +assert(m.match(m.P"eb" + "cd" + m.P"e"^0 + "x", "ee") == 3) +assert(m.match(m.P"ab" + "cd" + m.P"e"^0 + "x", "abcd") == 3) +assert(m.match(m.P"ab" + "cd" + m.P"e"^0 + "x", "eeex") == 4) +assert(m.match(m.P"ab" + "cd" + m.P"e"^0 + "x", "cd") == 3) +assert(m.match(m.P"ab" + "cd" + m.P"e"^0 + "x", "x") == 1) +assert(m.match(m.P"ab" + "cd" + m.P"e"^0 + "x" + "", "zee") == 1) +assert(m.match(m.P"ab" + "cd" + m.P"e"^1 + "x", "abcd") == 3) +assert(m.match(m.P"ab" + "cd" + m.P"e"^1 + "x", "eeex") == 4) +assert(m.match(m.P"ab" + "cd" + m.P"e"^1 + "x", "cd") == 3) +assert(m.match(m.P"ab" + "cd" + m.P"e"^1 + "x", "x") == 2) +assert(m.match(m.P"ab" + "cd" + m.P"e"^1 + "x" + "", "zee") == 1) +assert(not m.match(("aa" * m.P"bc"^-1 + "aab") * "e", "aabe")) + +assert(m.match("alo" * (m.P"\n" + -1), "alo") == 4) + + +-- bug in 0.12 (rc1) +assert(m.match((m.P"\128\187\191" + m.S"abc")^0, "\128\187\191") == 4) + +assert(m.match(m.S"\0\128\255\127"^0, string.rep("\0\128\255\127", 10)) == + 4*10 + 1) + +-- optimizations with optional parts +assert(m.match(("ab" * -m.P"c")^-1, "abc") == 1) +assert(m.match(("ab" * #m.P"c")^-1, "abd") == 1) +assert(m.match(("ab" * m.B"c")^-1, "ab") == 1) +assert(m.match(("ab" * m.P"cd"^0)^-1, "abcdcdc") == 7) + +assert(m.match(m.P"ab"^-1 - "c", "abcd") == 3) + +p = ('Aa' * ('Bb' * ('Cc' * m.P'Dd'^0)^0)^0)^-1 +assert(p:match("AaBbCcDdBbCcDdDdDdBb") == 21) + + +-- bug in 0.12.2 +-- p = { ('ab' ('c' 'ef'?)*)? } +p = m.C(('ab' * ('c' * m.P'ef'^-1)^0)^-1) +s = "abcefccefc" +assert(s == p:match(s)) + + +pi = "3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510" +assert(m.match(m.Cs((m.P"1" / "a" + m.P"5" / "b" + m.P"9" / "c" + 1)^0), pi) == + m.match(m.Cs((m.P(1) / {["1"] = "a", ["5"] = "b", ["9"] = "c"})^0), pi)) +print"+" + + +-- tests for capture optimizations +assert(m.match((m.P(3) + 4 * m.Cp()) * "a", "abca") == 5) +t = {m.match(((m.P"a" + m.Cp()) * m.P"x")^0, "axxaxx")} +checkeq(t, {3, 6}) + + +-- tests for numbered captures +p = m.C(1) +assert(m.match(m.C(m.C(p * m.C(2)) * m.C(3)) / 3, "abcdefgh") == "a") +assert(m.match(m.C(m.C(p * m.C(2)) * m.C(3)) / 1, "abcdefgh") == "abcdef") +assert(m.match(m.C(m.C(p * m.C(2)) * m.C(3)) / 4, "abcdefgh") == "bc") +assert(m.match(m.C(m.C(p * m.C(2)) * m.C(3)) / 0, "abcdefgh") == 7) + +a, b, c = m.match(p * (m.C(p * m.C(2)) * m.C(3) / 4) * p, "abcdefgh") +assert(a == "a" and b == "efg" and c == "h") + +-- test for table captures +t = m.match(m.Ct(letter^1), "alo") +checkeq(t, {}) + +t, n = m.match(m.Ct(m.C(letter)^1) * m.Cc"t", "alo") +assert(n == "t" and table.concat(t) == "alo") + +t = m.match(m.Ct(m.C(m.C(letter)^1)), "alo") +assert(table.concat(t, ";") == "alo;a;l;o") + +t = m.match(m.Ct(m.C(m.C(letter)^1)), "alo") +assert(table.concat(t, ";") == "alo;a;l;o") + +t = m.match(m.Ct(m.Ct((m.Cp() * letter * m.Cp())^1)), "alo") +assert(table.concat(t[1], ";") == "1;2;2;3;3;4") + +t = m.match(m.Ct(m.C(m.C(1) * 1 * m.C(1))), "alo") +checkeq(t, {"alo", "a", "o"}) + + +-- tests for groups +p = m.Cg(1) -- no capture +assert(p:match('x') == 'x') +p = m.Cg(m.P(true)/function () end * 1) -- no value +assert(p:match('x') == 'x') +p = m.Cg(m.Cg(m.Cg(m.C(1)))) +assert(p:match('x') == 'x') +p = m.Cg(m.Cg(m.Cg(m.C(1))^0) * m.Cg(m.Cc(1) * m.Cc(2))) +t = {p:match'abc'} +checkeq(t, {'a', 'b', 'c', 1, 2}) + +p = m.Ct(m.Cg(m.Cc(10), "hi") * m.C(1)^0 * m.Cg(m.Cc(20), "ho")) +t = p:match'' +checkeq(t, {hi = 10, ho = 20}) +t = p:match'abc' +checkeq(t, {hi = 10, ho = 20, 'a', 'b', 'c'}) + +-- non-string group names +p = m.Ct(m.Cg(1, print) * m.Cg(1, 23.5) * m.Cg(1, io)) +t = p:match('abcdefghij') +assert(t[print] == 'a' and t[23.5] == 'b' and t[io] == 'c') + + +-- test for error messages +local function checkerr (msg, f, ...) + local st, err = pcall(f, ...) + assert(not st and m.match({ m.P(msg) + 1 * m.V(1) }, err)) +end + +checkerr("rule '1' may be left recursive", m.match, { m.V(1) * 'a' }, "a") +checkerr("rule '1' used outside a grammar", m.match, m.V(1), "") +checkerr("rule 'hiii' used outside a grammar", m.match, m.V('hiii'), "") +checkerr("rule 'hiii' undefined in given grammar", m.match, { m.V('hiii') }, "") +checkerr("undefined in given grammar", m.match, { m.V{} }, "") + +checkerr("rule 'A' is not a pattern", m.P, { m.P(1), A = {} }) +checkerr("grammar has no initial rule", m.P, { [print] = {} }) + +-- grammar with a long call chain before left recursion +p = {'a', + a = m.V'b' * m.V'c' * m.V'd' * m.V'a', + b = m.V'c', + c = m.V'd', + d = m.V'e', + e = m.V'f', + f = m.V'g', + g = m.P'' +} +checkerr("rule 'a' may be left recursive", m.match, p, "a") + +-- Bug in peephole optimization of LPeg 0.12 (IJmp -> ICommit) +-- the next grammar has an original sequence IJmp -> ICommit -> IJmp L1 +-- that is optimized to ICommit L1 + +p = m.P { (m.P {m.P'abc'} + 'ayz') * m.V'y'; y = m.P'x' } +assert(p:match('abcx') == 5 and p:match('ayzx') == 5 and not p:match'abc') + + +do + -- large dynamic Cc + local lim = 2^16 - 1 + local c = 0 + local function seq (n) + if n == 1 then c = c + 1; return m.Cc(c) + else + local m = math.floor(n / 2) + return seq(m) * seq(n - m) + end + end + p = m.Ct(seq(lim)) + t = p:match('') + assert(t[lim] == lim) + checkerr("too many", function () p = p / print end) + checkerr("too many", seq, lim + 1) +end + + +do + -- nesting of captures too deep + local p = m.C(1) + for i = 1, 300 do + p = m.Ct(p) + end + checkerr("too deep", p.match, p, "x") +end + + +-- tests for non-pattern as arguments to pattern functions + +p = { ('a' * m.V(1))^-1 } * m.P'b' * { 'a' * m.V(2); m.V(1)^-1 } +assert(m.match(p, "aaabaac") == 7) + +p = m.P'abc' * 2 * -5 * true * 'de' -- mix of numbers and strings and booleans + +assert(p:match("abc01de") == 8) +assert(p:match("abc01de3456") == nil) + +p = 'abc' * (2 * (-5 * (true * m.P'de'))) + +assert(p:match("abc01de") == 8) +assert(p:match("abc01de3456") == nil) + +p = { m.V(2), m.P"abc" } * + (m.P{ "xx", xx = m.P"xx" } + { "x", x = m.P"a" * m.V"x" + "" }) +assert(p:match("abcaaaxx") == 7) +assert(p:match("abcxx") == 6) + + +-- a large table capture +t = m.match(m.Ct(m.C('a')^0), string.rep("a", 10000)) +assert(#t == 10000 and t[1] == 'a' and t[#t] == 'a') + +print('+') + + +-- bug in 0.10 (rechecking a grammar, after tail-call optimization) +m.P{ m.P { (m.P(3) + "xuxu")^0 * m.V"xuxu", xuxu = m.P(1) } } + +local V = m.V + +local Space = m.S(" \n\t")^0 +local Number = m.C(m.R("09")^1) * Space +local FactorOp = m.C(m.S("+-")) * Space +local TermOp = m.C(m.S("*/")) * Space +local Open = "(" * Space +local Close = ")" * Space + + +local function f_factor (v1, op, v2, d) + assert(d == nil) + if op == "+" then return v1 + v2 + else return v1 - v2 + end +end + + +local function f_term (v1, op, v2, d) + assert(d == nil) + if op == "*" then return v1 * v2 + else return v1 / v2 + end +end + +G = m.P{ "Exp", + Exp = m.Cf(V"Factor" * m.Cg(FactorOp * V"Factor")^0, f_factor); + Factor = m.Cf(V"Term" * m.Cg(TermOp * V"Term")^0, f_term); + Term = Number / tonumber + Open * V"Exp" * Close; +} + +G = Space * G * -1 + +for _, s in ipairs{" 3 + 5*9 / (1+1) ", "3+4/2", "3+3-3- 9*2+3*9/1- 8"} do + assert(m.match(G, s) == loadstring("return "..s)()) +end + + +-- test for grammars (errors deep in calling non-terminals) +g = m.P{ + [1] = m.V(2) + "a", + [2] = "a" * m.V(3) * "x", + [3] = "b" * m.V(3) + "c" +} + +assert(m.match(g, "abbbcx") == 7) +assert(m.match(g, "abbbbx") == 2) + + +-- tests for \0 +assert(m.match(m.R("\0\1")^1, "\0\1\0") == 4) +assert(m.match(m.S("\0\1ab")^1, "\0\1\0a") == 5) +assert(m.match(m.P(1)^3, "\0\1\0a") == 5) +assert(not m.match(-4, "\0\1\0a")) +assert(m.match("\0\1\0a", "\0\1\0a") == 5) +assert(m.match("\0\0\0", "\0\0\0") == 4) +assert(not m.match("\0\0\0", "\0\0")) + + +-- tests for predicates +assert(not m.match(-m.P("a") * 2, "alo")) +assert(m.match(- -m.P("a") * 2, "alo") == 3) +assert(m.match(#m.P("a") * 2, "alo") == 3) +assert(m.match(##m.P("a") * 2, "alo") == 3) +assert(not m.match(##m.P("c") * 2, "alo")) +assert(m.match(m.Cs((##m.P("a") * 1 + m.P(1)/".")^0), "aloal") == "a..a.") +assert(m.match(m.Cs((#((#m.P"a")/"") * 1 + m.P(1)/".")^0), "aloal") == "a..a.") +assert(m.match(m.Cs((- -m.P("a") * 1 + m.P(1)/".")^0), "aloal") == "a..a.") +assert(m.match(m.Cs((-((-m.P"a")/"") * 1 + m.P(1)/".")^0), "aloal") == "a..a.") + + +-- fixed length +do + -- 'and' predicate using fixed length + local p = m.C(#("a" * (m.P("bd") + "cd")) * 2) + assert(p:match("acd") == "ac") + + p = #m.P{ "a" * m.V(2), m.P"b" } * 2 + assert(p:match("abc") == 3) + + p = #(m.P"abc" * m.B"c") + assert(p:match("abc") == 1 and not p:match("ab")) + + p = m.P{ "a" * m.V(2), m.P"b"^1 } + checkerr("pattern may not have fixed length", m.B, p) + + p = "abc" * (m.P"b"^1 + m.P"a"^0) + checkerr("pattern may not have fixed length", m.B, p) +end + + +p = -m.P'a' * m.Cc(1) + -m.P'b' * m.Cc(2) + -m.P'c' * m.Cc(3) +assert(p:match('a') == 2 and p:match('') == 1 and p:match('b') == 1) + +p = -m.P'a' * m.Cc(10) + #m.P'a' * m.Cc(20) +assert(p:match('a') == 20 and p:match('') == 10 and p:match('b') == 10) + + + +-- look-behind predicate +assert(not m.match(m.B'a', 'a')) +assert(m.match(1 * m.B'a', 'a') == 2) +assert(not m.match(m.B(1), 'a')) +assert(m.match(1 * m.B(1), 'a') == 2) +assert(m.match(-m.B(1), 'a') == 1) +assert(m.match(m.B(250), string.rep('a', 250)) == nil) +assert(m.match(250 * m.B(250), string.rep('a', 250)) == 251) + +-- look-behind with an open call +checkerr("pattern may not have fixed length", m.B, m.V'S1') +checkerr("too long to look behind", m.B, 260) + +B = #letter * -m.B(letter) + -letter * m.B(letter) +x = m.Ct({ (B * m.Cp())^-1 * (1 * m.V(1) + m.P(true)) }) +checkeq(m.match(x, 'ar cal c'), {1,3,4,7,9,10}) +checkeq(m.match(x, ' ar cal '), {2,4,5,8}) +checkeq(m.match(x, ' '), {}) +checkeq(m.match(x, 'aloalo'), {1,7}) + +assert(m.match(B, "a") == 1) +assert(m.match(1 * B, "a") == 2) +assert(not m.B(1 - letter):match("")) +assert((-m.B(letter)):match("") == 1) + +assert((4 * m.B(letter, 4)):match("aaaaaaaa") == 5) +assert(not (4 * m.B(#letter * 5)):match("aaaaaaaa")) +assert((4 * -m.B(#letter * 5)):match("aaaaaaaa") == 5) + +-- look-behind with grammars +assert(m.match('a' * m.B{'x', x = m.P(3)}, 'aaa') == nil) +assert(m.match('aa' * m.B{'x', x = m.P('aaa')}, 'aaaa') == nil) +assert(m.match('aaa' * m.B{'x', x = m.P('aaa')}, 'aaaaa') == 4) + + + +-- bug in 0.9 +assert(m.match(('a' * #m.P'b'), "ab") == 2) +assert(not m.match(('a' * #m.P'b'), "a")) + +assert(not m.match(#m.S'567', "")) +assert(m.match(#m.S'567' * 1, "6") == 2) + + +-- tests for Tail Calls + +p = m.P{ 'a' * m.V(1) + '' } +assert(p:match(string.rep('a', 1000)) == 1001) + +-- create a grammar for a simple DFA for even number of 0s and 1s +-- +-- ->1 <---0---> 2 +-- ^ ^ +-- | | +-- 1 1 +-- | | +-- V V +-- 3 <---0---> 4 +-- +-- this grammar should keep no backtracking information + +p = m.P{ + [1] = '0' * m.V(2) + '1' * m.V(3) + -1, + [2] = '0' * m.V(1) + '1' * m.V(4), + [3] = '0' * m.V(4) + '1' * m.V(1), + [4] = '0' * m.V(3) + '1' * m.V(2), +} + +assert(p:match(string.rep("00", 10000))) +assert(p:match(string.rep("01", 10000))) +assert(p:match(string.rep("011", 10000))) +assert(not p:match(string.rep("011", 10000) .. "1")) +assert(not p:match(string.rep("011", 10001))) + + +-- this grammar does need backtracking info. +local lim = 10000 +p = m.P{ '0' * m.V(1) + '0' } +checkerr("stack overflow", m.match, p, string.rep("0", lim)) +m.setmaxstack(2*lim) +checkerr("stack overflow", m.match, p, string.rep("0", lim)) +m.setmaxstack(2*lim + 4) +assert(m.match(p, string.rep("0", lim)) == lim + 1) + +-- this repetition should not need stack space (only the call does) +p = m.P{ ('a' * m.V(1))^0 * 'b' + 'c' } +m.setmaxstack(200) +assert(p:match(string.rep('a', 180) .. 'c' .. string.rep('b', 180)) == 362) + +m.setmaxstack(100) -- restore low limit + +-- tests for optional start position +assert(m.match("a", "abc", 1)) +assert(m.match("b", "abc", 2)) +assert(m.match("c", "abc", 3)) +assert(not m.match(1, "abc", 4)) +assert(m.match("a", "abc", -3)) +assert(m.match("b", "abc", -2)) +assert(m.match("c", "abc", -1)) +assert(m.match("abc", "abc", -4)) -- truncate to position 1 + +assert(m.match("", "abc", 10)) -- empty string is everywhere! +assert(m.match("", "", 10)) +assert(not m.match(1, "", 1)) +assert(not m.match(1, "", -1)) +assert(not m.match(1, "", 0)) + +print("+") + + +-- tests for argument captures +checkerr("invalid argument", m.Carg, 0) +checkerr("invalid argument", m.Carg, -1) +checkerr("invalid argument", m.Carg, 2^18) +checkerr("absent extra argument #1", m.match, m.Carg(1), 'a', 1) +assert(m.match(m.Carg(1), 'a', 1, print) == print) +x = {m.match(m.Carg(1) * m.Carg(2), '', 1, 10, 20)} +checkeq(x, {10, 20}) + +assert(m.match(m.Cmt(m.Cg(m.Carg(3), "a") * + m.Cmt(m.Cb("a"), function (s,i,x) + assert(s == "a" and i == 1); + return i, x+1 + end) * + m.Carg(2), function (s,i,a,b,c) + assert(s == "a" and i == 1 and c == nil); + return i, 2*a + 3*b + end) * "a", + "a", 1, false, 100, 1000) == 2*1001 + 3*100) + + +-- tests for Lua functions + +t = {} +s = "" +p = m.P(function (s1, i) assert(s == s1); t[#t + 1] = i; return nil end) * false +s = "hi, this is a test" +assert(m.match(((p - m.P(-1)) + 2)^0, s) == string.len(s) + 1) +assert(#t == string.len(s)/2 and t[1] == 1 and t[2] == 3) + +assert(not m.match(p, s)) + +p = mt.__add(function (s, i) return i end, function (s, i) return nil end) +assert(m.match(p, "alo")) + +p = mt.__mul(function (s, i) return i end, function (s, i) return nil end) +assert(not m.match(p, "alo")) + + +t = {} +p = function (s1, i) assert(s == s1); t[#t + 1] = i; return i end +s = "hi, this is a test" +assert(m.match((m.P(1) * p)^0, s) == string.len(s) + 1) +assert(#t == string.len(s) and t[1] == 2 and t[2] == 3) + +t = {} +p = m.P(function (s1, i) assert(s == s1); t[#t + 1] = i; + return i <= s1:len() and i end) * 1 +s = "hi, this is a test" +assert(m.match(p^0, s) == string.len(s) + 1) +assert(#t == string.len(s) + 1 and t[1] == 1 and t[2] == 2) + +p = function (s1, i) return m.match(m.P"a"^1, s1, i) end +assert(m.match(p, "aaaa") == 5) +assert(m.match(p, "abaa") == 2) +assert(not m.match(p, "baaa")) + +checkerr("invalid position", m.match, function () return 2^20 end, s) +checkerr("invalid position", m.match, function () return 0 end, s) +checkerr("invalid position", m.match, function (s, i) return i - 1 end, s) +checkerr("invalid position", m.match, + m.P(1)^0 * function (_, i) return i - 1 end, s) +assert(m.match(m.P(1)^0 * function (_, i) return i end * -1, s)) +checkerr("invalid position", m.match, + m.P(1)^0 * function (_, i) return i + 1 end, s) +assert(m.match(m.P(function (s, i) return s:len() + 1 end) * -1, s)) +checkerr("invalid position", m.match, m.P(function (s, i) return s:len() + 2 end) * -1, s) +assert(not m.match(m.P(function (s, i) return s:len() end) * -1, s)) +assert(m.match(m.P(1)^0 * function (_, i) return true end, s) == + string.len(s) + 1) +for i = 1, string.len(s) + 1 do + assert(m.match(function (_, _) return i end, s) == i) +end + +p = (m.P(function (s, i) return i%2 == 0 and i end) * 1 + + m.P(function (s, i) return i%2 ~= 0 and i + 2 <= s:len() and i end) * 3)^0 + * -1 +assert(p:match(string.rep('a', 14000))) + +-- tests for Function Replacements +f = function (a, ...) if a ~= "x" then return {a, ...} end end + +t = m.match(m.C(1)^0/f, "abc") +checkeq(t, {"a", "b", "c"}) + +t = m.match(m.C(1)^0/f/f, "abc") +checkeq(t, {{"a", "b", "c"}}) + +t = m.match(m.P(1)^0/f/f, "abc") -- no capture +checkeq(t, {{"abc"}}) + +t = m.match((m.P(1)^0/f * m.Cp())/f, "abc") +checkeq(t, {{"abc"}, 4}) + +t = m.match((m.C(1)^0/f * m.Cp())/f, "abc") +checkeq(t, {{"a", "b", "c"}, 4}) + +t = m.match((m.C(1)^0/f * m.Cp())/f, "xbc") +checkeq(t, {4}) + +t = m.match(m.C(m.C(1)^0)/f, "abc") +checkeq(t, {"abc", "a", "b", "c"}) + +g = function (...) return 1, ... end +t = {m.match(m.C(1)^0/g/g, "abc")} +checkeq(t, {1, 1, "a", "b", "c"}) + +t = {m.match(m.Cc(nil,nil,4) * m.Cc(nil,3) * m.Cc(nil, nil) / g / g, "")} +t1 = {1,1,nil,nil,4,nil,3,nil,nil} +for i=1,10 do assert(t[i] == t1[i]) end + +-- bug in 0.12.2: ktable with only nil could be eliminated when joining +-- with a pattern without ktable +assert((m.P"aaa" * m.Cc(nil)):match"aaa" == nil) + +t = {m.match((m.C(1) / function (x) return x, x.."x" end)^0, "abc")} +checkeq(t, {"a", "ax", "b", "bx", "c", "cx"}) + +t = m.match(m.Ct((m.C(1) / function (x,y) return y, x end * m.Cc(1))^0), "abc") +checkeq(t, {nil, "a", 1, nil, "b", 1, nil, "c", 1}) + +-- tests for Query Replacements + +assert(m.match(m.C(m.C(1)^0)/{abc = 10}, "abc") == 10) +assert(m.match(m.C(1)^0/{a = 10}, "abc") == 10) +assert(m.match(m.S("ba")^0/{ab = 40}, "abc") == 40) +t = m.match(m.Ct((m.S("ba")/{a = 40})^0), "abc") +checkeq(t, {40}) + +assert(m.match(m.Cs((m.C(1)/{a=".", d=".."})^0), "abcdde") == ".bc....e") +assert(m.match(m.Cs((m.C(1)/{f="."})^0), "abcdde") == "abcdde") +assert(m.match(m.Cs((m.C(1)/{d="."})^0), "abcdde") == "abc..e") +assert(m.match(m.Cs((m.C(1)/{e="."})^0), "abcdde") == "abcdd.") +assert(m.match(m.Cs((m.C(1)/{e=".", f="+"})^0), "eefef") == "..+.+") +assert(m.match(m.Cs((m.C(1))^0), "abcdde") == "abcdde") +assert(m.match(m.Cs(m.C(m.C(1)^0)), "abcdde") == "abcdde") +assert(m.match(1 * m.Cs(m.P(1)^0), "abcdde") == "bcdde") +assert(m.match(m.Cs((m.C('0')/'x' + 1)^0), "abcdde") == "abcdde") +assert(m.match(m.Cs((m.C('0')/'x' + 1)^0), "0ab0b0") == "xabxbx") +assert(m.match(m.Cs((m.C('0')/'x' + m.P(1)/{b=3})^0), "b0a0b") == "3xax3") +assert(m.match(m.P(1)/'%0%0'/{aa = -3} * 'x', 'ax') == -3) +assert(m.match(m.C(1)/'%0%1'/{aa = 'z'}/{z = -3} * 'x', 'ax') == -3) + +assert(m.match(m.Cs(m.Cc(0) * (m.P(1)/"")), "4321") == "0") + +assert(m.match(m.Cs((m.P(1) / "%0")^0), "abcd") == "abcd") +assert(m.match(m.Cs((m.P(1) / "%0.%0")^0), "abcd") == "a.ab.bc.cd.d") +assert(m.match(m.Cs((m.P("a") / "%0.%0" + 1)^0), "abcad") == "a.abca.ad") +assert(m.match(m.C("a") / "%1%%%0", "a") == "a%a") +assert(m.match(m.Cs((m.P(1) / ".xx")^0), "abcd") == ".xx.xx.xx.xx") +assert(m.match(m.Cp() * m.P(3) * m.Cp()/"%2%1%1 - %0 ", "abcde") == + "411 - abc ") + +assert(m.match(m.P(1)/"%0", "abc") == "a") +checkerr("invalid capture index", m.match, m.P(1)/"%1", "abc") +checkerr("invalid capture index", m.match, m.P(1)/"%9", "abc") + +p = m.C(1) +p = p * p; p = p * p; p = p * p * m.C(1) / "%9 - %1" +assert(p:match("1234567890") == "9 - 1") + +assert(m.match(m.Cc(print), "") == print) + +-- too many captures (just ignore extra ones) +p = m.C(1)^0 / "%2-%9-%0-%9" +assert(p:match"01234567890123456789" == "1-8-01234567890123456789-8") +s = string.rep("12345678901234567890", 20) +assert(m.match(m.C(1)^0 / "%9-%1-%0-%3", s) == "9-1-" .. s .. "-3") + +-- string captures with non-string subcaptures +p = m.Cc('alo') * m.C(1) / "%1 - %2 - %1" +assert(p:match'x' == 'alo - x - alo') + +checkerr("invalid capture value (a boolean)", m.match, m.Cc(true) / "%1", "a") + +-- long strings for string capture +l = 10000 +s = string.rep('a', l) .. string.rep('b', l) .. string.rep('c', l) + +p = (m.C(m.P'a'^1) * m.C(m.P'b'^1) * m.C(m.P'c'^1)) / '%3%2%1' + +assert(p:match(s) == string.rep('c', l) .. + string.rep('b', l) .. + string.rep('a', l)) + +print"+" + +-- accumulator capture +function f (x) return x + 1 end +assert(m.match(m.Cf(m.Cc(0) * m.C(1)^0, f), "alo alo") == 7) + +t = {m.match(m.Cf(m.Cc(1,2,3), error), "")} +checkeq(t, {1}) +p = m.Cf(m.Ct(true) * m.Cg(m.C(m.R"az"^1) * "=" * m.C(m.R"az"^1) * ";")^0, + rawset) +t = p:match("a=b;c=du;xux=yuy;") +checkeq(t, {a="b", c="du", xux="yuy"}) + + +-- errors in accumulator capture + +-- no initial capture +checkerr("no initial value", m.match, m.Cf(m.P(5), print), 'aaaaaa') +-- no initial capture (very long match forces fold to be a pair open-close) +checkerr("no initial value", m.match, m.Cf(m.P(500), print), + string.rep('a', 600)) + +-- nested capture produces no initial value +checkerr("no initial value", m.match, m.Cf(m.P(1) / {}, print), "alo") + + +-- tests for loop checker + +local function isnullable (p) + checkerr("may accept empty string", function (p) return p^0 end, m.P(p)) +end + +isnullable(m.P("x")^-4) +assert(m.match(((m.P(0) + 1) * m.S"al")^0, "alo") == 3) +assert(m.match((("x" + #m.P(1))^-4 * m.S"al")^0, "alo") == 3) +isnullable("") +isnullable(m.P("x")^0) +isnullable(m.P("x")^-1) +isnullable(m.P("x") + 1 + 2 + m.P("a")^-1) +isnullable(-m.P("ab")) +isnullable(- -m.P("ab")) +isnullable(# #(m.P("ab") + "xy")) +isnullable(- #m.P("ab")^0) +isnullable(# -m.P("ab")^1) +isnullable(#m.V(3)) +isnullable(m.V(3) + m.V(1) + m.P('a')^-1) +isnullable({[1] = m.V(2) * m.V(3), [2] = m.V(3), [3] = m.P(0)}) +assert(m.match(m.P{[1] = m.V(2) * m.V(3), [2] = m.V(3), [3] = m.P(1)}^0, "abc") + == 3) +assert(m.match(m.P""^-3, "a") == 1) + +local function find (p, s) + return m.match(basiclookfor(p), s) +end + + +local function badgrammar (g, expected) + local stat, msg = pcall(m.P, g) + assert(not stat) + if expected then assert(find(expected, msg)) end +end + +badgrammar({[1] = m.V(1)}, "rule '1'") +badgrammar({[1] = m.V(2)}, "rule '2'") -- invalid non-terminal +badgrammar({[1] = m.V"x"}, "rule 'x'") -- invalid non-terminal +badgrammar({[1] = m.V{}}, "rule '(a table)'") -- invalid non-terminal +badgrammar({[1] = #m.P("a") * m.V(1)}, "rule '1'") -- left-recursive +badgrammar({[1] = -m.P("a") * m.V(1)}, "rule '1'") -- left-recursive +badgrammar({[1] = -1 * m.V(1)}, "rule '1'") -- left-recursive +badgrammar({[1] = -1 + m.V(1)}, "rule '1'") -- left-recursive +badgrammar({[1] = 1 * m.V(2), [2] = m.V(2)}, "rule '2'") -- left-recursive +badgrammar({[1] = 1 * m.V(2)^0, [2] = m.P(0)}, "rule '1'") -- inf. loop +badgrammar({ m.V(2), m.V(3)^0, m.P"" }, "rule '2'") -- inf. loop +badgrammar({ m.V(2) * m.V(3)^0, m.V(3)^0, m.P"" }, "rule '1'") -- inf. loop +badgrammar({"x", x = #(m.V(1) * 'a') }, "rule '1'") -- inf. loop +badgrammar({ -(m.V(1) * 'a') }, "rule '1'") -- inf. loop +badgrammar({"x", x = m.P'a'^-1 * m.V"x"}, "rule 'x'") -- left recursive +badgrammar({"x", x = m.P'a' * m.V"y"^1, y = #m.P(1)}, "rule 'x'") + +assert(m.match({'a' * -m.V(1)}, "aaa") == 2) +assert(m.match({'a' * -m.V(1)}, "aaaa") == nil) + + +-- good x bad grammars +m.P{ ('a' * m.V(1))^-1 } +m.P{ -('a' * m.V(1)) } +m.P{ ('abc' * m.V(1))^-1 } +m.P{ -('abc' * m.V(1)) } +badgrammar{ #m.P('abc') * m.V(1) } +badgrammar{ -('a' + m.V(1)) } +m.P{ #('a' * m.V(1)) } +badgrammar{ #('a' + m.V(1)) } +m.P{ m.B{ m.P'abc' } * 'a' * m.V(1) } +badgrammar{ m.B{ m.P'abc' } * m.V(1) } +badgrammar{ ('a' + m.P'bcd')^-1 * m.V(1) } + + +-- simple tests for maximum sizes: +local p = m.P"a" +for i=1,14 do p = p * p end + +p = {} +for i=1,100 do p[i] = m.P"a" end +p = m.P(p) + + +-- strange values for rule labels + +p = m.P{ "print", + print = m.V(print), + [print] = m.V(_G), + [_G] = m.P"a", + } + +assert(p:match("a")) + +-- initial rule +g = {} +for i = 1, 10 do g["i"..i] = "a" * m.V("i"..i+1) end +g.i11 = m.P"" +for i = 1, 10 do + g[1] = "i"..i + local p = m.P(g) + assert(p:match("aaaaaaaaaaa") == 11 - i + 1) +end + +print"+" + + +-- tests for back references +checkerr("back reference 'x' not found", m.match, m.Cb('x'), '') +checkerr("back reference 'b' not found", m.match, m.Cg(1, 'a') * m.Cb('b'), 'a') + +p = m.Cg(m.C(1) * m.C(1), "k") * m.Ct(m.Cb("k")) +t = p:match("ab") +checkeq(t, {"a", "b"}) + +p = m.P(true) +for i = 1, 10 do p = p * m.Cg(1, i) end +for i = 1, 10 do + local p = p * m.Cb(i) + assert(p:match('abcdefghij') == string.sub('abcdefghij', i, i)) +end + + +t = {} +function foo (p) t[#t + 1] = p; return p .. "x" end + +p = m.Cg(m.C(2) / foo, "x") * m.Cb"x" * + m.Cg(m.Cb('x') / foo, "x") * m.Cb"x" * + m.Cg(m.Cb('x') / foo, "x") * m.Cb"x" * + m.Cg(m.Cb('x') / foo, "x") * m.Cb"x" +x = {p:match'ab'} +checkeq(x, {'abx', 'abxx', 'abxxx', 'abxxxx'}) +checkeq(t, {'ab', + 'ab', 'abx', + 'ab', 'abx', 'abxx', + 'ab', 'abx', 'abxx', 'abxxx'}) + + + +-- tests for match-time captures + +p = m.P'a' * (function (s, i) return (s:sub(i, i) == 'b') and i + 1 end) + + 'acd' + +assert(p:match('abc') == 3) +assert(p:match('acd') == 4) + +local function id (s, i, ...) + return true, ... +end + +assert(m.Cmt(m.Cs((m.Cmt(m.S'abc' / { a = 'x', c = 'y' }, id) + + m.R'09'^1 / string.char + + m.P(1))^0), id):match"acb98+68c" == "xyb\98+\68y") + +p = m.P{'S', + S = m.V'atom' * space + + m.Cmt(m.Ct("(" * space * (m.Cmt(m.V'S'^1, id) + m.P(true)) * ")" * space), id), + atom = m.Cmt(m.C(m.R("AZ", "az", "09")^1), id) +} +x = p:match"(a g () ((b) c) (d (e)))" +checkeq(x, {'a', 'g', {}, {{'b'}, 'c'}, {'d', {'e'}}}); + +x = {(m.Cmt(1, id)^0):match(string.rep('a', 500))} +assert(#x == 500) + +local function id(s, i, x) + if x == 'a' then return i, 1, 3, 7 + else return nil, 2, 4, 6, 8 + end +end + +p = ((m.P(id) * 1 + m.Cmt(2, id) * 1 + m.Cmt(1, id) * 1))^0 +assert(table.concat{p:match('abababab')} == string.rep('137', 4)) + +local function ref (s, i, x) + return m.match(x, s, i - x:len()) +end + +assert(m.Cmt(m.P(1)^0, ref):match('alo') == 4) +assert((m.P(1) * m.Cmt(m.P(1)^0, ref)):match('alo') == 4) +assert(not (m.P(1) * m.Cmt(m.C(1)^0, ref)):match('alo')) + +ref = function (s,i,x) return i == tonumber(x) and i, 'xuxu' end + +assert(m.Cmt(1, ref):match'2') +assert(not m.Cmt(1, ref):match'1') +assert(m.Cmt(m.P(1)^0, ref):match'03') + +function ref (s, i, a, b) + if a == b then return i, a:upper() end +end + +p = m.Cmt(m.C(m.R"az"^1) * "-" * m.C(m.R"az"^1), ref) +p = (any - p)^0 * p * any^0 * -1 + +assert(p:match'abbbc-bc ddaa' == 'BC') + +do -- match-time captures cannot be optimized away + local touch = 0 + f = m.P(function () touch = touch + 1; return true end) + + local function check(n) n = n or 1; assert(touch == n); touch = 0 end + + assert(m.match(f * false + 'b', 'a') == nil); check() + assert(m.match(f * false + 'b', '') == nil); check() + assert(m.match( (f * 'a')^0 * 'b', 'b') == 2); check() + assert(m.match( (f * 'a')^0 * 'b', '') == nil); check() + assert(m.match( (f * 'a')^-1 * 'b', 'b') == 2); check() + assert(m.match( (f * 'a')^-1 * 'b', '') == nil); check() + assert(m.match( ('b' + f * 'a')^-1 * 'b', '') == nil); check() + assert(m.match( (m.P'b'^-1 * f * 'a')^-1 * 'b', '') == nil); check() + assert(m.match( (-m.P(1) * m.P'b'^-1 * f * 'a')^-1 * 'b', '') == nil); + check() + assert(m.match( (f * 'a' + 'b')^-1 * 'b', '') == nil); check() + assert(m.match(f * 'a' + f * 'b', 'b') == 2); check(2) + assert(m.match(f * 'a' + f * 'b', 'a') == 2); check(1) + assert(m.match(-f * 'a' + 'b', 'b') == 2); check(1) + assert(m.match(-f * 'a' + 'b', '') == nil); check(1) +end + +c = '[' * m.Cg(m.P'='^0, "init") * '[' * + { m.Cmt(']' * m.C(m.P'='^0) * ']' * m.Cb("init"), function (_, _, s1, s2) + return s1 == s2 end) + + 1 * m.V(1) } / 0 + +assert(c:match'[==[]]====]]]]==]===[]' == 18) +assert(c:match'[[]=]====]=]]]==]===[]' == 14) +assert(not c:match'[[]=]====]=]=]==]===[]') + + +-- old bug: optimization of concat with fail removed match-time capture +p = m.Cmt(0, function (s) p = s end) * m.P(false) +assert(not p:match('alo')) +assert(p == 'alo') + + +-- ensure that failed match-time captures are not kept on Lua stack +do + local t = {__mode = "kv"}; setmetatable(t,t) + local c = 0 + + local function foo (s,i) + collectgarbage(); + assert(next(t) == "__mode" and next(t, "__mode") == nil) + local x = {} + t[x] = true + c = c + 1 + return i, x + end + + local p = m.P{ m.Cmt(0, foo) * m.P(false) + m.P(1) * m.V(1) + m.P"" } + p:match(string.rep('1', 10)) + assert(c == 11) +end + + +-- Return a match-time capture that returns 'n' captures +local function manyCmt (n) + return m.Cmt("a", function () + local a = {}; for i = 1, n do a[i] = n - i end + return true, unpack(a) + end) +end + +-- bug in 1.0: failed match-time that used previous match-time results +do + local x + local function aux (...) x = #{...}; return false end + local res = {m.match(m.Cmt(manyCmt(20), aux) + manyCmt(10), "a")} + assert(#res == 10 and res[1] == 9 and res[10] == 0) +end + + +-- bug in 1.0: problems with math-times returning too many captures +do + local lim = 2^11 - 10 + local res = {m.match(manyCmt(lim), "a")} + assert(#res == lim and res[1] == lim - 1 and res[lim] == 0) + checkerr("too many", m.match, manyCmt(2^15), "a") +end + +p = (m.P(function () return true, "a" end) * 'a' + + m.P(function (s, i) return i, "aa", 20 end) * 'b' + + m.P(function (s,i) if i <= #s then return i, "aaa" end end) * 1)^0 + +t = {p:match('abacc')} +checkeq(t, {'a', 'aa', 20, 'a', 'aaa', 'aaa'}) + + +------------------------------------------------------------------- +-- Tests for 're' module +------------------------------------------------------------------- + +local re = require "re" + +local match, compile = re.match, re.compile + + + +assert(match("a", ".") == 2) +assert(match("a", "''") == 1) +assert(match("", " ! . ") == 1) +assert(not match("a", " ! . ")) +assert(match("abcde", " ( . . ) * ") == 5) +assert(match("abbcde", " [a-c] +") == 5) +assert(match("0abbc1de", "'0' [a-c]+ '1'") == 7) +assert(match("0zz1dda", "'0' [^a-c]+ 'a'") == 8) +assert(match("abbc--", " [a-c] + +") == 5) +assert(match("abbc--", " [ac-] +") == 2) +assert(match("abbc--", " [-acb] + ") == 7) +assert(not match("abbcde", " [b-z] + ")) +assert(match("abb\"de", '"abb"["]"de"') == 7) +assert(match("abceeef", "'ac' ? 'ab' * 'c' { 'e' * } / 'abceeef' ") == "eee") +assert(match("abceeef", "'ac'? 'ab'* 'c' { 'f'+ } / 'abceeef' ") == 8) + +assert(re.match("aaand", "[a]^2") == 3) + +local t = {match("abceefe", "( ( & 'e' {} ) ? . ) * ")} +checkeq(t, {4, 5, 7}) +local t = {match("abceefe", "((&&'e' {})? .)*")} +checkeq(t, {4, 5, 7}) +local t = {match("abceefe", "( ( ! ! 'e' {} ) ? . ) *")} +checkeq(t, {4, 5, 7}) +local t = {match("abceefe", "(( & ! & ! 'e' {})? .)*")} +checkeq(t, {4, 5, 7}) + +assert(match("cccx" , "'ab'? ('ccc' / ('cde' / 'cd'*)? / 'ccc') 'x'+") == 5) +assert(match("cdx" , "'ab'? ('ccc' / ('cde' / 'cd'*)? / 'ccc') 'x'+") == 4) +assert(match("abcdcdx" , "'ab'? ('ccc' / ('cde' / 'cd'*)? / 'ccc') 'x'+") == 8) + +assert(match("abc", "a <- (. a)?") == 4) +b = "balanced <- '(' ([^()] / balanced)* ')'" +assert(match("(abc)", b)) +assert(match("(a(b)((c) (d)))", b)) +assert(not match("(a(b ((c) (d)))", b)) + +b = compile[[ balanced <- "(" ([^()] / balanced)* ")" ]] +assert(b == m.P(b)) +assert(b:match"((((a))(b)))") + +local g = [[ + S <- "0" B / "1" A / "" -- balanced strings + A <- "0" S / "1" A A -- one more 0 + B <- "1" S / "0" B B -- one more 1 +]] +assert(match("00011011", g) == 9) + +local g = [[ + S <- ("0" B / "1" A)* + A <- "0" / "1" A A + B <- "1" / "0" B B +]] +assert(match("00011011", g) == 9) +assert(match("000110110", g) == 9) +assert(match("011110110", g) == 3) +assert(match("000110010", g) == 1) + +s = "aaaaaaaaaaaaaaaaaaaaaaaa" +assert(match(s, "'a'^3") == 4) +assert(match(s, "'a'^0") == 1) +assert(match(s, "'a'^+3") == s:len() + 1) +assert(not match(s, "'a'^+30")) +assert(match(s, "'a'^-30") == s:len() + 1) +assert(match(s, "'a'^-5") == 6) +for i = 1, s:len() do + assert(match(s, string.format("'a'^+%d", i)) >= i + 1) + assert(match(s, string.format("'a'^-%d", i)) <= i + 1) + assert(match(s, string.format("'a'^%d", i)) == i + 1) +end +assert(match("01234567890123456789", "[0-9]^3+") == 19) + + +assert(match("01234567890123456789", "({....}{...}) -> '%2%1'") == "4560123") +t = match("0123456789", "{| {.}* |}") +checkeq(t, {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}) +assert(match("012345", "{| (..) -> '%0%0' |}")[1] == "0101") + +assert(match("abcdef", "( {.} {.} {.} {.} {.} ) -> 3") == "c") +assert(match("abcdef", "( {:x: . :} {.} {.} {.} {.} ) -> 3") == "d") +assert(match("abcdef", "( {:x: . :} {.} {.} {.} {.} ) -> 0") == 6) + +assert(not match("abcdef", "{:x: ({.} {.} {.}) -> 2 :} =x")) +assert(match("abcbef", "{:x: ({.} {.} {.}) -> 2 :} =x")) + +eqcharset(compile"[]]", "]") +eqcharset(compile"[][]", m.S"[]") +eqcharset(compile"[]-]", m.S"-]") +eqcharset(compile"[-]", m.S"-") +eqcharset(compile"[az-]", m.S"a-z") +eqcharset(compile"[-az]", m.S"a-z") +eqcharset(compile"[a-z]", m.R"az") +eqcharset(compile"[]['\"]", m.S[[]['"]]) + +eqcharset(compile"[^]]", any - "]") +eqcharset(compile"[^][]", any - m.S"[]") +eqcharset(compile"[^]-]", any - m.S"-]") +eqcharset(compile"[^]-]", any - m.S"-]") +eqcharset(compile"[^-]", any - m.S"-") +eqcharset(compile"[^az-]", any - m.S"a-z") +eqcharset(compile"[^-az]", any - m.S"a-z") +eqcharset(compile"[^a-z]", any - m.R"az") +eqcharset(compile"[^]['\"]", any - m.S[[]['"]]) + +-- tests for comments in 're' +e = compile[[ +A <- _B -- \t \n %nl .<> <- -> -- +_B <- 'x' --]] +assert(e:match'xy' == 2) + +-- tests for 're' with pre-definitions +defs = {digits = m.R"09", letters = m.R"az", _=m.P"__"} +e = compile("%letters (%letters / %digits)*", defs) +assert(e:match"x123" == 5) +e = compile("%_", defs) +assert(e:match"__" == 3) + +e = compile([[ + S <- A+ + A <- %letters+ B + B <- %digits+ +]], defs) + +e = compile("{[0-9]+'.'?[0-9]*} -> sin", math) +assert(e:match("2.34") == math.sin(2.34)) + + +function eq (_, _, a, b) return a == b end + +c = re.compile([[ + longstring <- '[' {:init: '='* :} '[' close + close <- ']' =init ']' / . close +]]) + +assert(c:match'[==[]]===]]]]==]===[]' == 17) +assert(c:match'[[]=]====]=]]]==]===[]' == 14) +assert(not c:match'[[]=]====]=]=]==]===[]') + +c = re.compile" '[' {:init: '='* :} '[' (!(']' =init ']') .)* ']' =init ']' !. " + +assert(c:match'[==[]]===]]]]==]') +assert(c:match'[[]=]====]=][]==]===[]]') +assert(not c:match'[[]=]====]=]=]==]===[]') + +assert(re.find("hi alalo", "{:x:..:} =x") == 4) +assert(re.find("hi alalo", "{:x:..:} =x", 4) == 4) +assert(not re.find("hi alalo", "{:x:..:} =x", 5)) +assert(re.find("hi alalo", "{'al'}", 5) == 6) +assert(re.find("hi aloalolo", "{:x:..:} =x") == 8) +assert(re.find("alo alohi x x", "{:word:%w+:}%W*(=word)!%w") == 11) + +-- re.find discards any captures +local a,b,c = re.find("alo", "{.}{'o'}") +assert(a == 2 and b == 3 and c == nil) + +local function match (s,p) + local i,e = re.find(s,p) + if i then return s:sub(i, e) end +end +assert(match("alo alo", '[a-z]+') == "alo") +assert(match("alo alo", '{:x: [a-z]+ :} =x') == nil) +assert(match("alo alo", "{:x: [a-z]+ :} ' ' =x") == "alo alo") + +assert(re.gsub("alo alo", "[abc]", "x") == "xlo xlo") +assert(re.gsub("alo alo", "%w+", ".") == ". .") +assert(re.gsub("hi, how are you", "[aeiou]", string.upper) == + "hI, hOw ArE yOU") + +s = 'hi [[a comment[=]=] ending here]] and [=[another]]=]]' +c = re.compile" '[' {:i: '='* :} '[' (!(']' =i ']') .)* ']' { =i } ']' " +assert(re.gsub(s, c, "%2") == 'hi and =]') +assert(re.gsub(s, c, "%0") == s) +assert(re.gsub('[=[hi]=]', c, "%2") == '=') + +assert(re.find("", "!.") == 1) +assert(re.find("alo", "!.") == 4) + +function addtag (s, i, t, tag) t.tag = tag; return i, t end + +c = re.compile([[ + doc <- block !. + block <- (start {| (block / { [^<]+ })* |} end?) => addtag + start <- '<' {:tag: [a-z]+ :} '>' + end <- '' +]], {addtag = addtag}) + +x = c:match[[ +hihellobuttotheend]] +checkeq(x, {tag='x', 'hi', {tag = 'b', 'hello'}, 'but', + {'totheend'}}) + + +-- test for folding captures +c = re.compile([[ + S <- (number (%s+ number)*) ~> add + number <- %d+ -> tonumber +]], {tonumber = tonumber, add = function (a,b) return a + b end}) +assert(c:match("3 401 50") == 3 + 401 + 50) + +-- tests for look-ahead captures +x = {re.match("alo", "&(&{.}) !{'b'} {&(...)} &{..} {...} {!.}")} +checkeq(x, {"", "alo", ""}) + +assert(re.match("aloalo", + "{~ (((&'al' {.}) -> 'A%1' / (&%l {.}) -> '%1%1') / .)* ~}") + == "AallooAalloo") + +-- bug in 0.9 (and older versions), due to captures in look-aheads +x = re.compile[[ {~ (&(. ([a-z]* -> '*')) ([a-z]+ -> '+') ' '*)* ~} ]] +assert(x:match"alo alo" == "+ +") + +-- valid capture in look-ahead (used inside the look-ahead itself) +x = re.compile[[ + S <- &({:two: .. :} . =two) {[a-z]+} / . S +]] +assert(x:match("hello aloaLo aloalo xuxu") == "aloalo") + + +p = re.compile[[ + block <- {| {:ident:space*:} line + ((=ident !space line) / &(=ident space) block)* |} + line <- {[^%nl]*} %nl + space <- '_' -- should be ' ', but '_' is simpler for editors +]] + +t= p:match[[ +1 +__1.1 +__1.2 +____1.2.1 +____ +2 +__2.1 +]] +checkeq(t, {"1", {"1.1", "1.2", {"1.2.1", "", ident = "____"}, ident = "__"}, + "2", {"2.1", ident = "__"}, ident = ""}) + + +-- nested grammars +p = re.compile[[ + s <- a b !. + b <- ( x <- ('b' x)? ) + a <- ( x <- 'a' x? ) +]] + +assert(p:match'aaabbb') +assert(p:match'aaa') +assert(not p:match'bbb') +assert(not p:match'aaabbba') + +-- testing groups +t = {re.match("abc", "{:S <- {:.:} {S} / '':}")} +checkeq(t, {"a", "bc", "b", "c", "c", ""}) + +t = re.match("1234", "{| {:a:.:} {:b:.:} {:c:.{.}:} |}") +checkeq(t, {a="1", b="2", c="4"}) +t = re.match("1234", "{|{:a:.:} {:b:{.}{.}:} {:c:{.}:}|}") +checkeq(t, {a="1", b="2", c="4"}) +t = re.match("12345", "{| {:.:} {:b:{.}{.}:} {:{.}{.}:} |}") +checkeq(t, {"1", b="2", "4", "5"}) +t = re.match("12345", "{| {:.:} {:{:b:{.}{.}:}:} {:{.}{.}:} |}") +checkeq(t, {"1", "23", "4", "5"}) +t = re.match("12345", "{| {:.:} {{:b:{.}{.}:}} {:{.}{.}:} |}") +checkeq(t, {"1", "23", "4", "5"}) + + +-- testing pre-defined names +assert(os.setlocale("C") == "C") + +function eqlpeggsub (p1, p2) + local s1 = cs2str(re.compile(p1)) + local s2 = string.gsub(allchar, "[^" .. p2 .. "]", "") + -- if s1 ~= s2 then print(#s1,#s2) end + assert(s1 == s2) +end + + +eqlpeggsub("%w", "%w") +eqlpeggsub("%a", "%a") +eqlpeggsub("%l", "%l") +eqlpeggsub("%u", "%u") +eqlpeggsub("%p", "%p") +eqlpeggsub("%d", "%d") +eqlpeggsub("%x", "%x") +eqlpeggsub("%s", "%s") +eqlpeggsub("%c", "%c") + +eqlpeggsub("%W", "%W") +eqlpeggsub("%A", "%A") +eqlpeggsub("%L", "%L") +eqlpeggsub("%U", "%U") +eqlpeggsub("%P", "%P") +eqlpeggsub("%D", "%D") +eqlpeggsub("%X", "%X") +eqlpeggsub("%S", "%S") +eqlpeggsub("%C", "%C") + +eqlpeggsub("[%w]", "%w") +eqlpeggsub("[_%w]", "_%w") +eqlpeggsub("[^%w]", "%W") +eqlpeggsub("[%W%S]", "%W%S") + +re.updatelocale() + + +-- testing nested substitutions x string captures + +p = re.compile[[ + text <- {~ item* ~} + item <- macro / [^()] / '(' item* ')' + arg <- ' '* {~ (!',' item)* ~} + args <- '(' arg (',' arg)* ')' + macro <- ('apply' args) -> '%1(%2)' + / ('add' args) -> '%1 + %2' + / ('mul' args) -> '%1 * %2' +]] + +assert(p:match"add(mul(a,b), apply(f,x))" == "a * b + f(x)") + +rev = re.compile[[ R <- (!.) -> '' / ({.} R) -> '%2%1']] + +assert(rev:match"0123456789" == "9876543210") + + +-- testing error messages in re + +local function errmsg (p, err) + checkerr(err, re.compile, p) +end + +errmsg('aaaa', "rule 'aaaa'") +errmsg('a', 'outside') +errmsg('b <- a', 'undefined') +errmsg("x <- 'a' x <- 'b'", 'already defined') +errmsg("'a' -", "near '-'") + + +print"OK" + + From c4b8d320dd7e55ac5a2a24a1ed7337993f870637 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 25 Apr 2019 06:28:28 +0800 Subject: [PATCH 007/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0lpeg,=20=E5=BE=AE?= =?UTF-8?q?=E8=B0=83makefile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lpeg/makefile | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/luaclib/src/lpeg/makefile b/luaclib/src/lpeg/makefile index 8a5d4061..028b30d2 100644 --- a/luaclib/src/lpeg/makefile +++ b/luaclib/src/lpeg/makefile @@ -1,3 +1,11 @@ +.PHONY : build rebuild clean + +default : + @echo "=======================================" + @echo "Please use 'make build' command to build it.." + @echo "Please use 'make clean' command to clean all." + @echo "=======================================" + LIBNAME = lpeg LUADIR = ../lua/ @@ -38,13 +46,13 @@ macosx: build: $(FILES) CC -o lpeg.so $(FILES) -shared -fPIC -lcore mv *.so ../../ - rm *.o *.so + rm -rf *.o *.so rebuild: $(FILES) CC -o lpeg.so $(FILES) -shared -fPIC -lcore mv *.so ../../ - rm *.o *.so + rm -rf *.o *.so lpeg.so: $(FILES) From b79943cc9aaa8f4b03eba8d35fb3686cf8f0678f Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 25 Apr 2019 08:48:47 +0800 Subject: [PATCH 008/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8DCC=E7=9A=84=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E5=91=BD=E4=BB=A4=E5=85=BC=E5=AE=B9=E9=97=AE=E9=A2=98?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lpeg/makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/luaclib/src/lpeg/makefile b/luaclib/src/lpeg/makefile index 028b30d2..adc2b3b6 100644 --- a/luaclib/src/lpeg/makefile +++ b/luaclib/src/lpeg/makefile @@ -44,13 +44,13 @@ macosx: $(MAKE) lpeg.so "DLLFLAGS = -bundle -undefined dynamic_lookup" build: $(FILES) - CC -o lpeg.so $(FILES) -shared -fPIC -lcore + $(CC) -o lpeg.so $(FILES) -shared -fPIC -lcore mv *.so ../../ rm -rf *.o *.so rebuild: $(FILES) - CC -o lpeg.so $(FILES) -shared -fPIC -lcore + $(CC) -o lpeg.so $(FILES) -shared -fPIC -lcore mv *.so ../../ rm -rf *.o *.so From 57c17c2b7640ab3466baa8b5d983cecdac352c3b Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 26 Apr 2019 20:01:47 +0800 Subject: [PATCH 009/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0Nginx/Traefik?= =?UTF-8?q?=E5=81=9A=E4=B8=BA=E8=B4=9F=E8=BD=BD=E5=9D=87=E8=A1=A1=E7=9A=84?= =?UTF-8?q?=E7=A4=BA=E4=BE=8B=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose-with-nginx.yaml | 37 +++++++++++++++++++++++++ docker/docker-compose-with-traefik.yaml | 30 ++++++++++++++++++++ docker/docker-compose.yaml | 22 --------------- docker/nginx.conf | 11 ++++++-- docker/script/main.lua | 16 +++++++++++ 5 files changed, 92 insertions(+), 24 deletions(-) create mode 100644 docker/docker-compose-with-nginx.yaml create mode 100644 docker/docker-compose-with-traefik.yaml delete mode 100644 docker/docker-compose.yaml create mode 100644 docker/script/main.lua diff --git a/docker/docker-compose-with-nginx.yaml b/docker/docker-compose-with-nginx.yaml new file mode 100644 index 00000000..5e3d5798 --- /dev/null +++ b/docker/docker-compose-with-nginx.yaml @@ -0,0 +1,37 @@ +# docker-compose.yaml +version: "2" +services: + WebProxy: + image: nginx:latest + ports: + - 80:80 + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf + networks: + - local + links: + - WebApp1:webapp1 + - WebApp2:webapp2 + - WebApp3:webapp3 + WebApp1: + image: candymi/cfweb:latest + volumes: + - ./script:/app/script + networks: + - local + WebApp2: + image: candymi/cfweb:latest + volumes: + - ./script:/app/script + networks: + - local + WebApp3: + image: candymi/cfweb:latest + volumes: + - ./script:/app/script + networks: + - local + +networks: + local: + driver: bridge diff --git a/docker/docker-compose-with-traefik.yaml b/docker/docker-compose-with-traefik.yaml new file mode 100644 index 00000000..81084bad --- /dev/null +++ b/docker/docker-compose-with-traefik.yaml @@ -0,0 +1,30 @@ +# docker-compose.yaml +version: "2" +services: + WebProxy: + image: traefik + command: --api --docker # Enables the web UI and tells Traefik to listen to docker + ports: + - "80:80" # The HTTP port + - "8080:8080" # The Web UI (enabled by --api) + volumes: + - /var/run/docker.sock:/var/run/docker.sock # So that Traefik can listen to the Docker events + networks: + - local + + WebApp: + image: candymi/cfweb:latest + labels: + - "traefik.port=8080" + - "traefik.backend=WebApp" + - "traefik.enable=true" + - "traefik.domain=localhost" + - "traefik.frontend.rule=Host:localhost" + volumes: + - ./script:/app/script + networks: + - local + +networks: + local: + driver: bridge diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml deleted file mode 100644 index a45d3817..00000000 --- a/docker/docker-compose.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# docker-compose.yaml -version: "2" -services: - WebProxy: - container_name: WebProxy - image: nginx:latest - ports: - - 80:80 - volumes: - - ./nginx.conf:/etc/nginx/nginx.conf - networks: - - local - links: - - WebApp:webapp - WebApp: - container_name: WebApp - image: candymi/cfweb:latest - networks: - - local -networks: - local: - driver: bridge diff --git a/docker/nginx.conf b/docker/nginx.conf index 220eb955..469b57cb 100644 --- a/docker/nginx.conf +++ b/docker/nginx.conf @@ -34,19 +34,26 @@ http { gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png; gzip_disable "MSIE [1-6]\."; + + upstream myweb { + server webapp1:8080; + server webapp2:8080; + server webapp3:8080; + } + server { listen 80; #access_log /var/log/nginx/8080.log main; location /ws { - proxy_pass http://webapp:8080/ws; + proxy_pass http://myweb/ws; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } location / { - proxy_pass http://webapp:8080; + proxy_pass http://myweb; proxy_http_version 1.1; proxy_ignore_client_abort on; proxy_set_header Host $http_host; diff --git a/docker/script/main.lua b/docker/script/main.lua new file mode 100644 index 00000000..146d2a66 --- /dev/null +++ b/docker/script/main.lua @@ -0,0 +1,16 @@ +local httpd = require "httpd" +local http = require "httpd.http" + +local app = httpd:new("httpd") + +app:before(function (content) + return http.ok() +end) + +app:api('/api/login', function (content) + return '{"code":200, "data":{"token":"admin","uid":1}}' +end) + +app:listen("0.0.0.0", 8080) + +app:run() From ac29e8c39a8d6e87b2bd773bcf006f649912cd67 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 27 Apr 2019 01:16:21 +0800 Subject: [PATCH 010/956] =?UTF-8?q?=E8=B0=83=E6=95=B4class=E5=88=A4?= =?UTF-8?q?=E6=96=AD=E3=80=81=E5=88=A0=E9=99=A4Route=E7=9A=84=E4=B8=80?= =?UTF-8?q?=E4=BA=9B=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/class/init.lua | 15 ++++++++------- lualib/httpd/Router.lua | 13 +------------ 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/lualib/class/init.lua b/lualib/class/init.lua index f762fe41..8b882bae 100644 --- a/lualib/class/init.lua +++ b/lualib/class/init.lua @@ -1,11 +1,11 @@ --- a minimal class implementation +local type = type +local setmetatable = setmetatable -- 一个精简版的类实现 function class(cls_name) - local cls = { } - cls.__name = cls_name + local cls = { __name = cls_name } cls.__index = cls cls.__call = function (cls, ...) - local call = cls[cls.__name] + local call = cls[cls_name] if call then return call(cls, ...) end @@ -16,14 +16,15 @@ function class(cls_name) return print("Please use ':' to create new object :)") end local t = {} - if not c.ctor then + local ctor = c.ctor + if type(ctor) ~= 'function' then print("Can't find ctor to init.") else - c.ctor(t, ...) + ctor(t, ...) end return setmetatable(t, cls) end return cls end -return class \ No newline at end of file +return class diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index 39998aa5..b4813142 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -59,17 +59,6 @@ local function find_route(path) if #tab == 0 then -- 如果路由为/[/]{0, n}, 则转义为: '' tab[1] = '' end - -- for index, r in ipairs(tab) do - -- local route = routes[index][r] - -- if type(route) == 'table' then - -- if #tab == index then - -- return route.class, route.type - -- end - -- if route.type == Router.STATIC then - -- return route.class, route.type - -- end - -- end - -- end for index, route in ipairs(routes) do local r = tab[index] if not r then @@ -106,4 +95,4 @@ function Router.registery(route, class, route_type) return registery_router(route, class, route_type) end -return Router \ No newline at end of file +return Router From c05569c144d3ff3bcab176ca2cf1bd802eee04b9 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 27 Apr 2019 12:12:37 +0800 Subject: [PATCH 011/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0logging=E6=A8=A1?= =?UTF-8?q?=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/logging/init.lua | 151 ++++++++++++++++++++++++++++++++++++++++ script/test_logging.lua | 10 +++ 2 files changed, 161 insertions(+) create mode 100644 lualib/logging/init.lua create mode 100644 script/test_logging.lua diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua new file mode 100644 index 00000000..021c421a --- /dev/null +++ b/lualib/logging/init.lua @@ -0,0 +1,151 @@ +-- logging 核心配置 + +local class = require "class" +local system = require "system" +local now = system.now +local type = type +local print = print +local assert = assert +local pairs = pairs +local tostring = tostring +local getmetatable = getmetatable + +local modf = math.modf +local debug_getinfo = debug.getinfo +local os_date = os.date +local io_open = io.open +local format = string.format +local concat = table.concat +local modf = math.modf + + +-- 格式化时间: [年-月-日 时:分:秒,毫秒] +local function fmt_Y_m_d_H_M_S() + local ts, f = modf(now()) + f = format("%3.0f", f * 1e3) + return concat({'[', os_date('%Y-%m-%d %H:%M:%S'), ',', f, ']'}) +end + +-- 格式化时间: [年-月-日 时:分:秒] +local function Y_m_d() + return os_date('%Y-%m-%d') +end + +-- LOG函数的调用信息 +local function debuginfo () + local info = debug_getinfo(3, 'Sln') + return concat({'[', info.source, ':', info.currentline, ']'}) +end + +-- 格式化 +local function table_format(t) + local tab = {} + while 1 do + local mt = getmetatable(t) + for key, value in pairs(t) do + if type(key) == 'number' then + if type(value) == 'table' then + if t ~= value then + tab[#tab+1] = concat({'[', key, ']', '=', '{', concat(t, ", "), '}'}) + end + elseif type(value) == 'string' then + tab[#tab+1] = concat({'[', key, ']', '="', tostring(value), '"'}) + else + tab[#tab+1] = concat({'[', key, ']', '=', tostring(value)}) + end + else + if type(value) == 'table' then + if t ~= value then + tab[#tab+1] = concat({'["', key, '"]', '=', '{', concat(t, ", "), '}'}) + end + elseif type(value) == 'string' then + tab[#tab+1] = concat({'["', key, '"]', '="', tostring(value), '"'}) + else + tab[#tab+1] = concat({'["', key, '"]', '=', tostring(value)}) + end + end + end + if not mt then + break + end + t = mt + end + return concat({'{', concat(tab, ', '), '}'}) +end + +local function fmt(...) + local args = {...} + local index, len = 1, select('#', ...) + local tab = {} + while 1 do + local arg = args[index] + if type(arg) == 'table' then + tab[#tab+1] = table_format(arg) + else + tab[#tab+1]= tostring(arg) + end + if index == len then + break + end + index = index + 1 + end + return concat(tab, ', ') +end + +-- 格式化日志 +function FMT (where, level, ...) + return concat({ fmt_Y_m_d_H_M_S(), where, level, ':', fmt(...), '\n'}, ' ') +end + +local paths = {} + +local Log = class("Log") + +function Log:ctor (opt) + self.path = opt.path + self.now = Y_m_d() +end + +-- 常规日志 +function Log:INFO (...) + print(FMT("\27[32m"..debuginfo(), "[INFO]".."\27[0m", ...)) + self:dump(FMT(debuginfo(), "[INFO]", ...)) +end + +-- 错误日志 +function Log:ERROR (...) + print(FMT("\27[31m"..debuginfo(), "[ERROR]".."\27[0m", ...)) + self:dump(FMT(debuginfo(), "[ERROR]", ...)) +end + +-- 调试日志 +function Log:DEBUG (...) + print(FMT("\27[36m"..debuginfo(), "[DEBUG]".."\27[0m", ...)) + self:dump(FMT(debuginfo(), "[DEBUG]", ...)) +end + +-- 警告日志 +function Log:WARN (...) + print(FMT("\27[33m"..debuginfo(), "[WARN]".."\27[0m", ...)) + self:dump(FMT(debuginfo(), "[WARN]", ...)) +end + +function Log:dump(log) + local file = paths[self.path] + if type(self.path) == 'string' and self.path ~= '' then + if not file then + file = io_open(self.path..'_'..self.now..'.log', 'a') + paths[self.path] = file + else + if Y_m_d() ~= self.now then + file:close() + self.now = Y_m_d() + file = io_open(self.path..'_'..self.now..'.log', 'a') + paths[self.path] = file + end + end + file:write(log) + end +end + +return Log diff --git a/script/test_logging.lua b/script/test_logging.lua new file mode 100644 index 00000000..22c22eb4 --- /dev/null +++ b/script/test_logging.lua @@ -0,0 +1,10 @@ +local LOG = require "logging" + +-- 初始化日志 +local log = LOG:new { path = './admin' } + +-- 打印 +log:INFO('this is INFO LOG', nil, 1, nil) +log:DEBUG('this is DEBUG LOG', nil, nil, 1) +log:WARN('this is WARN LOG', 1, nil, nil) +log:ERROR('this is ERROR LOG', nil, nil, nil, log) From a5fcea3935246195f67ef2647a8e369475c63021 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 27 Apr 2019 20:56:19 +0800 Subject: [PATCH 012/956] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E4=BE=9D=E8=B5=96log?= =?UTF-8?q?,=20=E4=BD=BF=E7=94=A8cf=E8=87=AA=E5=B7=B1=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E7=9A=84logging=E8=AE=B0=E5=BD=95=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/Cache/init.lua | 8 +- lualib/DB/init.lua | 13 +-- lualib/MQ/init.lua | 8 +- lualib/httpd/Router.lua | 10 +- lualib/httpd/init.lua | 18 +--- lualib/internal/Co.lua | 8 +- lualib/internal/TCP.lua | 17 ++-- lualib/internal/Timer.lua | 11 ++- lualib/internal/UDP.lua | 3 +- lualib/log/init.lua | 142 --------------------------- lualib/logging/init.lua | 8 +- lualib/protocol/dns.lua | 4 +- lualib/protocol/http.lua | 16 +-- lualib/protocol/mqtt/init.lua | 10 +- lualib/protocol/mysql.lua | 1 - lualib/protocol/redis.lua | 10 +- lualib/protocol/websocket/server.lua | 18 ++-- script/main.lua | 2 +- 18 files changed, 87 insertions(+), 220 deletions(-) delete mode 100644 lualib/log/init.lua diff --git a/lualib/Cache/init.lua b/lualib/Cache/init.lua index a1194f51..337ad994 100644 --- a/lualib/Cache/init.lua +++ b/lualib/Cache/init.lua @@ -1,4 +1,4 @@ -local log = require "log" +local log = require "logging" local Co = require "internal.Co" local timer = require "internal.Timer" local redis = require "protocol.redis" @@ -18,6 +18,8 @@ local upper = string.upper local lower = string.lower local splite = string.gmatch +local Log = log:new() + -- 默认情况下, 保持50个redis连接 local MAX, COUNT = 50, 0 @@ -155,7 +157,7 @@ function Cache.init(opt) if ok then break end - log.error('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接") + Log:ERROR('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接") times = times + 1 rds:close() timer.sleep(3) @@ -177,4 +179,4 @@ function Cache.count() return #POOL end -return Cache \ No newline at end of file +return Cache diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index e5a5184f..74680e16 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -1,7 +1,8 @@ local mysql = require "protocol.mysql" local timer = require "internal.Timer" local co = require "internal.Co" -local log = require "log" +local log = require "logging" +local Log = log:new() local co_self = co.self local co_wait = co.wait @@ -170,7 +171,7 @@ local function execute(query) if db.state then break end - log.error(err) + Log:ERROR(err) db:close() db, ret, err = nil end @@ -380,7 +381,7 @@ function DB.init(opt) if connect then break end - log.error('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接") + Log:ERROR('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接") db:close() times = times + 1 timer.sleep(3) @@ -414,7 +415,7 @@ function DB.select(fields) limit = limit, execute = execute, } - if tpy == "string" then + if tpy == "string" then insert(query, fields) end if tpy == "table" then @@ -491,7 +492,7 @@ function DB.query(query) if db.state then break end - log.error(err) + Log:ERROR(err) db:close() db, ret, err = nil end @@ -508,4 +509,4 @@ function DB.count( ... ) return #POOL end -return DB \ No newline at end of file +return DB diff --git a/lualib/MQ/init.lua b/lualib/MQ/init.lua index 29b02942..9e426776 100644 --- a/lualib/MQ/init.lua +++ b/lualib/MQ/init.lua @@ -1,9 +1,11 @@ -local log = require "log" +local log = require "logging" local class = require "class" local Timer = require "internal.Timer" local mqtt = require "protocol.mqtt" local redis = require "protocol.redis" +local Log = log:new() + local type = type local math = math local random = math.random @@ -34,7 +36,7 @@ local function mq_login(self) return rds end rds:close() - log.error('连接mq(redis)失败:'..(err or "unknow")..'.正在尝试重连') + Log:ERROR('连接mq(redis)失败:'..(err or "unknow")..'.正在尝试重连') Timer.sleep(3) times = times + 1 elseif self.type == 'mqtt' then @@ -52,7 +54,7 @@ local function mq_login(self) return mqtt end mqtt:close() - log.error('连接mq(mqtt)失败:'..(err or "unknow")..'.正在尝试重连') + Log:ERROR('连接mq(mqtt)失败:'..(err or "unknow")..'.正在尝试重连') Timer.sleep(3) times = times + 1 else diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index b4813142..1de99f2f 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -1,4 +1,6 @@ -local log = require "log" +local log = require "logging" +local Log = log:new() + local math = math local string = string local find = string.find @@ -84,13 +86,13 @@ end -- 注册路由 function Router.registery(route, class, route_type) if type(route) ~= 'string' or route == '' then -- 过滤错误的路由输入 - return log.warn('Please Do not add empty string in route registery method :)') + return Log:WARN('Please Do not add empty string in route registery method :)') end if find(route, '//') then -- 不允许出现路由出现[//] - return log.warn('Please Do not add [//] in route registery method :)') + return Log:WARN('Please Do not add [//] in route registery method :)') end if find(route, '^/%[%w+:.+%]$') then -- 不允许顶层路由注册rest模式. - return log.warn('Please Do not add [/[type:key] in root route :)]') + return Log:WARN('Please Do not add [/[type:key] in root route :)]') end return registery_router(route, class, route_type) end diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua index a69abdf6..58633bc1 100644 --- a/lualib/httpd/init.lua +++ b/lualib/httpd/init.lua @@ -2,7 +2,7 @@ local HTTP = require "protocol.http" local tcp = require "internal.TCP" local class = require "class" local sys = require "system" -local log = require "log" +local log = require "logging" local cf = require "cf" local type = type @@ -127,23 +127,13 @@ end -- 记录日志到文件 function httpd:log(path) self.logpath = path or "cf-httpd.log" - log.outfile = self.logpath + self.log = log:new({ path = self.logpath }) end function httpd:tolog(code, path, ip, ip_list, method, speed) if self.logpath then - if not self.logfile then - local err - self.logfile, err = io_open(self.logpath, "a") - if not self.logfile then - return log.error(self.logpath..":"..err) - end - end - local ok, err = self.logfile:write(fmt("[%s] - %s - %s - %s - %s - %d - req_time: %0.6f/Sec\n", os_date("%Y/%m/%d %H:%M:%S"), ip, ip_list, path, method, code, speed)) - if not ok then - return log.error(self.logpath..":"..err) - end - self.logfile:flush() + local log = fmt("[%s] - %s - %s - %s - %s - %d - req_time: %0.6f/Sec\n", os_date("%Y/%m/%d %H:%M:%S"), ip, ip_list, path, method, code, speed) + self.log:dump(log) end print(fmt("[%s] - %s - %s - %s - %s - %d - req_time: %0.6f/Sec", os_date("%Y/%m/%d %H:%M:%S"), ip, ip_list, path, method, code, speed)) end diff --git a/lualib/internal/Co.lua b/lualib/internal/Co.lua index e6164b34..191069f4 100644 --- a/lualib/internal/Co.lua +++ b/lualib/internal/Co.lua @@ -1,6 +1,8 @@ -local log = require "log" +local log = require "logging" local task = require "task" +local Log = log:new() + local type = type local assert = assert local error = error @@ -55,7 +57,7 @@ local function f() while 1 do local ok, msg = pcall(co_wait()) if not ok then - log.error(msg) + Log:ERROR(msg) end local co, main = co_self() if not main then @@ -100,7 +102,7 @@ function Co.wakeup(co, ...) assert(type(co) == 'thread', "试图传递一个非协程的类型的参数到wakeup内部.") local status = co_status(co) if co == co_self() then - return log.error("不能唤醒当前正在执行的协程") + return Log:ERROR("不能唤醒当前正在执行的协程") end if main_co == co and status == "suspended" then return task_start(main_task, main_co, ...) diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index 4a2de9f5..d1e0b284 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -3,7 +3,8 @@ local dns = require "protocol.dns" local co = require "internal.Co" local class = require "class" local tcp = require "tcp" -local log = require "log" +local log = require "logging" +local Log = log:new() local split = string.sub local insert = table.insert @@ -79,7 +80,7 @@ end function TCP:send(buf) if self.ssl then - return log.error("Please use ssl_send method :)") + return Log:ERROR("Please use ssl_send method :)") end while 1 do local len = tcp_write(self.fd, buf, #buf) @@ -114,7 +115,7 @@ end function TCP:ssl_send(buf) if not self.ssl then - return log.error("Please use send method :)") + return Log:ERROR("Please use send method :)") end while 1 do local len = tcp_ssl_write(self.ssl, buf, #buf) @@ -151,7 +152,7 @@ end function TCP:recv(bytes) if self.ssl then - return log.error("Please use ssl_recv method :)") + return Log:ERROR("Please use ssl_recv method :)") end self.READ_IO = tcp_pop() local co = co_self() @@ -187,7 +188,7 @@ end function TCP:ssl_recv(bytes) if not self.ssl then - return log.error("Please use recv method :)") + return Log:ERROR("Please use recv method :)") end local buf, len = tcp_sslread(self.ssl, bytes) if not buf then @@ -243,7 +244,7 @@ function TCP:listen(ip, port, cb) self.LISTEN_IO = tcp_pop() self.fd = tcp_new_server_fd(ip, port) if not self.fd then - return log.error("this IP and port Create A bind or listen method Faild! :) ") + return Log:ERROR("this IP and port Create A bind or listen method Faild! :) ") end self.co = co_new(function (fd, ipaddr) while 1 do @@ -263,7 +264,7 @@ function TCP:connect(domain, port) end self.fd = tcp_new_client_fd(IP, port) if not self.fd then - log.error("Connect This IP or Port Faild!"..domain, IP) + Log:ERROR("Connect This IP or Port Faild!"..domain, IP) return nil, "Connect This host fault! :" end local co = co_self() @@ -304,7 +305,7 @@ function TCP:ssl_connect(domain, port) end self.ssl_ctx, self.ssl = tcp.new_ssl(self.fd) if not self.ssl_ctx or not self.ssl then - return log.error("Create a SSL Error! :) ") + return Log:ERROR("Create a SSL Error! :) ") end local co = co_self() self.CONNECT_IO = tcp_pop() diff --git a/lualib/internal/Timer.lua b/lualib/internal/Timer.lua index 63f02529..9966aeb3 100644 --- a/lualib/internal/Timer.lua +++ b/lualib/internal/Timer.lua @@ -1,6 +1,7 @@ local co = require "internal.Co" local ti = require "timer" -local log = require "log" +local log = require "logging" +local Log = log:new() local type = type local pcall = pcall @@ -48,7 +49,7 @@ function Timer.timeout(timeout, cb) end local t = Timer_new() if not t then - return log.error("timeout error: Create timer class error! memory maybe not enough...") + return Log:ERROR("timeout error: Create timer class error! memory maybe not enough...") end local timer = {STOP = false} timer.stop = function (...) @@ -64,7 +65,7 @@ function Timer.timeout(timeout, cb) Timer_release(t) local ok, err = pcall(cb) if not ok then - log.error('timeout error:', err) + Log:ERROR('timeout error:', err) end if timer.STOP then return @@ -88,7 +89,7 @@ function Timer.at(repeats, cb) end local t = Timer_new() if not t then - return log.error("timeout error: Create timer class error! memory maybe not enough...") + return Log:ERROR("timeout error: Create timer class error! memory maybe not enough...") end local timer = { STOP = false } timer.stop = function (...) @@ -125,7 +126,7 @@ function Timer.sleep(repeats) end local t = Timer_new() if not t then - return log.error("timeout error: Create timer class error! memory maybe not enough...") + return Log:ERROR("timeout error: Create timer class error! memory maybe not enough...") end local timer = {} timer.current_co = co_self() diff --git a/lualib/internal/UDP.lua b/lualib/internal/UDP.lua index 28765084..f0926da3 100644 --- a/lualib/internal/UDP.lua +++ b/lualib/internal/UDP.lua @@ -1,6 +1,5 @@ local ti = require "internal.Timer" local co = require "internal.Co" -local log = require "log" local udp = require "udp" local class = require "class" @@ -85,4 +84,4 @@ function UDP:close() -- var_dump(self) end -return UDP \ No newline at end of file +return UDP diff --git a/lualib/log/init.lua b/lualib/log/init.lua deleted file mode 100644 index 3affa571..00000000 --- a/lualib/log/init.lua +++ /dev/null @@ -1,142 +0,0 @@ ---[[ --- log.lua --- --- Copyright (c) 2016 rxi --- --- This library is free software; you can redistribute it and/or modify it --- under the terms of the MIT license. See LICENSE for details. --- Modefy by CandyMi In 2018.12.18 - -log的内部方法包括: -log.trace(...) 紫色 -log.debug(...) 天蓝色 -log.info(...) 绿色 -log.warn(...) 黄色 -log.error(...) 红色 -log.fatal(...) 粉色 - -log.usecolor -默认情况下: 这个为true! 如果你的终端不支持ANSI颜色转义码, 请将它设置为false或者nil. - -log.outfile -将log输出到outfile字符串指定的文件(路径). - -log.level -请参考使用方法相关method - ---]] - -local debug_getinfo = debug.getinfo - -local tostring = tostring - -local select = select - -local ipairs = ipairs - -local type = type - -local concat = table.concat - -local date = os.date - -local open = io.open - -local ceil = math.ceil - -local floor = math.floor - -local toint = math.tointeger - -local fmt = string.format - - -local log = { _version = "0.1.0" } - -log.usecolor = true -log.outfile = nil -log.level = "trace" - - -local modes = { - { name = "trace", color = "\27[34m", }, - { name = "debug", color = "\27[36m", }, - { name = "info", color = "\27[32m", }, - { name = "warn", color = "\27[33m", }, - { name = "error", color = "\27[31m", }, - { name = "fatal", color = "\27[35m", }, -} - - -local levels = {} -for i, v in ipairs(modes) do - levels[v.name] = i -end - - -local round = function(x, increment) - if not toint(x) then - increment = increment or 1 - x = x / increment - return (x > 0 and floor(x + .5) or ceil(x - .5)) * increment - end - return x -end - - -local _tostring = tostring - -local tostring = function(...) - local t = {} - for i = 1, select('#', ...) do - local x = select(i, ...) - if type(x) == "number" then - x = round(x, .01) - end - t[#t + 1] = _tostring(x) - end - return concat(t, " ") -end - - -for i, x in ipairs(modes) do - local nameupper = x.name:upper() - log[x.name] = function(...) - - -- Return early if we're below the log level - if i < levels[log.level] then - return - end - - local msg = tostring(...) - local info = debug_getinfo(2, "Sl") - local lineinfo = "[C]:[-1]" - if info then - lineinfo = info.short_src .. ":" .. info.currentline - end - - -- Output to console - print(fmt("%s[%s][%s]%s %s: %s", - log.usecolor and x.color or "", - nameupper, - date("%Y/%m/%d %H:%M:%S"), - log.usecolor and "\27[0m" or "", - lineinfo, - msg)) - - -- Output to log file - if log.outfile then - local fp = open(log.outfile, "a") - if not fp then - return log.warn("Cant't write info to "..(log.outfile or "")) - end - fp:write(fmt("[%s][%s] %s: %s\n", nameupper, date("%Y/%m/%d %H:%M:%S"), lineinfo, msg)) - fp:close() - end - - end - -end - - -return log diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua index 021c421a..5b479f6e 100644 --- a/lualib/logging/init.lua +++ b/lualib/logging/init.lua @@ -102,8 +102,10 @@ local paths = {} local Log = class("Log") function Log:ctor (opt) - self.path = opt.path - self.now = Y_m_d() + if type(opt) == 'table' then + self.path = opt.path + self.now = Y_m_d() + end end -- 常规日志 @@ -144,7 +146,7 @@ function Log:dump(log) paths[self.path] = file end end - file:write(log) + file:write(log):flush() end end diff --git a/lualib/protocol/dns.lua b/lualib/protocol/dns.lua index ab5193f8..7bb920cb 100644 --- a/lualib/protocol/dns.lua +++ b/lualib/protocol/dns.lua @@ -1,7 +1,7 @@ local UDP = require "internal.UDP" local co = require "internal.Co" local sys = require "sys" -local log = require "log" +local log = require "logging" local prefix = '::ffff:' @@ -315,4 +315,4 @@ function dns.resolve(domain) end -- require "utils" -- var_dump(dns_cache) -return dns \ No newline at end of file +return dns diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index 958db118..23e61c83 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -1,8 +1,10 @@ -local log = require "log" +local log = require "logging" local sys = require "system" local tcp = require "internal.TCP" local wsserver = require "protocol.websocket.server" +local Log = log:new() + local crypt = require "crypt" local sha1 = crypt.sha1 local base64 = crypt.base64encode @@ -449,14 +451,14 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) if before_func and (typ == HTTP_PROTOCOL.API or typ == HTTP_PROTOCOL.USE) then local ok, code, data = pcall(before_func, content) if not ok then -- before 函数执行出错 - log.error(code) + Log:ERROR(code) sock:send(ERROR_RESPONSE(http, 500, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) return sock:close() end if code then if type(code) == "number" then if code < 200 or code > 500 then - log.error("before function: Illegal return value") + Log:ERROR("before function: Illegal return value") sock:send(ERROR_RESPONSE(http, 500, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) return sock:close() elseif code == 301 or code == 302 then @@ -523,7 +525,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) ok, body = pcall(cls, content) end if not ok then - log.error(body) + Log:ERROR(body) statucode = 500 sock:send(ERROR_RESPONSE(http, statucode, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) return sock:close() @@ -533,7 +535,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) elseif typ == HTTP_PROTOCOL.WS then local ok, msg = pcall(Switch_Protocol, http, cls, sock, HEADER, METHOD, VERSION, PATH, HEADER['X-Real-IP'] or ipaddr, start) if not ok then - log.error(msg) + Log:ERROR(msg) return sock:close() end return @@ -546,7 +548,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) end ok, body, file_type = pcall(cls, './'..path) if not ok then - log.error(body) + Log:ERROR(body) statucode = 500 sock:send(ERROR_RESPONSE(http, statucode, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) return sock:close() @@ -586,7 +588,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) header[#header+1] = 'Content-Length: '.. #body end else - log.warn('response body not a string type.'..'('..tostring(body)..')') + Log:WARN('response body not a string type.'..'('..tostring(body)..')') body = '' end else diff --git a/lualib/protocol/mqtt/init.lua b/lualib/protocol/mqtt/init.lua index b3e8c03a..b9e6b777 100644 --- a/lualib/protocol/mqtt/init.lua +++ b/lualib/protocol/mqtt/init.lua @@ -10,7 +10,7 @@ local string = require "string" local calss = require "class" local tcp = require "internal.TCP" local Co = require "internal.Co" -local log = require "log" +local log = require "logging" local protocol = require "protocol.mqtt.protocol" local protocol4 = require "protocol.mqtt.protocol4" local co = require "internal.Co" @@ -23,6 +23,8 @@ local make_packet4 = protocol4.make_packet local parse_packet4 = protocol4.parse_packet local connack_return_code = protocol4.connack_return_code +local Log = log:new() + -- cache to locals local type = type local pairs = pairs @@ -79,7 +81,7 @@ function client:subscribe(opt, func) end local ok, err = pcall(self.handle, nil) if not ok then - log.error(err) + Log:ERROR(err) end return false, 'waiting for the next packet failed' end @@ -87,7 +89,7 @@ function client:subscribe(opt, func) if packet.type == packet_type.PUBLISH then local ok, err = pcall(self.handle, packet) if not ok then - log.error(err) + Log:ERROR(err) end self:acknowledge(packet) elseif packet.type == packet_type.PUBACK then @@ -330,4 +332,4 @@ function client:close( ... ) self.sock = nil end -return client \ No newline at end of file +return client diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua index 61c585c0..386aef91 100644 --- a/lualib/protocol/mysql.lua +++ b/lualib/protocol/mysql.lua @@ -1,5 +1,4 @@ local tcp = require "internal.TCP" -local log = require "log" local crypt = require "crypt" local sub = string.sub diff --git a/lualib/protocol/redis.lua b/lualib/protocol/redis.lua index 132a9b49..937102ac 100644 --- a/lualib/protocol/redis.lua +++ b/lualib/protocol/redis.lua @@ -1,10 +1,12 @@ -local log = require "log" +local log = require "logging" local class = require "class" local Co = require "internal.Co" local tcp = require "internal.TCP" local table = table local concat = table.concat +local Log = log:new() + local co_spwan = Co.spwan local sub = string.sub @@ -153,7 +155,7 @@ function redis:psubscribe(pattern, func) if not ok or not msg or not self.sock then local ok, err = pcall(func, nil) if not ok then - log.error(err) + Log:ERROR(err) end return end @@ -163,7 +165,7 @@ function redis:psubscribe(pattern, func) end local ok, err = pcall(func, data) if not ok then - return log.error(err) + return Log:ERROR(err) end end end) @@ -209,4 +211,4 @@ function redis:close() end end -return redis \ No newline at end of file +return redis diff --git a/lualib/protocol/websocket/server.lua b/lualib/protocol/websocket/server.lua index 58da2d36..b0098e45 100644 --- a/lualib/protocol/websocket/server.lua +++ b/lualib/protocol/websocket/server.lua @@ -1,10 +1,12 @@ -local log = require "log" +local log = require "logging" local class = require "class" local co = require "internal.Co" local wbproto = require "protocol.websocket.protocol" local _recv_frame = wbproto.recv_frame local _send_frame = wbproto.send_frame +local Log = log:new() + local co_self = co.self local co_wait = co.wait local co_spwan = co.spwan @@ -55,7 +57,7 @@ function websocket:start() for _, f in ipairs(write_list) do local ok, err = pcall(f) if not ok then - log.error(err) + Log:ERROR(err) end end write_list = {} @@ -108,8 +110,8 @@ function websocket:start() end, -- ping = function (self, data) -- if self.CLOSE then return end - -- add_to_queue(write_list, function() - -- _send_frame(sock, true, 0x9, data, cls.max_payload_len or 65535, cls.send_masked or false) + -- add_to_queue(write_list, function() + -- _send_frame(sock, true, 0x9, data, cls.max_payload_len or 65535, cls.send_masked or false) -- end) -- return wakeup(write_co) -- end, @@ -128,7 +130,7 @@ function websocket:start() local on_close = cls.on_close local ok, err = pcall(on_open, cls) if not ok then - log.error(err) + Log:ERROR(err) return sock:close() end while 1 do @@ -144,12 +146,12 @@ function websocket:start() if err then local ok, err = pcall(on_error, cls, err) if not ok then - log.error(err) + Log:ERROR(err) end end local ok, err = pcall(on_close, cls, data) if not ok then - log.error(err) + Log:ERROR(err) end -- print("读取协程退出了") return wakeup(write_co) @@ -174,4 +176,4 @@ function websocket:start() end end -return websocket \ No newline at end of file +return websocket diff --git a/script/main.lua b/script/main.lua index 11da6b6f..5499cafd 100644 --- a/script/main.lua +++ b/script/main.lua @@ -74,4 +74,4 @@ app:static('static', 10) app:listen("0.0.0.0", 8080) -- 运行 -app:run() \ No newline at end of file +app:run() From f0c19a263302a37b484d66207a32b589469b9570 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 28 Apr 2019 17:28:19 +0800 Subject: [PATCH 013/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0redis=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/redis.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lualib/protocol/redis.lua b/lualib/protocol/redis.lua index 937102ac..1b2310cf 100644 --- a/lualib/protocol/redis.lua +++ b/lualib/protocol/redis.lua @@ -2,9 +2,8 @@ local log = require "logging" local class = require "class" local Co = require "internal.Co" local tcp = require "internal.TCP" -local table = table -local concat = table.concat +local concat = table.concat local Log = log:new() local co_spwan = Co.spwan @@ -98,14 +97,14 @@ local function read_boolean(sock) end local function redis_login(sock, auth, db) - if auth then + if type(auth) == 'string' then sock:send(CMD("AUTH", auth)) local ok, err = read_response(sock) if not ok then return nil, err end end - if db then + if type(db) == 'number' then sock:send(CMD("SELECT", db)) local ok, err = read_response(sock) if not ok then From 68dbcccf656c5da38824fc1d9d238e04478dbf8b Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 28 Apr 2019 18:01:56 +0800 Subject: [PATCH 014/956] =?UTF-8?q?=E4=B8=BACache=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E5=A4=9A=E5=AE=9E=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/Cache/init.lua | 234 +++++++++++++++++++++--------------------- script/test_Cache.lua | 10 +- 2 files changed, 125 insertions(+), 119 deletions(-) diff --git a/lualib/Cache/init.lua b/lualib/Cache/init.lua index 337ad994..2192da3c 100644 --- a/lualib/Cache/init.lua +++ b/lualib/Cache/init.lua @@ -1,3 +1,4 @@ +local class = require "class" local log = require "logging" local Co = require "internal.Co" local timer = require "internal.Timer" @@ -7,12 +8,10 @@ local co_self = Co.self local co_wait = Co.wait local co_wakeup = Co.wakeup -local type = type local ipairs = ipairs local setmetatable = setmetatable local table = table -local unpack = table.unpack local remove = table.remove local upper = string.upper local lower = string.lower @@ -20,19 +19,6 @@ local splite = string.gmatch local Log = log:new() - --- 默认情况下, 保持50个redis连接 -local MAX, COUNT = 50, 0 - --- 连接池 -local POOL = {} - --- 是否已经初始化 -local INITIALIZATION = false - --- session创建函数 -local CREATE_CACHE - -- 注册命令 local commands = { 'sismember', 'exists' @@ -47,136 +33,154 @@ local function in_command(cmd) return false end -local wlist = {} +local keys = {} -local function add_wait(co) - wlist[#wlist+1] = co +-- 注入函数 +local function in_keys(key) + return keys[key] end -local function pop_wait() - return remove(wlist) +-- 创建Cache函数 +local function CREATE_CACHE(opt) + local times = 1 + local rds + while 1 do + rds = redis:new(opt) + local ok, err = rds:connect() + if ok then + break + end + Log:ERROR('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接") + times = times + 1 + rds:close() + timer.sleep(3) + end + local ok, ret = rds:cmd("CONFIG", "GET", "TIMEOUT") + if not INITIALIZATION and ret[2] ~= '0' then + rds:cmd("CONFIG SET", "TIMEOUT", "0") + end + return rds end -local function add_cache(session) - POOL[#POOL+1] = session +-- 加入到连接池内 +local function add_cache(self, cache) + self.cache_pool[#self.cache_pool+1] = cache end -local function pop_cache() - if #POOL > 0 then - return remove(POOL) - end - if COUNT < MAX then - COUNT = COUNT + 1 - return CREATE_CACHE() - end - add_wait(co_self()) - return co_wait() +-- 加入到协程池内 +local function add_wait(self, co) + self.co_pool[#self.co_pool+1] = co end +-- 从连接池内取出一个cache对象 +local function pop_cache(self) + if #self.cache_pool > 0 then + return remove(self.cache_pool) + end + if self.current < self.max then + self.current = self.current + 1 + return CREATE_CACHE(self) + end + add_wait(self, co_self()) + return co_wait() +end -local Cache = setmetatable({}, {__index = function (_, key) - if not INITIALIZATION then - return nil, 'Cache尚未初始化' - end - if lower(key) == "publish" or lower(key) == "subscribe" or lower(key) == "psubscribe" then - return nil, 'Cache error: Cache不支持在缓存中直接使用此命令.' - end - local cache = pop_cache() - if in_command(key) then - return function (_, ...) - local ok, ret - while 1 do - ok, ret = cache[key](cache, ...) - if ret ~= 'server close!!' then - break - end - cache:close() - cache = CREATE_CACHE() - end - if #wlist > 0 then - co_wakeup(pop_wait(), cache) - else - add_cache(cache) - end - return ok, ret - end - end - return function (_, ...) +-- 弹出一个等待协程 +local function pop_wait(self) + return remove(self.co_pool) +end + +-- 构建Cache对象 +local function setmeta(self) + keys['count'] = self.count + return setmetatable(self, { + __index = function(t, key) + local f = in_keys(key) + if f then + return f + end + if lower(key) == "publish" or lower(key) == "subscribe" or lower(key) == "psubscribe" then + return nil, 'Cache error: Cache不支持在缓存中直接使用此命令.' + end + if in_command(key) then + return function (_, ...) + local ok, ret + local session + while 1 do + session = pop_cache(t) + ok, ret = session[key](session, ...) + if ret ~= 'server close!!' then + break + end + session:close() + session = nil + end + local co = pop_wait(t) + if co then + co_wakeup(co, session) + return ok, ret + end + add_cache(t, session) + return ok, ret + end + end + return function (_, ...) local ok, ret local keys = {} for k in splite(key, "([^_]+)") do keys[#keys+1] = k end + local session while 1 do + session = pop_cache(t) if #keys > 1 then - ok, ret = cache:cmd(upper(keys[1]), upper(keys[2]), ...) + ok, ret = session:cmd(upper(keys[1]), upper(keys[2]), ...) else - ok, ret = cache:cmd(upper(keys[1]), ...) + ok, ret = session:cmd(upper(keys[1]), ...) end if ret ~= 'server close!!' then break end - cache:close() - cache = CREATE_CACHE() + session:close() + session = nil end - if #wlist > 0 then - co_wakeup(pop_wait(), cache) - else - add_cache(cache) + local co = pop_wait(t) + if co then + co_wakeup(co, session) + return ok, ret end + add_cache(t, session) return ok, ret end -end}) - --- 初始化 -function Cache.init(opt) - if INITIALIZATION then - return nil, "Cache已经初始化." - end - - assert(type(opt) == 'table', "Cache error: 错误的Cache配置文件.") - - assert(type(opt.host) == 'string' and opt.host ~= '', "Cache error: 异常的主机名.") - - assert(type(opt.port) == 'number' and opt.port > 0 and opt.port <= 65535, "Cache error: 异常的端口.") - - assert(not opt.auth or type(opt.auth) == 'string' , "Cache error: 异常的auth.") - - assert(not opt.db or type(opt.db) == 'number' and opt.db >= 0 and opt.db <= 15, "Cache error: 异常的db.") + end}) == self +end - if type(opt.max) == 'number' and opt.max > 0 then - MAX = opt.max - end +local Cache = class("Cache") + +function Cache:ctor (opt) + self.host = opt.host + self.port = opt.port + self.db = opt.db + self.auth = opt.auth + self.max = opt.max or 50 + self.current = 0 + -- 连接池 + self.cache_pool = {} + -- 协程池 + self.co_pool = {} +end - CREATE_CACHE = function () - local times = 1 - local rds - while 1 do - rds = redis:new(opt) - local ok, err = rds:connect() - if ok then - break - end - Log:ERROR('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接") - times = times + 1 - rds:close() - timer.sleep(3) - end - local ok, ret = rds:cmd("CONFIG", "GET", "TIMEOUT") - if not INITIALIZATION and ret[2] ~= '0' then - rds:cmd("CONFIG SET", "TIMEOUT", "0") - end - return rds - end - add_cache(CREATE_CACHE()) - INITIALIZATION = true - COUNT = COUNT + 1 - return true +function Cache:connect () + if not self.INITIALIZATION then + add_cache(self, pop_cache(self)) + self.INITIALIZATION = true + return setmeta(self) + end + return true end --- 连接池数量 -function Cache.count() - return #POOL +function Cache:count() + return self.current, self.max end return Cache diff --git a/script/test_Cache.lua b/script/test_Cache.lua index 1832c693..2c259445 100644 --- a/script/test_Cache.lua +++ b/script/test_Cache.lua @@ -3,16 +3,18 @@ local Cache = require "Cache" local Co = require "internal.Co" local timer = require "internal.Timer" require "utils" + local opt = { host = "localhost", port = 6379, auth = nil, - db = nil, + db = 1, max = 1, } Co.spwan(function ( ... ) - local ok, err = Cache.init(opt) + local Cache = Cache:new(opt) + local ok, err = Cache:connect() if not ok then return print(err) end @@ -114,7 +116,7 @@ Co.spwan(function ( ... ) local ok, ret = Cache:script_load("return 10086") print(ok); var_dump(ret) - local sha = ret + local sha = ret local ok, ret = Cache:script_exists(sha) print(ok); var_dump(ret) @@ -126,5 +128,5 @@ Co.spwan(function ( ... ) -- 其它一些特殊方法支持 -- type, move, rename, keys, randomkey等等 - + print(Cache:count()) end) From 11fdae72a463169e1fac42e2bb124f87976bfb7e Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 28 Apr 2019 18:29:47 +0800 Subject: [PATCH 015/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0logging?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/logging/init.lua | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua index 5b479f6e..3e95e272 100644 --- a/lualib/logging/init.lua +++ b/lualib/logging/init.lua @@ -22,7 +22,7 @@ local modf = math.modf -- 格式化时间: [年-月-日 时:分:秒,毫秒] local function fmt_Y_m_d_H_M_S() local ts, f = modf(now()) - f = format("%3.0f", f * 1e3) + f = format("%03.0f", f * 1e3) return concat({'[', os_date('%Y-%m-%d %H:%M:%S'), ',', f, ']'}) end @@ -43,29 +43,26 @@ local function table_format(t) while 1 do local mt = getmetatable(t) for key, value in pairs(t) do + local k, v if type(key) == 'number' then - if type(value) == 'table' then - if t ~= value then - tab[#tab+1] = concat({'[', key, ']', '=', '{', concat(t, ", "), '}'}) - end - elseif type(value) == 'string' then - tab[#tab+1] = concat({'[', key, ']', '="', tostring(value), '"'}) - else - tab[#tab+1] = concat({'[', key, ']', '=', tostring(value)}) - end + k = concat({'[', key, ']'}) else - if type(value) == 'table' then - if t ~= value then - tab[#tab+1] = concat({'["', key, '"]', '=', '{', concat(t, ", "), '}'}) - end - elseif type(value) == 'string' then - tab[#tab+1] = concat({'["', key, '"]', '="', tostring(value), '"'}) + k = concat({'["', key, '"]'}) + end + if type(value) == 'table' then + if t ~= value then + v = table_format(value) else - tab[#tab+1] = concat({'["', key, '"]', '=', tostring(value)}) + v = tostring(value) end + elseif type(value) == 'string' then + v = concat({'"', value, '"'}) + else + v = tostring(value) end + tab[#tab+1] = concat({k, '=', v}) end - if not mt then + if not mt or mt == t then break end t = mt From c1d67b79f5b4632416b42bce551c67b802769e03 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 29 Apr 2019 02:57:25 +0800 Subject: [PATCH 016/956] =?UTF-8?q?=E4=BC=98=E5=8C=96DB=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=A4=9A=E5=AE=9E=E4=BE=8B=E5=88=9D=E5=A7=8B=E5=8C=96=E3=80=81?= =?UTF-8?q?=E8=BF=9E=E6=8E=A5=E6=96=B9=E6=B3=95,=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E4=BF=AE=E6=94=B9DB=5Ftest.lua=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/DB/init.lua | 300 +++++++++++++++++--------------------- lualib/protocol/mysql.lua | 7 +- script/test_DB.lua | 55 ++++--- 3 files changed, 164 insertions(+), 198 deletions(-) diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index 74680e16..01e63062 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -1,6 +1,7 @@ local mysql = require "protocol.mysql" local timer = require "internal.Timer" local co = require "internal.Co" +local class = require "class" local log = require "logging" local Log = log:new() @@ -26,8 +27,6 @@ local remove = table.remove local concat = table.concat local unpack = table.unpack -local os_time = os.time - local SELECT = "SELECT" local INSERT = "INSERT INTO" @@ -64,58 +63,53 @@ local GROUPBY = "GROUP BY" local COMMA = ", " -local INITIALIZATION - --- 最大DB连接数量 -local MAX, COUNT = 50, 0 - -- 空闲连接时间 -local WAIT_TIMEOUT = 2592000 +local WAIT_TIMEOUT = 31104000 -- 数据库连接创建函数 -local DB_CREATE - --- 等待db对象的协程列表 -local wlist = {} - -local function add_wait(co) - wlist[#wlist+1] = co +local function DB_CREATE (opt) + local times = 1 + local db + while 1 do + db = mysql:new() + local connect, err = db:connect(opt) + if connect then + break + end + db:close() + Log:ERROR('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接") + times = times + 1 + timer.sleep(3) + end + db:query(fmt('SET wait_timeout=%s', tostring(WAIT_TIMEOUT))) + db:query(fmt('SET interactive_timeout=%s', tostring(WAIT_TIMEOUT))) + db:query(fmt('SET wait_timeout=%s', tostring(WAIT_TIMEOUT))) + -- Log:DEBUG(db:query("show session variables where variable_name ='wait_timeout' or variable_name = 'interactive_timeout'")) + return db end -local function pop_wait() - return remove(wlist) +local function add_wait(self, co) + self.co_pool[#self.co_pool + 1] = co end --- 数据库连接池 -local POOL = {} +local function pop_wait(self) + return remove(self.co_pool) +end -local function add_db(db) - POOL[#POOL + 1] = { session = db, ttl = os_time() } +local function add_db(self, db) + self.db_pool[#self.db_pool + 1] = db end -- 负责创建连接/加入等待队列 -local function get_db() - if #POOL > 0 then - while 1 do - local db = remove(POOL) - if not db then break end -- 连接池内已经没有连接了 - if db.ttl > os_time() - WAIT_TIMEOUT then - return db.session - end - db.session:close() - COUNT = COUNT - 1 - end +local function pop_db(self) + if #self.db_pool > 0 then + return remove(self.db_pool) end - if COUNT < MAX then - COUNT = COUNT + 1 - local db = DB_CREATE() - if db then - return db - end - COUNT = COUNT - 1 - -- 连接失败或者其他情况, 将等待其他协程唤醒; 保证公平竞争数据库连接 + if self.current < self.max then + self.current = self.current + 1 + return DB_CREATE(self) end - add_wait(co_self()) + add_wait(self, co_self()) return co_wait() end @@ -147,43 +141,6 @@ local function format_value4(t) return fmt("%s %s %s '%s'", unpack(t)) end --- 执行 -local function execute(query) - if query.SELECT then - assert(query.FROM and query.WHERE, "查询语句必须使用from方法与where条件.") - end - if query.DELETE then - assert(query.WHERE, "删除语句请加上where条件") - end - if query.INSERT then - assert(query.FILEDS and query.VALUES, "插入语句请加上fields与values") - end - if query.UPDATE then - assert(query.WHERE, "更新语句请加上where条件") - end - local QUERY = concat(query, " ") - -- print(QUERY) - local db, ret, err - while 1 do - db = get_db() - if db then - ret, err = db:query(QUERY) - if db.state then - break - end - Log:ERROR(err) - db:close() - db, ret, err = nil - end - end - if #wlist > 0 then - co_wakeup(pop_wait(), db) - else - add_db(db) - end - return ret, err -end - local function limit(query, limit1, limit2) local t1 = type(limit1) local t2 = type(limit2) @@ -332,10 +289,6 @@ local function fields(query, fields) end -- 插入语句专用函数 -- - - - - -- 更新语句专用 -- local function set(query, values) local tpy = type(values) @@ -350,60 +303,62 @@ local function set(query, values) end -- 更新语句专用 -- - - -local DB = {} - --- 初始化数据库 -function DB.init(opt) - if INITIALIZATION then - return nil, "DB已经初始化." +-- 执行 +local function execute(query) + -- Log:DEBUG(query) + if query.SELECT then + assert(query.FROM and query.WHERE, "查询语句必须使用from方法与where条件.") end - if type(opt) ~= 'table' then - return nil, '错误的DB配置文件.' + if query.DELETE then + assert(query.WHERE, "删除语句请加上where条件") end - if type(opt.max) == 'number' then - MAX = opt.max + if query.INSERT then + assert(query.FILEDS and query.VALUES, "插入语句请加上fields与values") end - local config = { - host = opt.host or 'localhost', - port = opt.port or 3306, - database = opt.database, - user = opt.user, - password = opt.password, - } - DB_CREATE = function(...) - local times = 1 - local db - while 1 do - db = mysql:new() - local connect, err = db:connect(config) - if connect then - break - end - Log:ERROR('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接") - db:close() - times = times + 1 - timer.sleep(3) - end - db:query(fmt('SET wait_timeout=%s', tostring(WAIT_TIMEOUT))) - db:query(fmt('SET interactive_timeout=%s', tostring(WAIT_TIMEOUT))) - return db + if query.UPDATE then + assert(query.WHERE, "更新语句请加上where条件") end - add_db(get_db()) - INITIALIZATION = true - return true + local QUERY = concat(query, " ") + Log:DEBUG(QUERY) + local self = query.self + query.self = nil + return self:query(QUERY) end +local DB = class("DB") + +function DB:ctor(opt) + self.host = opt.host + self.port = opt.port + self.username = opt.username + self.password = opt.password + self.database = opt.database + self.max = opt.max or 50 + self.current = 0 + -- 协程池 + self.co_pool = {} + -- 连接池 + self.db_pool = {} +end + +function DB:connect () + if not self.INITIALIZATION then + add_db(self, pop_db(self)) + self.INITIALIZATION = true + return self.INITIALIZATION + end + return self.INITIALIZATION +end -- 查询语句 -function DB.select(fields) - if not INITIALIZATION then +function DB:select(fields) + if not self.INITIALIZATION then return nil, "DB尚未初始化" end local tpy = type(fields) assert(tpy == "string" or tpy == "table", "错误的字段类型(fields):"..tostring(fields)) local query = { + self = self, [1] = SELECT, SELECT = true, from = from, @@ -424,31 +379,16 @@ function DB.select(fields) return query end --- 插入语句 -function DB.insert(table_name) - if not INITIALIZATION then - return nil, "DB尚未初始化" - end - local tpy = type(table_name) - assert(tpy == "string", "错误的表名(table_name):"..tostring(table_name)) - return { - [1] = INSERT, - [2] = table_name, - INSERT = true, - fields = fields, - values = values, - execute = execute, - } -end -- 更新语句 -function DB.update(table_name) - if not INITIALIZATION then +function DB:update(table_name) + if not self.INITIALIZATION then return nil, "DB尚未初始化" end local tpy = type(table_name) assert(tpy == "string" or tpy == "tables", "错误的表名(table_name):"..tostring(table_name)) return { + self = self, [1] = UPDATE, [2] = table_name, UPDATE = true, @@ -459,14 +399,34 @@ function DB.update(table_name) } end + +-- 插入语句 +function DB:insert(table_name) + if not self.INITIALIZATION then + return nil, "DB尚未初始化" + end + local tpy = type(table_name) + assert(tpy == "string", "错误的表名(table_name):"..tostring(table_name)) + return { + self = self, + [1] = INSERT, + [2] = table_name, + INSERT = true, + fields = fields, + values = values, + execute = execute, + } +end + -- 删除语句 -function DB.delete(table_name) - if not INITIALIZATION then +function DB:delete(table_name) + if not self.INITIALIZATION then return nil, "DB尚未初始化" end local tpy = type(table_name) assert(tpy == "string" or tpy == "tables", "错误的表名(table_name):"..tostring(table_name)) return { + self = self, [1] = DELETE, [2] = FROM, [3] = table_name, @@ -478,35 +438,37 @@ function DB.delete(table_name) } end --- 原始SQL -function DB.query(query) - if not INITIALIZATION then - return nil, "DB尚未初始化" - end - assert(type(query) == 'string' and query ~= '' , "原始SQL类型错误(query):"..tostring(query)) - local db, ret, err - while 1 do - db = get_db() - if db then - ret, err = db:query(query) - if db.state then - break - end - Log:ERROR(err) - db:close() - db, ret, err = nil - end - end - if #wlist > 0 then - co_wakeup(pop_wait(), db) - else - add_db(db) - end +-- 原始查询语句 +function DB:query(query) + if not self.INITIALIZATION then + return nil, "DB尚未初始化" + end + assert(type(query) == 'string' and query ~= '' , "原始SQL类型错误(query):"..tostring(query)) + local db, ret, err + while 1 do + db = pop_db(self) + if db then + ret, err = db:query(query) + if db.state then + break + end + Log:ERROR(err) + db:close() + self.current = self.current - 1 + db, ret, err = nil, nil, nil + end + end + local co = pop_wait(self) + if co then + co_wakeup(co, db) return ret, err + end + add_db(self, db) + return ret, err end -function DB.count( ... ) - return #POOL +function DB:count() + return self.current, self.max, #self.co_pool, #self.db_pool end return DB diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua index 386aef91..507a4027 100644 --- a/lualib/protocol/mysql.lua +++ b/lualib/protocol/mysql.lua @@ -375,7 +375,7 @@ function MySQL.connect(self, opts) self.compact = opts.compact_arrays local database = opts.database or "" - local user = opts.user or "" + local username = opts.username or "" local host = opts.host if not host then @@ -383,9 +383,6 @@ function MySQL.connect(self, opts) end local port = opts.port or 3306 - if not pool then - pool = user .. ":" .. database .. ":" .. host .. ":" .. port - end local ok = sock:connect(host, port) if not ok then @@ -475,7 +472,7 @@ function MySQL.connect(self, opts) client_flags, self._max_packet_size, strrep("\0", 24), -- TODO: add support for charset encoding - user, + username, token, database) diff --git a/script/test_DB.lua b/script/test_DB.lua index 475d6f44..a011acfa 100644 --- a/script/test_DB.lua +++ b/script/test_DB.lua @@ -1,17 +1,24 @@ +local log = require "logging" +local cf = require "cf" local DB = require "DB" -local co = require "internal.Co" +local Log = log:new() + require "utils" -local ok, err = DB.init({ - host = "localhost", - port = 3306, - database = "test", - user = "root", - password = "123456789" - }) +local db = DB:new { + host = 'localhost', + port = 3306, + database = 'test', + username = 'root', + password = '123456789', + max = 1, +} + +local ok = db:connect() if not ok then - return print("连接mysql 失败: "..err) + return print("连接mysql失败") end +print("连接成功") --[[ 复制下面语句到任意管理工具即可导入测试表进行测试 @@ -25,19 +32,19 @@ end DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `name` varchar(255) NOT NULL, - `user` varchar(255) NOT NULL, - `passwd` varchar(255) NOT NULL, + `name` varchar(255) CHARACTER SET utf8mb4 NOT NULL, + `user` varchar(255) CHARACTER SET utf8mb4 NOT NULL, + `passwd` varchar(255) CHARACTER SET utf8mb4 NOT NULL, PRIMARY KEY (`id`) - ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4; + ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; SET FOREIGN_KEY_CHECKS = 1; --]] -- 插入语句示例 -co.spwan(function ( ... ) - local ret, err = DB.insert("user") +cf.fork(function ( ... ) + local ret, err = db:insert("user") :fields({"name", "user", "passwd"}) :values({ {"candy", "root", "123456789"}, @@ -53,8 +60,8 @@ co.spwan(function ( ... ) end) -- 查询语句示例 -co.spwan(function ( ... ) - local ret, err = DB.select({"id", "name", "user", "passwd"}) +cf.fork(function ( ... ) + local ret, err = db:select({"id", "name", "user", "passwd"}) :from({"user"}) :where({ {"id", "!=", "0"}, @@ -91,8 +98,8 @@ co.spwan(function ( ... ) end) -- 更新语句示例 -co.spwan(function ( ... ) - local ret, err = DB.update("user") +cf.fork(function ( ... ) + local ret, err = db:update("user") :set({ {"name", "=", "管理员"}, {"user", "=", "Administrator"}, @@ -112,8 +119,8 @@ co.spwan(function ( ... ) end) -- 删除语句示例 -co.spwan(function ( ... ) - local ret, err = DB.delete("user") +cf.fork(function ( ... ) + local ret, err = db:delete("user") :where({ {"id", ">", 1}, }) @@ -129,11 +136,11 @@ co.spwan(function ( ... ) end) -co.spwan(function ( ... ) - local ret, err = DB.query("show variables like 'wait_timeout'") +cf.fork(function ( ... ) + local ret, err = db:query("show variables like 'wait_timeout'") if not ret then return print(err) end var_dump(ret) -end) \ No newline at end of file +end) From 4b30b24f13d9f896d9272e5cb2f160082c51bf7d Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 29 Apr 2019 02:58:08 +0800 Subject: [PATCH 017/956] =?UTF-8?q?=E4=BC=98=E5=8C=96DB=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=A4=9A=E5=AE=9E=E4=BE=8B=E5=88=9D=E5=A7=8B=E5=8C=96=E3=80=81?= =?UTF-8?q?=E8=BF=9E=E6=8E=A5=E6=96=B9=E6=B3=95,=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E4=BF=AE=E6=94=B9DB=5Ftest.lua=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/DB/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index 01e63062..7785190a 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -319,7 +319,7 @@ local function execute(query) assert(query.WHERE, "更新语句请加上where条件") end local QUERY = concat(query, " ") - Log:DEBUG(QUERY) + -- Log:DEBUG(QUERY) local self = query.self query.self = nil return self:query(QUERY) From 550921836c6f0169bf5bf7149bd968bc0357f63f Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 1 May 2019 16:58:17 +0800 Subject: [PATCH 018/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9http=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE=E4=BB=85=E5=9C=A8options=E4=B8=8Ehead=E6=89=8D?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E8=B7=A8=E5=9F=9F=E6=A0=87=E5=BF=97=E5=A4=B4?= =?UTF-8?q?=E9=83=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http.lua | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index 23e61c83..9e165c83 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -310,11 +310,6 @@ local function ERROR_RESPONSE(http, code, path, ip, forword, method, speed) HTTP_DATE(), 'Origin: *', 'Allow: GET, POST, PUT, HEAD, OPTIONS', - 'Allow: GET, POST, PUT, HEAD, OPTIONS', - 'Access-Control-Allow-Origin: *', - 'Access-Control-Allow-Headers: *', - 'Access-Control-Allow-Methods: GET, POST, PUT, HEAD, OPTIONS', - 'Access-Control-Max-Age: 86400', 'Connection: close', 'server: ' .. (http.__server or SERVER), }, CRLF), CRLF2}) @@ -440,6 +435,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) 'Access-Control-Allow-Origin: *', 'Access-Control-Allow-Headers: *', 'Access-Control-Allow-Methods: GET, POST, PUT, HEAD, OPTIONS', + 'Access-Control-Allow-Credentials: true', 'Access-Control-Max-Age: 86400', 'Connection: keep-alive', 'server: ' .. (server or SERVER), @@ -465,12 +461,6 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) http:tolog(code, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, now() - start) sock:send(concat({ REQUEST_STATUCODE_RESPONSE(code), HTTP_DATE(), - 'Origin: *', - 'Allow: GET, POST, PUT, HEAD, OPTIONS', - 'Access-Control-Allow-Origin: *', - 'Access-Control-Allow-Headers: *', - 'Access-Control-Allow-Methods: GET, POST, PUT, HEAD, OPTIONS', - 'Access-Control-Max-Age: 86400', 'Connection: close', 'server: ' .. (server or SERVER), 'Location: ' .. (data or "https://github.com/CandyMi/core_framework"), @@ -484,10 +474,6 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) REQUEST_STATUCODE_RESPONSE(code), HTTP_DATE(), 'Origin: *', 'Allow: GET, POST, PUT, HEAD, OPTIONS', - 'Access-Control-Allow-Origin: *', - 'Access-Control-Allow-Headers: *', - 'Access-Control-Allow-Methods: GET, POST, PUT, HEAD, OPTIONS', - 'Access-Control-Max-Age: 86400', 'server: ' .. (server or SERVER), 'Connection: close', 'Content-Type: ' .. REQUEST_MIME_RESPONSE('html'), @@ -571,10 +557,6 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) header[#header+1] = HTTP_DATE() header[#header+1] = 'Origin: *' header[#header+1] = 'Allow: GET, POST, PUT, HEAD, OPTIONS' - header[#header+1] = 'Access-Control-Allow-Origin: *' - header[#header+1] = 'Access-Control-Allow-Headers: *' - header[#header+1] = 'Access-Control-Allow-Methods: GET, POST, PUT, HEAD, OPTIONS' - header[#header+1] = 'Access-Control-Max-Age: 86400' header[#header+1] = 'server: ' .. (server or SERVER) local Connection = 'Connection: keep-alive' if not HEADER['Connection'] or lower(HEADER['Connection']) == 'close' then From b11abfeeca79c7c2d57f02ccb7b78dde6e7d0ca1 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 2 May 2019 23:08:33 +0800 Subject: [PATCH 019/956] =?UTF-8?q?=E8=B0=83=E6=95=B4lcript=E7=9A=84C?= =?UTF-8?q?=E5=BA=93=E5=BC=95=E7=94=A8=E6=96=B9=E5=BC=8F,=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E4=B8=80=E4=B8=AA=E4=B8=AD=E9=97=B4=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=BD=9C=E4=B8=BA=E6=98=BE=E7=A4=BA=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/Makefile | 4 +- luaclib/src/lcrypt.c | 18 ++++----- lualib/crypt/init.lua | 94 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 11 deletions(-) create mode 100644 lualib/crypt/init.lua diff --git a/luaclib/Makefile b/luaclib/Makefile index 83ac640b..72c96936 100644 --- a/luaclib/Makefile +++ b/luaclib/Makefile @@ -19,7 +19,7 @@ build : $(CC) -o udp.so src/ludp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore $(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore $(CC) -o task.so src/ltask.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore - $(CC) -o crypt.so src/lcrypt.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore + $(CC) -o lcrypt.so src/lcrypt.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore ### 以下为预留第三方库编译位置 ### cd src/lhttpparser && rm -rf *.o *.so && make build # 增加PicoHTTPParser库 cd src/lcjson && rm -rf *.o *.so && make build # 增加cjson库 @@ -32,7 +32,7 @@ rebuild : $(CC) -o udp.so src/ludp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore $(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore $(CC) -o task.so src/ltask.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore - $(CC) -o crypt.so src/lcrypt.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore + $(CC) -o lcrypt.so src/lcrypt.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore ### 以下为预留第三方库编译位置 ### cd src/lhttpparser && rm -rf *.o *.so && make rebuild # 增加PicoHTTPParser库 cd src/lcjson && rm -rf *.o *.so && make rebuild # 增加cjson库 diff --git a/luaclib/src/lcrypt.c b/luaclib/src/lcrypt.c index b3fdcca2..11085430 100644 --- a/luaclib/src/lcrypt.c +++ b/luaclib/src/lcrypt.c @@ -15,7 +15,7 @@ typedef struct { uint32_t count[2]; uint8_t buffer[64]; } SHA1_CTX; - + #define SHA1_DIGEST_SIZE 20 @@ -469,7 +469,7 @@ lhmac_sha1(lua_State *L) { return 1; } -static void +static void des_main_ks( uint32_t SK[32], const uint8_t key[8] ) { int i; uint32_t X, Y, T; @@ -538,7 +538,7 @@ des_main_ks( uint32_t SK[32], const uint8_t key[8] ) { /* DES 64-bit block encryption/decryption */ -static void +static void des_crypt( const uint32_t SK[32], const uint8_t input[8], uint8_t output[8] ) { uint32_t X, Y, T; @@ -762,13 +762,13 @@ static const uint32_t k[64] = { 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1 , 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1 , 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 }; - + // r specifies the per-round shift amounts static const uint32_t r[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21}; - + // leftrotate function definition #define LEFTROTATE(x, c) (((x) << (c)) | ((x) >> (32 - (c)))) @@ -776,7 +776,7 @@ static void digest_md5(uint32_t w[16], uint32_t result[4]) { uint32_t a, b, c, d, f, g, temp; int i; - + a = 0x67452301u; b = 0xefcdab89u; c = 0x98badcfeu; @@ -791,7 +791,7 @@ digest_md5(uint32_t w[16], uint32_t result[4]) { g = (5*i + 1) % 16; } else if (i < 48) { f = b ^ c ^ d; - g = (3*i + 5) % 16; + g = (3*i + 5) % 16; } else { f = c ^ (b | (~d)); g = (7*i) % 16; @@ -1176,7 +1176,7 @@ lxor_str(lua_State *L) { } LUAMOD_API int -luaopen_crypt(lua_State *L) { +luaopen_lcrypt(lua_State *L) { luaL_checkversion(L); static int init = 0; if (!init) { @@ -1205,4 +1205,4 @@ luaopen_crypt(lua_State *L) { }; luaL_newlib(L, crypt_libs); return 1; -} \ No newline at end of file +} diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua new file mode 100644 index 00000000..26b0b720 --- /dev/null +++ b/lualib/crypt/init.lua @@ -0,0 +1,94 @@ +local CRYPT = require "lcrypt" + +local sha1 = CRYPT.sha1 +local xor_str = CRYPT.xor_str + +local randomkey = CRYPT.randomkey +local hashkey = CRYPT.hashkey + +local hmac_sha1 = CRYPT.hmac_sha1 +local hmac_hash = CRYPT.hmac_hash + +local hmac64 = CRYPT.hmac64 +local hmac64_md5 = CRYPT.hmac64_md5 + +local base64encode = CRYPT.base64encode +local base64decode = CRYPT.base64decode + +local hexencode = CRYPT.hexencode +local hexdecode = CRYPT.hexdecode + +local desencode = CRYPT.desencode +local desdecode = CRYPT.desdecode + +local dhsecret = CRYPT.dhsecret +local dhexchange = CRYPT.dhexchange + + +local crypt = {} + +function crypt.sha1(...) + return sha1(...) +end + +function crypt.xor_str (...) + return CRYPT(...) +end + +function crypt.randomkey(...) + return randomkey(...) +end + +function crypt.hashkey (...) + return hashkey(...) +end + +function crypt.hmac_sha1 (...) + return hmac_sha1(...) +end + +function crypt.hmac_hash (...) + return hmac_hash(...) +end + +function crypt.hmac64 (...) + return hmac64(...) +end + +function crypt.hmac64_md5 (...) + return hmac64_md5(...) +end + +function crypt.base64encode (...) + return base64encode(...) +end + +function crypt.base64decode (...) + return base64decode(...) +end + +function crypt.hexencode (...) + return hexencode(...) +end + +function crypt.hexdecode (...) + return hexdecode(...) +end + +function crypt.desencode (...) + return desencode(...) +end + +function crypt.desdecode (...) + return desdecode(...) +end + +function crypt.dhsecret (...) + return dhsecret(...) +end + +function crypt.dhexchange (...) + return dhexchange(...) +end + +return crypt From 168df63cd19c6469f0c6d2e0dd1b622c147b949d Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 2 May 2019 23:10:15 +0800 Subject: [PATCH 020/956] =?UTF-8?q?=E8=B0=83=E6=95=B4lcript=E7=9A=84C?= =?UTF-8?q?=E5=BA=93=E5=BC=95=E7=94=A8=E6=96=B9=E5=BC=8F,=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E4=B8=80=E4=B8=AA=E4=B8=AD=E9=97=B4=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=BD=9C=E4=B8=BA=E6=98=BE=E7=A4=BA=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/crypt/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index 26b0b720..315f0b5f 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -32,7 +32,7 @@ function crypt.sha1(...) end function crypt.xor_str (...) - return CRYPT(...) + return xor_str(...) end function crypt.randomkey(...) From 015bf1de287e10ff3bfcf302c309d80783544231 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 3 May 2019 00:04:54 +0800 Subject: [PATCH 021/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E7=9A=84=E4=B8=80=E4=B8=AA=E6=AD=BB=E5=BE=AA=E7=8E=AFbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/logging/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua index 3e95e272..a83f8f9d 100644 --- a/lualib/logging/init.lua +++ b/lualib/logging/init.lua @@ -81,7 +81,7 @@ local function fmt(...) else tab[#tab+1]= tostring(arg) end - if index == len then + if index >= len then break end index = index + 1 From 7d3870e0359dd61d22a3bc95cef9fed8e01be731 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 3 May 2019 01:11:59 +0800 Subject: [PATCH 022/956] =?UTF-8?q?=E6=96=B0=E5=A2=9ECookie=E6=89=A9?= =?UTF-8?q?=E5=B1=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Cookie.lua | 122 +++++++++++++++++++++++++++++++++++++++ lualib/httpd/init.lua | 17 +++++- lualib/protocol/http.lua | 25 +++++++- 3 files changed, 162 insertions(+), 2 deletions(-) create mode 100644 lualib/httpd/Cookie.lua diff --git a/lualib/httpd/Cookie.lua b/lualib/httpd/Cookie.lua new file mode 100644 index 00000000..84405982 --- /dev/null +++ b/lualib/httpd/Cookie.lua @@ -0,0 +1,122 @@ +local Co = require "internal.Co" +local co_self = Co.self + +local crypt = require "crypt" +local xore_str = crypt.xor_str +local hexencode = crypt.hexencode +local hexdecode = crypt.hexdecode + +local type = type +local assert = assert +local ipairs = ipairs +local os_date = os.date +local os_time = os.time +local concat = table.concat +local splite = string.gmatch + +-- 默认密匙 +local secure = 'http://github.com/candymi/core_framework' + +-- 加密Cookie Value +local function encode_value (value) + return hexencode(xore_str(value, secure)):upper() +end + +-- 解密Cookie Value +local function decode_value (value) + return xore_str(hexdecode(value:lower()), secure) +end + +-- 当前协程注册的cookie +local Cookie = { + client = {}, + server = {}, +} + +function Cookie.setSecure (sec) + if type(sec) == 'string' and sec ~= '' then + secure = sec + end +end + +-- 设置Cookie +function Cookie.setCookie (name, value, expires, notall, https) + assert(type(name) == 'string' and key ~= '', '错误的Cookie Key, 请检查参数有效性') + assert(type(value) == 'string' or type(value) == 'number', '错误的Cookie Value, 请检查参数有效性') + assert(not expires or expires > os_time(), '错误的Cookie Expires, 请检查参数有效性') + local co = co_self() + local cs = Cookie.server[co] + if not cs then + cs = {} + Cookie.server[co] = cs + end + cs[#cs+1] = { + name = name, + value = value, + expires = expires, + path = '/', + httponly = notall and 'HttpOnly', + secure = https and 'Secure' + } +end + +-- 获取置顶Cookie字段 +function Cookie.getCookie (name) + local co = co_self + local cs = Cookie.client[co] + local value = cs.client[name] + if not value then + return + end + return decode_value(value) +end + +-- 对cookie进行反序列化 +function Cookie.deserialization (cs) + if type(cs) ~= 'string' or cs == '' then + return + end + local co = co_self() + local Cookies = {} + for name, value in splite(cs, '([^ ;]+)=([^ ;]+)') do + Cookies[name] = value + end + Cookie.client[co] = Cookies + -- local log = require 'logging' + -- local Log = log:new() + -- Log:DEBUG('反序列化后的cookie', Cookies) +end + +-- 对cookie进行序列化 +function Cookie.serialization () + local co = co_self() + local cs = Cookie.server[co] + if not cs then + return {} + end + local tab = {} + for _, cookie in ipairs(cs) do + local t = {} + t[#t+1] = concat({cookie.name, '=', encode_value(cookie.value)}) + t[#t+1] = cookie.expires and concat({'expires', '=', os_date("%a, %d %b %Y %X GMT", cookie.expires)}) + t[#t+1] = concat({'path', '=', cookie.path}) + t[#t+1] = cookie.httponly + if cookie.httponly then + t[#t+1] = cookie.secure + end + tab[#tab+1] = 'Set-Cookie: '..concat(t, "; ") + end + return tab +end + +-- 清理Cookie +function Cookie.clear () + local co = co_self() + Cookie.server[co] = nil + Cookie.client[co] = nil + -- local log = require 'logging' + -- local Log = log:new() + -- Log:DEBUG('反序列化后的cookie', Cookie) +end + +return Cookie diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua index 58633bc1..ab008f16 100644 --- a/lualib/httpd/init.lua +++ b/lualib/httpd/init.lua @@ -97,12 +97,25 @@ function httpd:server_name(server) end end +-- 连接保持时间 function httpd:timeout(timeout) if type(timeout) == "number" then self.__timeout = timeout end end +-- 是否记录解析cookie +function httpd:enable_cookie () + self.__cookie = true +end + +-- 设置Cookie加密Key +function httpd:cookie_secure (secure) + if type(secure) == 'string' and secure ~= '' then + self.__cookie_secure = secure + end +end + -- 注册静态文件读取路径, foldor是一个目录, ttl是静态文件缓存周期 function httpd:static(foldor, ttl) if foldor and type(foldor) == 'string' and #foldor > 0 then @@ -126,8 +139,10 @@ end -- 记录日志到文件 function httpd:log(path) - self.logpath = path or "cf-httpd.log" + if type(path) == 'string' and path ~= '' then + self.logpath = path self.log = log:new({ path = self.logpath }) + end end function httpd:tolog(code, path, ip, ip_list, method, speed) diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index 9e165c83..21f5889e 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -10,6 +10,7 @@ local sha1 = crypt.sha1 local base64 = crypt.base64encode local now = sys.now local DATE = os.date +local insert = table.insert local form = require "httpd.Form" local FILE_TYPE = form.FILE @@ -17,6 +18,13 @@ local ARGS_TYPE = form.ARGS local form_multipart = form.multipart local form_urlencode = form.urlencode +local Cookie = require "httpd.Cookie" +local clCookie = Cookie.clear -- 清理 +local secCookie = Cookie.setSecure -- 设置Cookie加密字段 +local seCookie = Cookie.serialization -- 序列化 +local deCookie = Cookie.deserialization -- 反序列化 + + local Router = require "httpd.Router" local ROUTE_FIND = Router.find local ROUTE_REGISTERY = Router.registery @@ -373,10 +381,13 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) local ttl = http.ttl local server = http.__server local timeout = http.__timeout + local cookie = http.__cookie + local cookie_secure = http.__cookie_secure local before_func = http._before_func local max_path_size = http.__max_path_size local max_header_size = http.__max_header_size local max_body_size = http.__max_body_size + secCookie(cookie_secure) -- 如果需要 local sock = tcp:new():set_fd(fd):timeout(timeout or 15) while 1 do local buf = sock:recv(1024) @@ -499,6 +510,10 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) local ok, body, static, statucode if typ == HTTP_PROTOCOL.API or typ == HTTP_PROTOCOL.USE then + -- 如果httpd开启了记录Cookie字段, 则每次尝试是否deCookie + if cookie and typ == HTTP_PROTOCOL.USE then + deCookie(content['headers']["Cookie"]) + end if type(cls) == "table" then local method = cls[lower(METHOD)] if not method or type(method) ~= 'function' then -- 注册的路由未实现这个方法 @@ -510,6 +525,14 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) else ok, body = pcall(cls, content) end + -- 如果httpd开启了记录Cookie字段, 则每次尝试是否需要seCookie + if cookie and typ == HTTP_PROTOCOL.USE then + local Cookies = seCookie() + for _, Cookie in ipairs(Cookies) do + header[#header+1] = Cookie + end + clCookie() + end if not ok then Log:ERROR(body) statucode = 500 @@ -517,7 +540,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) return sock:close() end statucode = 200 - header[#header+1] = REQUEST_STATUCODE_RESPONSE(statucode) + insert(header, 1, REQUEST_STATUCODE_RESPONSE(statucode)) elseif typ == HTTP_PROTOCOL.WS then local ok, msg = pcall(Switch_Protocol, http, cls, sock, HEADER, METHOD, VERSION, PATH, HEADER['X-Real-IP'] or ipaddr, start) if not ok then From 0f86037837ad5af7cd11f84bfc22f06f214b470a Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 3 May 2019 01:44:09 +0800 Subject: [PATCH 023/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=93=E9=97=A8?= =?UTF-8?q?=E7=9A=84=E6=97=A5=E5=BF=97=E7=9B=AE=E5=BD=95,=20=E6=89=80?= =?UTF-8?q?=E6=9C=89=E5=86=85=E9=83=A8=E6=97=A5=E5=BF=97=E4=B8=8E=E7=94=A8?= =?UTF-8?q?=E6=88=B7dump=E5=87=BA=E7=9A=84=E5=B0=86=E9=83=BD=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E5=9C=A8=E8=BF=99=E9=87=8C.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- logs/README.md | 3 +++ lualib/Cache/init.lua | 2 +- lualib/DB/init.lua | 2 +- lualib/MQ/init.lua | 2 +- lualib/httpd/Router.lua | 2 +- lualib/httpd/init.lua | 5 ++--- lualib/internal/Co.lua | 2 +- lualib/internal/TCP.lua | 2 +- lualib/internal/Timer.lua | 2 +- 9 files changed, 12 insertions(+), 10 deletions(-) create mode 100644 logs/README.md diff --git a/logs/README.md b/logs/README.md new file mode 100644 index 00000000..55dceb52 --- /dev/null +++ b/logs/README.md @@ -0,0 +1,3 @@ +## 日志文件夹 + +> 日志将会自动按照日期切割 diff --git a/lualib/Cache/init.lua b/lualib/Cache/init.lua index 2192da3c..4506d272 100644 --- a/lualib/Cache/init.lua +++ b/lualib/Cache/init.lua @@ -17,7 +17,7 @@ local upper = string.upper local lower = string.lower local splite = string.gmatch -local Log = log:new() +local Log = log:new({ dump = true, path = 'Cache'}) -- 注册命令 local commands = { diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index 7785190a..124f943b 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -3,7 +3,7 @@ local timer = require "internal.Timer" local co = require "internal.Co" local class = require "class" local log = require "logging" -local Log = log:new() +local Log = log:new({ dump = true, path = 'DB'}) local co_self = co.self local co_wait = co.wait diff --git a/lualib/MQ/init.lua b/lualib/MQ/init.lua index 9e426776..0e00c984 100644 --- a/lualib/MQ/init.lua +++ b/lualib/MQ/init.lua @@ -4,7 +4,7 @@ local Timer = require "internal.Timer" local mqtt = require "protocol.mqtt" local redis = require "protocol.redis" -local Log = log:new() +local Log = log:new({ dump = true, path = 'MQ' }) local type = type local math = math diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index 1de99f2f..4c42c06c 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -1,5 +1,5 @@ local log = require "logging" -local Log = log:new() +local Log = log:new({dump = true, path = 'httpd-Router'}) local math = math local string = string diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua index ab008f16..ceb1f6f6 100644 --- a/lualib/httpd/init.lua +++ b/lualib/httpd/init.lua @@ -140,13 +140,12 @@ end -- 记录日志到文件 function httpd:log(path) if type(path) == 'string' and path ~= '' then - self.logpath = path - self.log = log:new({ path = self.logpath }) + self.log = log:new({ dump = true, path = path }) end end function httpd:tolog(code, path, ip, ip_list, method, speed) - if self.logpath then + if self.log then local log = fmt("[%s] - %s - %s - %s - %s - %d - req_time: %0.6f/Sec\n", os_date("%Y/%m/%d %H:%M:%S"), ip, ip_list, path, method, code, speed) self.log:dump(log) end diff --git a/lualib/internal/Co.lua b/lualib/internal/Co.lua index 191069f4..c8a9a136 100644 --- a/lualib/internal/Co.lua +++ b/lualib/internal/Co.lua @@ -1,7 +1,7 @@ local log = require "logging" local task = require "task" -local Log = log:new() +local Log = log:new({ dump = true, path = 'internal-Co' }) local type = type local assert = assert diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index d1e0b284..fd09f8e8 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -4,7 +4,7 @@ local co = require "internal.Co" local class = require "class" local tcp = require "tcp" local log = require "logging" -local Log = log:new() +local Log = log:new({ dump = true, path = 'internal-TCP' }) local split = string.sub local insert = table.insert diff --git a/lualib/internal/Timer.lua b/lualib/internal/Timer.lua index 9966aeb3..61a337a9 100644 --- a/lualib/internal/Timer.lua +++ b/lualib/internal/Timer.lua @@ -1,7 +1,7 @@ local co = require "internal.Co" local ti = require "timer" local log = require "logging" -local Log = log:new() +local Log = log:new({ dump = true, path = 'internal-Timer' }) local type = type local pcall = pcall From d5558853f14efc2ec0fc6228bbb701b771a4cec1 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 3 May 2019 01:45:48 +0800 Subject: [PATCH 024/956] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E6=96=B9=E5=BC=8F=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/logging/init.lua | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua index a83f8f9d..c6171c6d 100644 --- a/lualib/logging/init.lua +++ b/lualib/logging/init.lua @@ -1,5 +1,4 @@ -- logging 核心配置 - local class = require "class" local system = require "system" local now = system.now @@ -94,12 +93,11 @@ function FMT (where, level, ...) return concat({ fmt_Y_m_d_H_M_S(), where, level, ':', fmt(...), '\n'}, ' ') end -local paths = {} - local Log = class("Log") function Log:ctor (opt) if type(opt) == 'table' then + self.to_dump = opt.dump self.path = opt.path self.now = Y_m_d() end @@ -129,21 +127,20 @@ function Log:WARN (...) self:dump(FMT(debuginfo(), "[WARN]", ...)) end +-- dump日志到磁盘 function Log:dump(log) - local file = paths[self.path] - if type(self.path) == 'string' and self.path ~= '' then - if not file then - file = io_open(self.path..'_'..self.now..'.log', 'a') - paths[self.path] = file + if self.to_dump and self.path then + if not self.file then + self.file, err = io_open('logs/'..self.path..'_'..self.now..'.log', 'a') else - if Y_m_d() ~= self.now then - file:close() - self.now = Y_m_d() - file = io_open(self.path..'_'..self.now..'.log', 'a') - paths[self.path] = file + local now = Y_m_d() + if now ~= self.now then + self.file:close() + self.now = now + self.file = io_open('logs/'..self.path..'_'..self.now..'.log', 'a') end end - file:write(log):flush() + self.file:write(log):flush() end end From 68297d8a925be1fec9ac3b38de6ec9ff8fa532f2 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 3 May 2019 02:14:18 +0800 Subject: [PATCH 025/956] =?UTF-8?q?=E8=A1=A5=E5=85=85protocol=E5=86=85?= =?UTF-8?q?=E7=9A=84logging=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/init.lua | 13 ++++++------- lualib/protocol/dns.lua | 3 ++- lualib/protocol/http.lua | 2 +- lualib/protocol/mqtt/init.lua | 2 +- lualib/protocol/redis.lua | 2 +- lualib/protocol/websocket/server.lua | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua index ceb1f6f6..0f493e53 100644 --- a/lualib/httpd/init.lua +++ b/lualib/httpd/init.lua @@ -140,24 +140,23 @@ end -- 记录日志到文件 function httpd:log(path) if type(path) == 'string' and path ~= '' then - self.log = log:new({ dump = true, path = path }) + self.logging = log:new({ dump = true, path = path }) end end function httpd:tolog(code, path, ip, ip_list, method, speed) - if self.log then - local log = fmt("[%s] - %s - %s - %s - %s - %d - req_time: %0.6f/Sec\n", os_date("%Y/%m/%d %H:%M:%S"), ip, ip_list, path, method, code, speed) - self.log:dump(log) + if self.logging then + self.logging:dump(fmt("[%s] - %s - %s - %s - %s - %d - req_time: %0.6f/Sec\n", os_date("%Y/%m/%d %H:%M:%S"), ip, ip_list, path, method, code, speed)) end print(fmt("[%s] - %s - %s - %s - %s - %d - req_time: %0.6f/Sec", os_date("%Y/%m/%d %H:%M:%S"), ip, ip_list, path, method, code, speed)) end -- 监听请求 function httpd:listen(ip, port) - return self.IO:listen(ip, port, function (fd, ipaddr) - ipaddr = match(ipaddr, '^::[f]+:(.+)') or ipaddr - return EVENT_DISPATCH(fd, ipaddr, self) + self.IO:listen(ip, port, function (fd, ipaddr) + return EVENT_DISPATCH(fd, match(ipaddr, '^::[f]+:(.+)') or ipaddr, self) end) + return self end -- 正确的运行方式 diff --git a/lualib/protocol/dns.lua b/lualib/protocol/dns.lua index 7bb920cb..23b49820 100644 --- a/lualib/protocol/dns.lua +++ b/lualib/protocol/dns.lua @@ -2,6 +2,7 @@ local UDP = require "internal.UDP" local co = require "internal.Co" local sys = require "sys" local log = require "logging" +local Log = log:new({ dump = true, path = 'protocol-dns'}) local prefix = '::ffff:' @@ -225,7 +226,7 @@ local function dns_query(domain) break end if type(len) == "string" then - log.info("正在解析["..domain.."]:"..len) + Log:INFO("正在解析["..domain.."]:"..len) end end if not len or len < LIMIT_HEADER_LEN then diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index 21f5889e..245cdf6c 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -3,7 +3,7 @@ local sys = require "system" local tcp = require "internal.TCP" local wsserver = require "protocol.websocket.server" -local Log = log:new() +local Log = log:new({ dump = true, path = 'protocol-http'}) local crypt = require "crypt" local sha1 = crypt.sha1 diff --git a/lualib/protocol/mqtt/init.lua b/lualib/protocol/mqtt/init.lua index b9e6b777..d4e2e2de 100644 --- a/lualib/protocol/mqtt/init.lua +++ b/lualib/protocol/mqtt/init.lua @@ -23,7 +23,7 @@ local make_packet4 = protocol4.make_packet local parse_packet4 = protocol4.parse_packet local connack_return_code = protocol4.connack_return_code -local Log = log:new() +local Log = log:new({ dump = true, path = 'protocol-MQTT'}) -- cache to locals local type = type diff --git a/lualib/protocol/redis.lua b/lualib/protocol/redis.lua index 1b2310cf..17d2710f 100644 --- a/lualib/protocol/redis.lua +++ b/lualib/protocol/redis.lua @@ -4,7 +4,7 @@ local Co = require "internal.Co" local tcp = require "internal.TCP" local concat = table.concat -local Log = log:new() +local Log = log:new({ dump = true, path = 'protocol-redis' }) local co_spwan = Co.spwan diff --git a/lualib/protocol/websocket/server.lua b/lualib/protocol/websocket/server.lua index b0098e45..1a54f5dc 100644 --- a/lualib/protocol/websocket/server.lua +++ b/lualib/protocol/websocket/server.lua @@ -5,7 +5,7 @@ local wbproto = require "protocol.websocket.protocol" local _recv_frame = wbproto.recv_frame local _send_frame = wbproto.send_frame -local Log = log:new() +local Log = log:new({ dump = true, path = 'protocol-websocket-server'}) local co_self = co.self local co_wait = co.wait From bee9359bad1c51c883c0cb2473c5c40f67c420c7 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 3 May 2019 02:25:41 +0800 Subject: [PATCH 026/956] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=A4=B9=E4=B8=8E=E6=90=9C=E7=B4=A2=E6=96=87=E4=BB=B6=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E4=BC=98=E5=85=88=E7=BA=A7=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core.c b/src/core.c index c3b63398..89211fa8 100644 --- a/src/core.c +++ b/src/core.c @@ -142,7 +142,7 @@ static void * L_ALLOC(void *ud, void *ptr, size_t osize, size_t nsize){ // 为lua内存hook注入日志; /* 用户自定义数据 */ - (void)ud; (void)osize; + (void)ud; (void)osize; if (nsize == 0) return xfree(ptr), NULL; for (;;) { void *newptr = xrealloc(ptr, nsize); @@ -170,10 +170,10 @@ init_lua_libs(lua_State *L){ /* 注入lua搜索域 */ lua_getglobal(L, "package"); - lua_pushliteral(L, "./script/?.lua;./script/?/init.lua;./lualib/?.lua;./lualib/?/init.lua;./?/init.lua"); + lua_pushliteral(L, "lualib/?.lua;lualib/?/init.lua;script/?.lua;script/?/init.lua;"); lua_setfield(L, 1, "path"); - lua_pushliteral(L, "./luaclib/?.so;./luaclib/?/init.so;"); + lua_pushliteral(L, "luaclib/?.so;luaclib/?/init.so;"); lua_setfield(L, 1, "cpath"); lua_settop(L, 0); From e08003426c82206e75212addfc31bfece41fea50 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 3 May 2019 08:26:37 +0800 Subject: [PATCH 027/956] =?UTF-8?q?=E8=B0=83=E6=95=B4=E8=B7=AF=E7=94=B1?= =?UTF-8?q?=E6=9F=A5=E6=89=BE=E7=AE=97=E6=B3=95,=20=E9=80=82=E5=BA=94?= =?UTF-8?q?=E9=9D=99=E6=80=81=E6=96=87=E4=BB=B6=E6=90=9C=E7=B4=A2=E8=B7=AF?= =?UTF-8?q?=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Router.lua | 37 +++++++++++++++++++++++++++++++++---- lualib/httpd/init.lua | 40 ++++++++++++++++------------------------ lualib/protocol/http.lua | 4 ++-- 3 files changed, 51 insertions(+), 30 deletions(-) diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index 4c42c06c..579eb234 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -3,6 +3,7 @@ local Log = log:new({dump = true, path = 'httpd-Router'}) local math = math local string = string +local split = string.sub local find = string.find local match = string.match local splite = string.gmatch @@ -13,6 +14,9 @@ local ipairs = ipairs local tonumber = tonumber local tostring = tostring local toint = math.tointeger +local io_open = io.open + +local load_file local Router = { API = 1, @@ -23,6 +27,8 @@ local Router = { local routes = {} -- 存储路由 +local static = {} -- 静态文件路由 + local typ = { int = toint, float = tonumber, @@ -38,6 +44,16 @@ local function splite_route(route) return tab end +local function registery_static (prefix, route_type) + if route_type == Router.STATIC then + if not next(static) then + static.prefix = prefix + static.type = route_type + end + return + end +end + local function registery_router(route, class, route_type) local tab = splite_route(route) if route == '/' then -- 如果注册路由为'/', 则转义为:'' @@ -64,18 +80,26 @@ local function find_route(path) for index, route in ipairs(routes) do local r = tab[index] if not r then - return false + break end local t = route[r] if type(t) == 'table' then if #tab == index then return t.class, t.type end - if t.type == Router.STATIC then - return t.class, t.type - end end end + local prefix, type = static.prefix, static.type + load_file = load_file or function (path) + local f, error = io_open(prefix..path, 'rb') + if not f then + return + end + local file = f:read('*a') + f:close() + return file, match(path, '.+%.([%a]+)') + end + return load_file, type end -- 查找路由 @@ -83,6 +107,11 @@ function Router.find(path) return find_route(path) end +-- 注册静态文件查找路径 +function Router.static (...) + return registery_static(...) +end + -- 注册路由 function Router.registery(route, class, route_type) if type(route) ~= 'string' or route == '' then -- 过滤错误的路由输入 diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua index 0f493e53..0e462539 100644 --- a/lualib/httpd/init.lua +++ b/lualib/httpd/init.lua @@ -1,4 +1,5 @@ local HTTP = require "protocol.http" +local HTTPD = require "httpd.Router" local tcp = require "internal.TCP" local class = require "class" local sys = require "system" @@ -16,36 +17,37 @@ local os_date = os.date -- 请求解析 local EVENT_DISPATCH = HTTP.EVENT_DISPATCH --- 注册HTTP路由 -local HTTP_ROUTE_REGISTERY = HTTP.ROUTE_REGISTERY +-- 注册HTTP路由与静态文件 +local HTTP_STATIC_REGISTERY = HTTPD.static +local HTTP_ROUTE_REGISTERY = HTTPD.registery + local httpd = class("httpd") function httpd:ctor(opt) - self.API = HTTP.API - self.USE = HTTP.USE + self.API = HTTPD.API + self.USE = HTTPD.USE self.IO = tcp:new() end -- 用来注册WebSocket对象 function httpd:ws(route, class) if route and type(class) == "table" then - -- HTTP_ROUTE_REGISTERY(self.routes, route, class, HTTP.WS) - HTTP_ROUTE_REGISTERY(route, class, HTTP.WS) + HTTP_ROUTE_REGISTERY(route, class, HTTPD.WS) end end -- 用来注册接口 function httpd:api(route, class) if route and (type(class) == "table" or type(class) == "function")then - HTTP_ROUTE_REGISTERY(route, class, HTTP.API) + HTTP_ROUTE_REGISTERY(route, class, HTTPD.API) end end -- 用来注册普通路由 function httpd:use(route, class) if route and (type(class) == "table" or type(class) == "function") then - HTTP_ROUTE_REGISTERY(route, class, HTTP.USE) + HTTP_ROUTE_REGISTERY(route, class, HTTPD.USE) end end @@ -118,23 +120,13 @@ end -- 注册静态文件读取路径, foldor是一个目录, ttl是静态文件缓存周期 function httpd:static(foldor, ttl) - if foldor and type(foldor) == 'string' and #foldor > 0 then - ttl = math.tointeger(ttl) - if ttl and ttl > 0 then - self.ttl = ttl - end - HTTP_ROUTE_REGISTERY('./'..foldor, function (path) - if path then - local FILE = io_open(path, "rb") - if not FILE then - return - end - local file = FILE:read('*a') - FILE:close() - return file, match(path, '.+%.([%a]+)') - end - end, HTTP.STATIC) + if not self.foldor then + self.foldor = foldor or 'static' + if ttl and ttl > 0 then + self.ttl = ttl end + return HTTP_STATIC_REGISTERY(self.foldor, HTTPD.STATIC) + end end -- 记录日志到文件 diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index 245cdf6c..b1851b03 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -553,9 +553,9 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) local path = PATH local pos, _ = find(PATH, '%?') if pos then - path = split(path, 1, pos - 1) + path = split(PATH, 1, pos - 1) end - ok, body, file_type = pcall(cls, './'..path) + ok, body, file_type = pcall(cls, path) if not ok then Log:ERROR(body) statucode = 500 From 5732b3948ae924394e9f596a877e1ac2410398e4 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 3 May 2019 11:03:53 +0800 Subject: [PATCH 028/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8DCookie=E7=9A=84?= =?UTF-8?q?=E4=B8=80=E4=BA=9B=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Cookie.lua | 17 +++++++++++------ lualib/protocol/http.lua | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lualib/httpd/Cookie.lua b/lualib/httpd/Cookie.lua index 84405982..5eec6a06 100644 --- a/lualib/httpd/Cookie.lua +++ b/lualib/httpd/Cookie.lua @@ -60,24 +60,29 @@ function Cookie.setCookie (name, value, expires, notall, https) } end --- 获取置顶Cookie字段 +-- 获取指定Cookie字段 function Cookie.getCookie (name) - local co = co_self + local co = co_self() local cs = Cookie.client[co] - local value = cs.client[name] + local value = cs[name] if not value then return end return decode_value(value) end +function Cookie.delCookie (name) + return Cookie.setCookie(name, ' ', os_time() + 1) +end + -- 对cookie进行反序列化 function Cookie.deserialization (cs) + local co = co_self() + local Cookies = {} if type(cs) ~= 'string' or cs == '' then + Cookie.client[co] = Cookies return end - local co = co_self() - local Cookies = {} for name, value in splite(cs, '([^ ;]+)=([^ ;]+)') do Cookies[name] = value end @@ -110,7 +115,7 @@ function Cookie.serialization () end -- 清理Cookie -function Cookie.clear () +function Cookie.clean () local co = co_self() Cookie.server[co] = nil Cookie.client[co] = nil diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index b1851b03..b6a73d2c 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -19,7 +19,7 @@ local form_multipart = form.multipart local form_urlencode = form.urlencode local Cookie = require "httpd.Cookie" -local clCookie = Cookie.clear -- 清理 +local clCookie = Cookie.clean -- 清理 local secCookie = Cookie.setSecure -- 设置Cookie加密字段 local seCookie = Cookie.serialization -- 序列化 local deCookie = Cookie.deserialization -- 反序列化 From 7fa4a34f1b8feb5edda5e62810dc93c8a2e2a79e Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 3 May 2019 13:51:05 +0800 Subject: [PATCH 029/956] =?UTF-8?q?=E5=B0=86=E8=B7=AF=E7=94=B1=E6=9F=A5?= =?UTF-8?q?=E6=89=BE=E8=B0=83=E6=95=B4=E4=B8=BAhash=20key-value=E6=98=A0?= =?UTF-8?q?=E5=B0=84=E6=9F=A5=E6=89=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Router.lua | 65 +++++++++++++---------------------------- 1 file changed, 21 insertions(+), 44 deletions(-) diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index 579eb234..09520032 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -1,6 +1,9 @@ local log = require "logging" local Log = log:new({dump = true, path = 'httpd-Router'}) +local crypt = require "crypt" +local hexencode = crypt.hexencode + local math = math local string = string local split = string.sub @@ -13,10 +16,9 @@ local assert = assert local ipairs = ipairs local tonumber = tonumber local tostring = tostring -local toint = math.tointeger local io_open = io.open - -local load_file +local concat = table.concat +local toint = math.tointeger local Router = { API = 1, @@ -35,59 +37,34 @@ local typ = { string = tostring, } --- 路由分割: /a/b/c/d = {'a', 'b', 'c', 'd'} -local function splite_route(route) +-- 分割路径后进行hex, 得到key后一次查表即可完成 +local function hex_route(route) local tab = {} for r in splite(route, '/([^/?]+)') do tab[#tab + 1] = r end - return tab + return hexencode(concat(tab)) end local function registery_static (prefix, route_type) - if route_type == Router.STATIC then - if not next(static) then - static.prefix = prefix - static.type = route_type - end - return + if not next(static) then + static.prefix = prefix + static.type = route_type end + return end -local function registery_router(route, class, route_type) - local tab = splite_route(route) - if route == '/' then -- 如果注册路由为'/', 则转义为:'' - tab = {''} - end - for index, r in ipairs(tab) do - if not routes[index] then - routes[index] = {} - end - if not routes[index][r] then - routes[index][r] = true - end - if #tab == index then - routes[index][r] = {class = class, type = route_type} - end - end +local load_file + +local function registery_router (route, class, route_type) + routes[hex_route(route)] = {class = class, type = route_type} end -local function find_route(path) - local tab = splite_route(path) - if #tab == 0 then -- 如果路由为/[/]{0, n}, 则转义为: '' - tab[1] = '' - end - for index, route in ipairs(routes) do - local r = tab[index] - if not r then - break - end - local t = route[r] - if type(t) == 'table' then - if #tab == index then - return t.class, t.type - end - end +local function find_route (path) + local hex = hex_route(path) + local t = routes[hex] + if t then + return t.class, t.type end local prefix, type = static.prefix, static.type load_file = load_file or function (path) From 9cffd9f58d9352dfe7023e532c8983fb7744e391 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 4 May 2019 15:54:42 +0800 Subject: [PATCH 030/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=9D=99=E6=80=81?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E8=B7=A8=E5=9F=9F=E9=BB=98=E8=AE=A4=E6=94=AF?= =?UTF-8?q?=E6=8C=81i?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index b6a73d2c..261230d7 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -576,6 +576,8 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) else static = fmt('Content-Type: %s', conten_type) end + -- 如果是静态文件, 增加默认跨域访问支持 + header[#header+1] = "Access-Control-Allow-Origin: *" end header[#header+1] = HTTP_DATE() header[#header+1] = 'Origin: *' From 0d57484debb6bc1acf109469a1adcb87ab4ae3c4 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 5 May 2019 02:31:15 +0800 Subject: [PATCH 031/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=9C=AA=E6=B3=A8?= =?UTF-8?q?=E5=86=8Chttpd=20static=E4=B9=9F=E6=9F=A5=E6=89=BE=E8=B7=AF?= =?UTF-8?q?=E7=94=B1=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Router.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index 09520032..99d7ed9e 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -67,6 +67,9 @@ local function find_route (path) return t.class, t.type end local prefix, type = static.prefix, static.type + if not prefix and not type then + return + end load_file = load_file or function (path) local f, error = io_open(prefix..path, 'rb') if not f then From 3542e31022f4eff9f66159518da8c9b8f9f9e3e7 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 7 May 2019 14:20:48 +0800 Subject: [PATCH 032/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0lua=E7=9A=84urlencode?= =?UTF-8?q?=E4=B8=8Eurldecode=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/url/init.lua | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 lualib/url/init.lua diff --git a/lualib/url/init.lua b/lualib/url/init.lua new file mode 100644 index 00000000..d5d42c9f --- /dev/null +++ b/lualib/url/init.lua @@ -0,0 +1,19 @@ +local tonumber = tonumber +local byte = string.byte +local char = string.char +local fmt = string.format +local spliter = string.gsub + +local url = {} + +-- urlencode编码 +function url.encode(s) + return spliter(spliter(s, "([^%w%.%- ])", function(c) return fmt("%%%02X", byte(c)) end), " ", "+") +end + +-- urlencode解码 +function url.decode(s) + return spliter(s, '%%(%x%x)', function(h) return char(tonumber(h, 16)) end) +end + +return url From 0b4e2901a9faeed20c158040d690e5a9168bd453 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 10 May 2019 14:28:07 +0800 Subject: [PATCH 033/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E5=88=A4=E6=96=AD=E8=81=94=E5=90=88=E6=9F=A5=E8=AF=A2=E4=B8=8D?= =?UTF-8?q?=E9=9C=80=E8=A6=81format?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/DB/init.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index 124f943b..8be58a75 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -118,7 +118,7 @@ end -- 将['field', '=', 'a'] 格式化为 field = a -- 将['field', 'NOT', 'IN', '(1, 2, 3)'] 格式化为 field NOT IN (1, 2, 3) local function format(t) - return fmt(concat({rep("%s ", #t-1), "%s"}), unpack(t)) + return fmt(rep("'%s' ", #t), unpack(t)) end local function format_value1(t) @@ -220,7 +220,7 @@ local function where(query, conditions) insert(CONDITIONS, format({condition[1], con2, LEFT..format_value2(condition[3], c)..RIGHT})) elseif con2 == IS then insert(CONDITIONS, concat(condition, " ")) - elseif find(condition[3], condition[1]) then + elseif find(condition[3], condition[1]) or find(condition[3], '`.') then insert(CONDITIONS, concat(condition, " ")) else insert(CONDITIONS, format_value3(condition)) @@ -444,6 +444,7 @@ function DB:query(query) return nil, "DB尚未初始化" end assert(type(query) == 'string' and query ~= '' , "原始SQL类型错误(query):"..tostring(query)) + -- Log:DEBUG(query) local db, ret, err while 1 do db = pop_db(self) From cf31bfe3b5aac1c2a19dd1941f347ec5a84e0c2d Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 10 May 2019 23:07:07 +0800 Subject: [PATCH 034/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AAHTT?= =?UTF-8?q?P=E5=A4=B4=E9=83=A8=E7=9A=84=E9=97=AE=E9=A2=98,=20=E4=B8=BA?= =?UTF-8?q?=E6=89=80=E6=9C=89=E6=83=85=E5=86=B5=E5=A2=9E=E5=8A=A0body?= =?UTF-8?q?=E9=95=BF=E5=BA=A6=E6=8F=90=E5=8D=87=E5=85=BC=E5=AE=B9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index 261230d7..8652f9eb 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -590,15 +590,15 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) header[#header+1] = Connection if body then if type(body) == 'string' then - if #body >= 1 then - header[#header+1] = 'Transfer-Encoding: identity' - header[#header+1] = 'Content-Length: '.. #body - end + header[#header+1] = 'Transfer-Encoding: identity' + header[#header+1] = 'Content-Length: '.. #body else Log:WARN('response body not a string type.'..'('..tostring(body)..')') + header[#header+1] = 'Content-Length: 0' body = '' end else + header[#header+1] = 'Content-Length: 0' body = '' end if typ == HTTP_PROTOCOL.API then From a9ca946ec127a557695f19f8a6422cc03e60c6e9 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 14 May 2019 00:40:16 +0800 Subject: [PATCH 035/956] =?UTF-8?q?=E4=B8=BAcrypt=E5=A2=9E=E5=8A=A0crc-32?= =?UTF-8?q?=E4=B8=8Ecrc-64=E6=96=B9=E6=B3=95,=20=E5=BA=94=E7=94=A8?= =?UTF-8?q?=E4=B8=80=E4=BA=9B=E7=89=B9=E6=AE=8A=E5=9C=BA=E6=99=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt.c | 228 ++++++++++++++++++++++++++++++++++++++++++ lualib/crypt/init.lua | 10 ++ 2 files changed, 238 insertions(+) diff --git a/luaclib/src/lcrypt.c b/luaclib/src/lcrypt.c index 11085430..6e2a8bcb 100644 --- a/luaclib/src/lcrypt.c +++ b/luaclib/src/lcrypt.c @@ -43,6 +43,205 @@ static void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]); #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); +/* CRC32 TAB */ +static uint32_t CRC32[] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, + 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, + 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, + 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, + 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, + 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, + 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, + 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, + 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, + 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, + 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, + 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, + 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, + 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, + 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, + 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, + 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, + 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, + 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, + 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, + 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, + 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, + 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, + 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, + 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, + 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, + 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, + 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, + 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, + 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, + 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, + 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, + 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, + 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, + 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, + 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, + 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, + 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, + 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, + 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, + 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, + 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, + 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, + 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, + 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, + 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, + 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, + 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, + 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, + 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, + 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, + 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL +}; + +/* CRC64 TAB */ +static uint64_t CRC64[] = { + 0x0000000000000000, 0x7ad870c830358979, + 0xf5b0e190606b12f2, 0x8f689158505e9b8b, + 0xc038e5739841b68f, 0xbae095bba8743ff6, + 0x358804e3f82aa47d, 0x4f50742bc81f2d04, + 0xab28ecb46814fe75, 0xd1f09c7c5821770c, + 0x5e980d24087fec87, 0x24407dec384a65fe, + 0x6b1009c7f05548fa, 0x11c8790fc060c183, + 0x9ea0e857903e5a08, 0xe478989fa00bd371, + 0x7d08ff3b88be6f81, 0x07d08ff3b88be6f8, + 0x88b81eabe8d57d73, 0xf2606e63d8e0f40a, + 0xbd301a4810ffd90e, 0xc7e86a8020ca5077, + 0x4880fbd87094cbfc, 0x32588b1040a14285, + 0xd620138fe0aa91f4, 0xacf86347d09f188d, + 0x2390f21f80c18306, 0x594882d7b0f40a7f, + 0x1618f6fc78eb277b, 0x6cc0863448deae02, + 0xe3a8176c18803589, 0x997067a428b5bcf0, + 0xfa11fe77117cdf02, 0x80c98ebf2149567b, + 0x0fa11fe77117cdf0, 0x75796f2f41224489, + 0x3a291b04893d698d, 0x40f16bccb908e0f4, + 0xcf99fa94e9567b7f, 0xb5418a5cd963f206, + 0x513912c379682177, 0x2be1620b495da80e, + 0xa489f35319033385, 0xde51839b2936bafc, + 0x9101f7b0e12997f8, 0xebd98778d11c1e81, + 0x64b116208142850a, 0x1e6966e8b1770c73, + 0x8719014c99c2b083, 0xfdc17184a9f739fa, + 0x72a9e0dcf9a9a271, 0x08719014c99c2b08, + 0x4721e43f0183060c, 0x3df994f731b68f75, + 0xb29105af61e814fe, 0xc849756751dd9d87, + 0x2c31edf8f1d64ef6, 0x56e99d30c1e3c78f, + 0xd9810c6891bd5c04, 0xa3597ca0a188d57d, + 0xec09088b6997f879, 0x96d1784359a27100, + 0x19b9e91b09fcea8b, 0x636199d339c963f2, + 0xdf7adabd7a6e2d6f, 0xa5a2aa754a5ba416, + 0x2aca3b2d1a053f9d, 0x50124be52a30b6e4, + 0x1f423fcee22f9be0, 0x659a4f06d21a1299, + 0xeaf2de5e82448912, 0x902aae96b271006b, + 0x74523609127ad31a, 0x0e8a46c1224f5a63, + 0x81e2d7997211c1e8, 0xfb3aa75142244891, + 0xb46ad37a8a3b6595, 0xceb2a3b2ba0eecec, + 0x41da32eaea507767, 0x3b024222da65fe1e, + 0xa2722586f2d042ee, 0xd8aa554ec2e5cb97, + 0x57c2c41692bb501c, 0x2d1ab4dea28ed965, + 0x624ac0f56a91f461, 0x1892b03d5aa47d18, + 0x97fa21650afae693, 0xed2251ad3acf6fea, + 0x095ac9329ac4bc9b, 0x7382b9faaaf135e2, + 0xfcea28a2faafae69, 0x8632586aca9a2710, + 0xc9622c4102850a14, 0xb3ba5c8932b0836d, + 0x3cd2cdd162ee18e6, 0x460abd1952db919f, + 0x256b24ca6b12f26d, 0x5fb354025b277b14, + 0xd0dbc55a0b79e09f, 0xaa03b5923b4c69e6, + 0xe553c1b9f35344e2, 0x9f8bb171c366cd9b, + 0x10e3202993385610, 0x6a3b50e1a30ddf69, + 0x8e43c87e03060c18, 0xf49bb8b633338561, + 0x7bf329ee636d1eea, 0x012b592653589793, + 0x4e7b2d0d9b47ba97, 0x34a35dc5ab7233ee, + 0xbbcbcc9dfb2ca865, 0xc113bc55cb19211c, + 0x5863dbf1e3ac9dec, 0x22bbab39d3991495, + 0xadd33a6183c78f1e, 0xd70b4aa9b3f20667, + 0x985b3e827bed2b63, 0xe2834e4a4bd8a21a, + 0x6debdf121b863991, 0x1733afda2bb3b0e8, + 0xf34b37458bb86399, 0x8993478dbb8deae0, + 0x06fbd6d5ebd3716b, 0x7c23a61ddbe6f812, + 0x3373d23613f9d516, 0x49aba2fe23cc5c6f, + 0xc6c333a67392c7e4, 0xbc1b436e43a74e9d, + 0x95ac9329ac4bc9b5, 0xef74e3e19c7e40cc, + 0x601c72b9cc20db47, 0x1ac40271fc15523e, + 0x5594765a340a7f3a, 0x2f4c0692043ff643, + 0xa02497ca54616dc8, 0xdafce7026454e4b1, + 0x3e847f9dc45f37c0, 0x445c0f55f46abeb9, + 0xcb349e0da4342532, 0xb1eceec59401ac4b, + 0xfebc9aee5c1e814f, 0x8464ea266c2b0836, + 0x0b0c7b7e3c7593bd, 0x71d40bb60c401ac4, + 0xe8a46c1224f5a634, 0x927c1cda14c02f4d, + 0x1d148d82449eb4c6, 0x67ccfd4a74ab3dbf, + 0x289c8961bcb410bb, 0x5244f9a98c8199c2, + 0xdd2c68f1dcdf0249, 0xa7f41839ecea8b30, + 0x438c80a64ce15841, 0x3954f06e7cd4d138, + 0xb63c61362c8a4ab3, 0xcce411fe1cbfc3ca, + 0x83b465d5d4a0eece, 0xf96c151de49567b7, + 0x76048445b4cbfc3c, 0x0cdcf48d84fe7545, + 0x6fbd6d5ebd3716b7, 0x15651d968d029fce, + 0x9a0d8ccedd5c0445, 0xe0d5fc06ed698d3c, + 0xaf85882d2576a038, 0xd55df8e515432941, + 0x5a3569bd451db2ca, 0x20ed197575283bb3, + 0xc49581ead523e8c2, 0xbe4df122e51661bb, + 0x3125607ab548fa30, 0x4bfd10b2857d7349, + 0x04ad64994d625e4d, 0x7e7514517d57d734, + 0xf11d85092d094cbf, 0x8bc5f5c11d3cc5c6, + 0x12b5926535897936, 0x686de2ad05bcf04f, + 0xe70573f555e26bc4, 0x9ddd033d65d7e2bd, + 0xd28d7716adc8cfb9, 0xa85507de9dfd46c0, + 0x273d9686cda3dd4b, 0x5de5e64efd965432, + 0xb99d7ed15d9d8743, 0xc3450e196da80e3a, + 0x4c2d9f413df695b1, 0x36f5ef890dc31cc8, + 0x79a59ba2c5dc31cc, 0x037deb6af5e9b8b5, + 0x8c157a32a5b7233e, 0xf6cd0afa9582aa47, + 0x4ad64994d625e4da, 0x300e395ce6106da3, + 0xbf66a804b64ef628, 0xc5bed8cc867b7f51, + 0x8aeeace74e645255, 0xf036dc2f7e51db2c, + 0x7f5e4d772e0f40a7, 0x05863dbf1e3ac9de, + 0xe1fea520be311aaf, 0x9b26d5e88e0493d6, + 0x144e44b0de5a085d, 0x6e963478ee6f8124, + 0x21c640532670ac20, 0x5b1e309b16452559, + 0xd476a1c3461bbed2, 0xaeaed10b762e37ab, + 0x37deb6af5e9b8b5b, 0x4d06c6676eae0222, + 0xc26e573f3ef099a9, 0xb8b627f70ec510d0, + 0xf7e653dcc6da3dd4, 0x8d3e2314f6efb4ad, + 0x0256b24ca6b12f26, 0x788ec2849684a65f, + 0x9cf65a1b368f752e, 0xe62e2ad306bafc57, + 0x6946bb8b56e467dc, 0x139ecb4366d1eea5, + 0x5ccebf68aecec3a1, 0x2616cfa09efb4ad8, + 0xa97e5ef8cea5d153, 0xd3a62e30fe90582a, + 0xb0c7b7e3c7593bd8, 0xca1fc72bf76cb2a1, + 0x45775673a732292a, 0x3faf26bb9707a053, + 0x70ff52905f188d57, 0x0a2722586f2d042e, + 0x854fb3003f739fa5, 0xff97c3c80f4616dc, + 0x1bef5b57af4dc5ad, 0x61372b9f9f784cd4, + 0xee5fbac7cf26d75f, 0x9487ca0fff135e26, + 0xdbd7be24370c7322, 0xa10fceec0739fa5b, + 0x2e675fb4576761d0, 0x54bf2f7c6752e8a9, + 0xcdcf48d84fe75459, 0xb71738107fd2dd20, + 0x387fa9482f8c46ab, 0x42a7d9801fb9cfd2, + 0x0df7adabd7a6e2d6, 0x772fdd63e7936baf, + 0xf8474c3bb7cdf024, 0x829f3cf387f8795d, + 0x66e7a46c27f3aa2c, 0x1c3fd4a417c62355, + 0x935745fc4798b8de, 0xe98f353477ad31a7, + 0xa6df411fbfb21ca3, 0xdc0731d78f8795da, + 0x536fa08fdfd90e51, 0x29b7d047efec8728, +}; /* the eight DES S-boxes */ @@ -1175,6 +1374,33 @@ lxor_str(lua_State *L) { return 1; } +static int +lcrc32(lua_State *L){ + size_t len; + const char *str = luaL_checklstring(L, 1, &len); + if (len <= 0) return luaL_error(L, "#1 need a string"); + + uint32_t i = 0; + uint32_t crc = 0xFFFFFFFF; + + for (i = 0; i < len; i++) crc = CRC32[ (crc ^ str[i]) & 0xff ] ^ (crc >> 8); + lua_pushinteger(L, crc ^ 0xFFFFFFFF); + return 1; +}; + +static int +lcrc64(lua_State *L){ + size_t len; + const char *str = luaL_checklstring(L, 1, &len); + if (len <= 0) return luaL_error(L, "#1 need a string"); + + uint32_t i = 0; + uint64_t crc = 0x0; + for (i = 0; i < len; i++) crc = CRC64[(uint8_t)crc ^ (uint8_t)str[i]] ^ (crc >> 8); + lua_pushnumber(L, crc); + return 1; +}; + LUAMOD_API int luaopen_lcrypt(lua_State *L) { luaL_checkversion(L); @@ -1201,6 +1427,8 @@ luaopen_lcrypt(lua_State *L) { { "hmac_sha1", lhmac_sha1 }, { "hmac_hash", lhmac_hash }, { "xor_str", lxor_str }, + { "crc32", lcrc32 }, + { "crc64", lcrc64 }, { NULL, NULL }, }; luaL_newlib(L, crypt_libs); diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index 315f0b5f..deb267e6 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -2,6 +2,8 @@ local CRYPT = require "lcrypt" local sha1 = CRYPT.sha1 local xor_str = CRYPT.xor_str +local crc32 = CRYPT.crc32 +local crc64 = CRYPT.crc64 local randomkey = CRYPT.randomkey local hashkey = CRYPT.hashkey @@ -91,4 +93,12 @@ function crypt.dhexchange (...) return dhexchange(...) end +function crypt.crc32 (...) + return crc32(...) +end + +function crypt.crc64 (...) + return crc64(...) +end + return crypt From 37b14da6467bf04f2d31a4b975c5f158170ad690 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 14 May 2019 11:22:07 +0800 Subject: [PATCH 036/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0DB=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E9=9B=86=E8=AE=BE=E7=BD=AE,=20=E8=A7=A3=E5=86=B3DB(mysql)?= =?UTF-8?q?=E6=8F=92=E5=85=A5=E6=95=B0=E6=8D=AE=E4=B8=BA=E4=B9=B1=E7=A0=81?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/DB/init.lua | 1 + lualib/protocol/mysql.lua | 50 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index 8be58a75..c92e9cb4 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -333,6 +333,7 @@ function DB:ctor(opt) self.username = opt.username self.password = opt.password self.database = opt.database + self.charset = opt.charset or 'utf8' self.max = opt.max or 50 self.current = 0 -- 协程池 diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua index 507a4027..5c5af623 100644 --- a/lualib/protocol/mysql.lua +++ b/lualib/protocol/mysql.lua @@ -15,6 +15,51 @@ local error = error local tonumber = tonumber local new_tab = function (narr, nrec) return {} end +local CHARSET_MAP = { + _default = 0, + big5 = 1, + dec8 = 3, + cp850 = 4, + hp8 = 6, + koi8r = 7, + latin1 = 8, + latin2 = 9, + swe7 = 10, + ascii = 11, + ujis = 12, + sjis = 13, + hebrew = 16, + tis620 = 18, + euckr = 19, + koi8u = 22, + gb2312 = 24, + greek = 25, + cp1250 = 26, + gbk = 28, + latin5 = 30, + armscii8 = 32, + utf8 = 33, + ucs2 = 35, + cp866 = 36, + keybcs2 = 37, + macce = 38, + macroman = 39, + cp852 = 40, + latin7 = 41, + utf8mb4 = 45, + cp1251 = 51, + utf16 = 54, + utf16le = 56, + cp1256 = 57, + cp1257 = 59, + utf32 = 60, + binary = 63, + geostd8 = 92, + cp932 = 95, + eucjpms = 97, + gb18030 = 248 +} + local MySQL = class("MySQL") local STATE_CONNECTED = 1 @@ -468,10 +513,11 @@ function MySQL.connect(self, opts) local client_flags = 260047; - local req = strpack(" Date: Wed, 15 May 2019 02:40:41 +0800 Subject: [PATCH 037/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E9=9D=99=E6=80=81=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/css/font.css | 16 + static/css/login.css | 105 ++++ static/css/theme1.css | 21 + static/css/theme2.css | 21 + static/css/theme3.css | 22 + static/css/theme4.css | 21 + static/css/theme5.css | 27 + static/css/xadmin.css | 530 ++++++++++++++++ static/favicon.ico | Bin 0 -> 16958 bytes static/fonts/iconfont.eot | Bin 0 -> 49600 bytes static/fonts/iconfont.svg | 477 ++++++++++++++ static/fonts/iconfont.ttf | Bin 0 -> 49432 bytes static/fonts/iconfont.woff | Bin 0 -> 30200 bytes static/images/aiwrap.png | Bin 0 -> 3032 bytes static/images/bg.png | Bin 0 -> 28211 bytes static/index.html | 10 + static/js/jquery.min.js | 4 + static/js/xadmin.js | 584 ++++++++++++++++++ static/js/xcity.js | 506 +++++++++++++++ static/lib/layui/css/layui.css | 2 + static/lib/layui/css/layui.mobile.css | 2 + static/lib/layui/css/modules/code.css | 2 + .../css/modules/laydate/default/laydate.css | 2 + .../css/modules/layer/default/icon-ext.png | Bin 0 -> 5911 bytes .../layui/css/modules/layer/default/icon.png | Bin 0 -> 11493 bytes .../layui/css/modules/layer/default/layer.css | 2 + .../css/modules/layer/default/loading-0.gif | Bin 0 -> 5793 bytes .../css/modules/layer/default/loading-1.gif | Bin 0 -> 701 bytes .../css/modules/layer/default/loading-2.gif | Bin 0 -> 1787 bytes static/lib/layui/font/iconfont.eot | Bin 0 -> 40844 bytes static/lib/layui/font/iconfont.svg | 473 ++++++++++++++ static/lib/layui/font/iconfont.ttf | Bin 0 -> 40668 bytes static/lib/layui/font/iconfont.woff | Bin 0 -> 26744 bytes static/lib/layui/images/face/0.gif | Bin 0 -> 2689 bytes static/lib/layui/images/face/1.gif | Bin 0 -> 5514 bytes static/lib/layui/images/face/10.gif | Bin 0 -> 2797 bytes static/lib/layui/images/face/11.gif | Bin 0 -> 4121 bytes static/lib/layui/images/face/12.gif | Bin 0 -> 3361 bytes static/lib/layui/images/face/13.gif | Bin 0 -> 7425 bytes static/lib/layui/images/face/14.gif | Bin 0 -> 2375 bytes static/lib/layui/images/face/15.gif | Bin 0 -> 1793 bytes static/lib/layui/images/face/16.gif | Bin 0 -> 6721 bytes static/lib/layui/images/face/17.gif | Bin 0 -> 4439 bytes static/lib/layui/images/face/18.gif | Bin 0 -> 3017 bytes static/lib/layui/images/face/19.gif | Bin 0 -> 3040 bytes static/lib/layui/images/face/2.gif | Bin 0 -> 3222 bytes static/lib/layui/images/face/20.gif | Bin 0 -> 5144 bytes static/lib/layui/images/face/21.gif | Bin 0 -> 5191 bytes static/lib/layui/images/face/22.gif | Bin 0 -> 9823 bytes static/lib/layui/images/face/23.gif | Bin 0 -> 3792 bytes static/lib/layui/images/face/24.gif | Bin 0 -> 8096 bytes static/lib/layui/images/face/25.gif | Bin 0 -> 3127 bytes static/lib/layui/images/face/26.gif | Bin 0 -> 3291 bytes static/lib/layui/images/face/27.gif | Bin 0 -> 4377 bytes static/lib/layui/images/face/28.gif | Bin 0 -> 2793 bytes static/lib/layui/images/face/29.gif | Bin 0 -> 4854 bytes static/lib/layui/images/face/3.gif | Bin 0 -> 4017 bytes static/lib/layui/images/face/30.gif | Bin 0 -> 2555 bytes static/lib/layui/images/face/31.gif | Bin 0 -> 2002 bytes static/lib/layui/images/face/32.gif | Bin 0 -> 3481 bytes static/lib/layui/images/face/33.gif | Bin 0 -> 2454 bytes static/lib/layui/images/face/34.gif | Bin 0 -> 3700 bytes static/lib/layui/images/face/35.gif | Bin 0 -> 1800 bytes static/lib/layui/images/face/36.gif | Bin 0 -> 2331 bytes static/lib/layui/images/face/37.gif | Bin 0 -> 1513 bytes static/lib/layui/images/face/38.gif | Bin 0 -> 3615 bytes static/lib/layui/images/face/39.gif | Bin 0 -> 6495 bytes static/lib/layui/images/face/4.gif | Bin 0 -> 5689 bytes static/lib/layui/images/face/40.gif | Bin 0 -> 3154 bytes static/lib/layui/images/face/41.gif | Bin 0 -> 3644 bytes static/lib/layui/images/face/42.gif | Bin 0 -> 5305 bytes static/lib/layui/images/face/43.gif | Bin 0 -> 2674 bytes static/lib/layui/images/face/44.gif | Bin 0 -> 4126 bytes static/lib/layui/images/face/45.gif | Bin 0 -> 3417 bytes static/lib/layui/images/face/46.gif | Bin 0 -> 3007 bytes static/lib/layui/images/face/47.gif | Bin 0 -> 2333 bytes static/lib/layui/images/face/48.gif | Bin 0 -> 2689 bytes static/lib/layui/images/face/49.gif | Bin 0 -> 2315 bytes static/lib/layui/images/face/5.gif | Bin 0 -> 4567 bytes static/lib/layui/images/face/50.gif | Bin 0 -> 5866 bytes static/lib/layui/images/face/51.gif | Bin 0 -> 2785 bytes static/lib/layui/images/face/52.gif | Bin 0 -> 777 bytes static/lib/layui/images/face/53.gif | Bin 0 -> 2127 bytes static/lib/layui/images/face/54.gif | Bin 0 -> 2196 bytes static/lib/layui/images/face/55.gif | Bin 0 -> 1971 bytes static/lib/layui/images/face/56.gif | Bin 0 -> 2034 bytes static/lib/layui/images/face/57.gif | Bin 0 -> 2705 bytes static/lib/layui/images/face/58.gif | Bin 0 -> 2258 bytes static/lib/layui/images/face/59.gif | Bin 0 -> 10311 bytes static/lib/layui/images/face/6.gif | Bin 0 -> 2213 bytes static/lib/layui/images/face/60.gif | Bin 0 -> 3245 bytes static/lib/layui/images/face/61.gif | Bin 0 -> 2495 bytes static/lib/layui/images/face/62.gif | Bin 0 -> 2017 bytes static/lib/layui/images/face/63.gif | Bin 0 -> 5871 bytes static/lib/layui/images/face/64.gif | Bin 0 -> 6448 bytes static/lib/layui/images/face/65.gif | Bin 0 -> 3576 bytes static/lib/layui/images/face/66.gif | Bin 0 -> 3029 bytes static/lib/layui/images/face/67.gif | Bin 0 -> 2701 bytes static/lib/layui/images/face/68.gif | Bin 0 -> 1424 bytes static/lib/layui/images/face/69.gif | Bin 0 -> 2431 bytes static/lib/layui/images/face/7.gif | Bin 0 -> 3398 bytes static/lib/layui/images/face/70.gif | Bin 0 -> 4590 bytes static/lib/layui/images/face/71.gif | Bin 0 -> 5304 bytes static/lib/layui/images/face/8.gif | Bin 0 -> 4050 bytes static/lib/layui/images/face/9.gif | Bin 0 -> 4221 bytes static/lib/layui/lay/modules/carousel.js | 2 + static/lib/layui/lay/modules/code.js | 2 + static/lib/layui/lay/modules/colorpicker.js | 2 + static/lib/layui/lay/modules/element.js | 2 + static/lib/layui/lay/modules/flow.js | 2 + static/lib/layui/lay/modules/form.js | 2 + static/lib/layui/lay/modules/jquery.js | 5 + static/lib/layui/lay/modules/laydate.js | 2 + static/lib/layui/lay/modules/layedit.js | 2 + static/lib/layui/lay/modules/layer.js | 2 + static/lib/layui/lay/modules/laypage.js | 2 + static/lib/layui/lay/modules/laytpl.js | 2 + static/lib/layui/lay/modules/mobile.js | 2 + static/lib/layui/lay/modules/rate.js | 2 + static/lib/layui/lay/modules/slider.js | 2 + static/lib/layui/lay/modules/table.js | 2 + static/lib/layui/lay/modules/tree.js | 2 + static/lib/layui/lay/modules/upload.js | 2 + static/lib/layui/lay/modules/util.js | 2 + static/lib/layui/layui.js | 2 + static/welcome.html | 219 +++++++ 126 files changed, 3089 insertions(+) create mode 100755 static/css/font.css create mode 100755 static/css/login.css create mode 100755 static/css/theme1.css create mode 100755 static/css/theme2.css create mode 100755 static/css/theme3.css create mode 100755 static/css/theme4.css create mode 100755 static/css/theme5.css create mode 100755 static/css/xadmin.css create mode 100644 static/favicon.ico create mode 100755 static/fonts/iconfont.eot create mode 100755 static/fonts/iconfont.svg create mode 100755 static/fonts/iconfont.ttf create mode 100755 static/fonts/iconfont.woff create mode 100755 static/images/aiwrap.png create mode 100755 static/images/bg.png create mode 100644 static/index.html create mode 100755 static/js/jquery.min.js create mode 100755 static/js/xadmin.js create mode 100755 static/js/xcity.js create mode 100755 static/lib/layui/css/layui.css create mode 100755 static/lib/layui/css/layui.mobile.css create mode 100755 static/lib/layui/css/modules/code.css create mode 100755 static/lib/layui/css/modules/laydate/default/laydate.css create mode 100755 static/lib/layui/css/modules/layer/default/icon-ext.png create mode 100755 static/lib/layui/css/modules/layer/default/icon.png create mode 100755 static/lib/layui/css/modules/layer/default/layer.css create mode 100755 static/lib/layui/css/modules/layer/default/loading-0.gif create mode 100755 static/lib/layui/css/modules/layer/default/loading-1.gif create mode 100755 static/lib/layui/css/modules/layer/default/loading-2.gif create mode 100755 static/lib/layui/font/iconfont.eot create mode 100755 static/lib/layui/font/iconfont.svg create mode 100755 static/lib/layui/font/iconfont.ttf create mode 100755 static/lib/layui/font/iconfont.woff create mode 100755 static/lib/layui/images/face/0.gif create mode 100755 static/lib/layui/images/face/1.gif create mode 100755 static/lib/layui/images/face/10.gif create mode 100755 static/lib/layui/images/face/11.gif create mode 100755 static/lib/layui/images/face/12.gif create mode 100755 static/lib/layui/images/face/13.gif create mode 100755 static/lib/layui/images/face/14.gif create mode 100755 static/lib/layui/images/face/15.gif create mode 100755 static/lib/layui/images/face/16.gif create mode 100755 static/lib/layui/images/face/17.gif create mode 100755 static/lib/layui/images/face/18.gif create mode 100755 static/lib/layui/images/face/19.gif create mode 100755 static/lib/layui/images/face/2.gif create mode 100755 static/lib/layui/images/face/20.gif create mode 100755 static/lib/layui/images/face/21.gif create mode 100755 static/lib/layui/images/face/22.gif create mode 100755 static/lib/layui/images/face/23.gif create mode 100755 static/lib/layui/images/face/24.gif create mode 100755 static/lib/layui/images/face/25.gif create mode 100755 static/lib/layui/images/face/26.gif create mode 100755 static/lib/layui/images/face/27.gif create mode 100755 static/lib/layui/images/face/28.gif create mode 100755 static/lib/layui/images/face/29.gif create mode 100755 static/lib/layui/images/face/3.gif create mode 100755 static/lib/layui/images/face/30.gif create mode 100755 static/lib/layui/images/face/31.gif create mode 100755 static/lib/layui/images/face/32.gif create mode 100755 static/lib/layui/images/face/33.gif create mode 100755 static/lib/layui/images/face/34.gif create mode 100755 static/lib/layui/images/face/35.gif create mode 100755 static/lib/layui/images/face/36.gif create mode 100755 static/lib/layui/images/face/37.gif create mode 100755 static/lib/layui/images/face/38.gif create mode 100755 static/lib/layui/images/face/39.gif create mode 100755 static/lib/layui/images/face/4.gif create mode 100755 static/lib/layui/images/face/40.gif create mode 100755 static/lib/layui/images/face/41.gif create mode 100755 static/lib/layui/images/face/42.gif create mode 100755 static/lib/layui/images/face/43.gif create mode 100755 static/lib/layui/images/face/44.gif create mode 100755 static/lib/layui/images/face/45.gif create mode 100755 static/lib/layui/images/face/46.gif create mode 100755 static/lib/layui/images/face/47.gif create mode 100755 static/lib/layui/images/face/48.gif create mode 100755 static/lib/layui/images/face/49.gif create mode 100755 static/lib/layui/images/face/5.gif create mode 100755 static/lib/layui/images/face/50.gif create mode 100755 static/lib/layui/images/face/51.gif create mode 100755 static/lib/layui/images/face/52.gif create mode 100755 static/lib/layui/images/face/53.gif create mode 100755 static/lib/layui/images/face/54.gif create mode 100755 static/lib/layui/images/face/55.gif create mode 100755 static/lib/layui/images/face/56.gif create mode 100755 static/lib/layui/images/face/57.gif create mode 100755 static/lib/layui/images/face/58.gif create mode 100755 static/lib/layui/images/face/59.gif create mode 100755 static/lib/layui/images/face/6.gif create mode 100755 static/lib/layui/images/face/60.gif create mode 100755 static/lib/layui/images/face/61.gif create mode 100755 static/lib/layui/images/face/62.gif create mode 100755 static/lib/layui/images/face/63.gif create mode 100755 static/lib/layui/images/face/64.gif create mode 100755 static/lib/layui/images/face/65.gif create mode 100755 static/lib/layui/images/face/66.gif create mode 100755 static/lib/layui/images/face/67.gif create mode 100755 static/lib/layui/images/face/68.gif create mode 100755 static/lib/layui/images/face/69.gif create mode 100755 static/lib/layui/images/face/7.gif create mode 100755 static/lib/layui/images/face/70.gif create mode 100755 static/lib/layui/images/face/71.gif create mode 100755 static/lib/layui/images/face/8.gif create mode 100755 static/lib/layui/images/face/9.gif create mode 100755 static/lib/layui/lay/modules/carousel.js create mode 100755 static/lib/layui/lay/modules/code.js create mode 100755 static/lib/layui/lay/modules/colorpicker.js create mode 100755 static/lib/layui/lay/modules/element.js create mode 100755 static/lib/layui/lay/modules/flow.js create mode 100755 static/lib/layui/lay/modules/form.js create mode 100755 static/lib/layui/lay/modules/jquery.js create mode 100755 static/lib/layui/lay/modules/laydate.js create mode 100755 static/lib/layui/lay/modules/layedit.js create mode 100755 static/lib/layui/lay/modules/layer.js create mode 100755 static/lib/layui/lay/modules/laypage.js create mode 100755 static/lib/layui/lay/modules/laytpl.js create mode 100755 static/lib/layui/lay/modules/mobile.js create mode 100755 static/lib/layui/lay/modules/rate.js create mode 100755 static/lib/layui/lay/modules/slider.js create mode 100755 static/lib/layui/lay/modules/table.js create mode 100755 static/lib/layui/lay/modules/tree.js create mode 100755 static/lib/layui/lay/modules/upload.js create mode 100755 static/lib/layui/lay/modules/util.js create mode 100755 static/lib/layui/layui.js create mode 100755 static/welcome.html diff --git a/static/css/font.css b/static/css/font.css new file mode 100755 index 00000000..b83e5b4b --- /dev/null +++ b/static/css/font.css @@ -0,0 +1,16 @@ +@font-face { + font-family: 'iconfont'; + src: url('../fonts/iconfont.eot'); + src: url('../fonts/iconfont.eot?#iefix') format('embedded-opentype'), + url('../fonts/iconfont.woff') format('woff'), + url('../fonts/iconfont.ttf') format('truetype'), + url('../fonts/iconfont.svg#iconfont') format('svg'); +} +.iconfont{ + font-family:"iconfont" !important; + font-size:16px;font-style:normal; + -webkit-font-smoothing: antialiased; + -webkit-text-stroke-width: 0.2px; + -moz-osx-font-smoothing: grayscale; +} + diff --git a/static/css/login.css b/static/css/login.css new file mode 100755 index 00000000..10b8f33d --- /dev/null +++ b/static/css/login.css @@ -0,0 +1,105 @@ +/* +* @Author: xuebingsi +* @Date: 2019-04-01 13:37:17 +* @Last Modified by: zhibinm +* @Last Modified time: 2019-04-01 13:37:19 +*/ +.login-bg{ + /*background: #eeeeee url() 0 0 no-repeat;*/ + background:url(../images/bg.png) no-repeat center; + background-size: cover; + overflow: hidden; +} +.login{ + margin: 120px auto 0 auto; + min-height: 420px; + max-width: 420px; + padding: 40px; + background-color: #ffffff; + margin-left: auto; + margin-right: auto; + border-radius: 4px; + /* overflow-x: hidden; */ + box-sizing: border-box; +} +.login a.logo{ + display: block; + height: 58px; + width: 167px; + margin: 0 auto 30px auto; + background-size: 167px 42px; +} +.login .message { + margin: 10px 0 0 -58px; + padding: 18px 10px 18px 60px; + background: #189F92; + position: relative; + color: #fff; + font-size: 16px; +} +.login #darkbannerwrap { + background: url(../images/aiwrap.png); + width: 18px; + height: 10px; + margin: 0 0 20px -58px; + position: relative; +} + +.login input[type=text], +.login input[type=file], +.login input[type=password], +.login input[type=email], select { + border: 1px solid #DCDEE0; + vertical-align: middle; + border-radius: 3px; + height: 50px; + padding: 0px 16px; + font-size: 14px; + color: #555555; + outline:none; + width:100%; + box-sizing: border-box; +} +.login input[type=text]:focus, +.login input[type=file]:focus, +.login input[type=password]:focus, +.login input[type=email]:focus, select:focus { + border: 1px solid #27A9E3; +} +.login input[type=submit], +.login input[type=button]{ + display: inline-block; + vertical-align: middle; + padding: 12px 24px; + margin: 0px; + font-size: 18px; + line-height: 24px; + text-align: center; + white-space: nowrap; + vertical-align: middle; + cursor: pointer; + color: #ffffff; + background-color: #189F92; + border-radius: 3px; + border: none; + -webkit-appearance: none; + outline:none; + width:100%; +} +.login hr { + background: #fff url() 0 0 no-repeat; +} +.login hr.hr15 { + height: 15px; + border: none; + margin: 0px; + padding: 0px; + width: 100%; +} +.login hr.hr20 { + height: 20px; + border: none; + margin: 0px; + padding: 0px; + width: 100%; +} \ No newline at end of file diff --git a/static/css/theme1.css b/static/css/theme1.css new file mode 100755 index 00000000..79b2dc8e --- /dev/null +++ b/static/css/theme1.css @@ -0,0 +1,21 @@ +body{ + background:#F2F1F2; +} +.container{ + background:#1A1B20; +} +.left-nav{ + background:#1A1B20; +} + +.left-nav a{ + color:rgba(255,255,255,.7); +} +..left-nav a.active{ + background: #009688 !important; + color: #fff; +} +.left-nav a:hover{ + background: #009688 !important; + color: #fff; +} \ No newline at end of file diff --git a/static/css/theme2.css b/static/css/theme2.css new file mode 100755 index 00000000..04e91717 --- /dev/null +++ b/static/css/theme2.css @@ -0,0 +1,21 @@ +body{ + background:#EEF5F9; +} +.container{ + background:#323640; +} +.left-nav{ + background:#fff; +} + +.left-nav a{ + color:#686a76; +} +.left-nav a.active{ + background: #786AED !important; + color: #fff; +} +.left-nav a:hover{ + background: #786AED !important; + color: #fff; +} diff --git a/static/css/theme3.css b/static/css/theme3.css new file mode 100755 index 00000000..d5919c57 --- /dev/null +++ b/static/css/theme3.css @@ -0,0 +1,22 @@ +body{ + background:#E8E8E8; +} +.container{ + background:#F34743; +} + +.left-nav{ + background:#F4F4F4; +} + +.left-nav a{ + color:#686a76; +} +.left-nav a.active{ + background: #FEFEFE !important; + color: #F34743; +} +.left-nav a:hover{ + background: #FEFEFE !important; + color: #F34743; +} \ No newline at end of file diff --git a/static/css/theme4.css b/static/css/theme4.css new file mode 100755 index 00000000..0a0ff61d --- /dev/null +++ b/static/css/theme4.css @@ -0,0 +1,21 @@ +body{ + background:#E4E4E4; +} +.container{ + background:#019587; +} +.left-nav{ + background:#263035; +} + +.left-nav a{ + color:#fff; +} +.left-nav a.active{ + background: #212525 !important; + color: #fff !important; +} +.left-nav a:hover{ + background: #212525 !important; + color: #fff !important; +} \ No newline at end of file diff --git a/static/css/theme5.css b/static/css/theme5.css new file mode 100755 index 00000000..92c9a06d --- /dev/null +++ b/static/css/theme5.css @@ -0,0 +1,27 @@ +body{ + background:#EEF5F9 !important; +} +.container{ + background:linear-gradient(to left, #7b4397, #2196f3); +} + +.left-nav{ + background:#fff !important; +} + +.left-nav a{ + color:#686a76 !important; +} +.left-nav a.active{ + background: linear-gradient(to left, #7c8ce4, #2196f3) !important; + color: #fff !important; + border-color: #7b4397 !important; +} +.left-nav a:hover{ + background: linear-gradient(to left, #7c8ce4, #2196f3) !important; + color: #fff !important; + border-color: #7b4397 !important; +} +.container .logo a{ + background: rgba(0,0,0,0) !important; +} \ No newline at end of file diff --git a/static/css/xadmin.css b/static/css/xadmin.css new file mode 100755 index 00000000..2a4d4b4b --- /dev/null +++ b/static/css/xadmin.css @@ -0,0 +1,530 @@ +@charset "utf-8"; +@import url(../lib/layui/css/layui.css); +*{ + margin: 0px; + padding: 0px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} +a{ + text-decoration: none; +} +html{ + width: 100%; + height: 100%; + overflow-x:hidden; + overflow-y:auto; +} +body{ + width: 100%; + min-height: 100%; + background: #f1f1f1; + /*background: #fff;*/ +} +.x-red{ + color: red; +} + +.layui-form-switch{ + margin-top: 0px; +} +.layui-input:focus, .layui-textarea:focus { + border-color: #189f92!important; +} + +.layui-fluid{ + padding:15px; +} +.x-nav{ + padding: 0 20px; + position: relative; + z-index: 99; + border-bottom: 1px solid #e5e5e5; + line-height: 39px; + height: 39px; + overflow: hidden; + background: #fff; +} +.page{ + text-align: center; + +} +.page a{ + display: inline-block; + background: #fff; + color: #888; + padding: 5px; + min-width: 15px; + border: 1px solid #E2E2E2; + +} +.page span{ + display: inline-block; + padding: 5px; + min-width: 15px; + border: 1px solid #E2E2E2; +} +.page span.current{ + display: inline-block; + background: #009688; + color: #fff; + padding: 5px; + min-width: 15px; + border: 1px solid #009688; +} +.page .pagination li{ + display: inline-block; + margin-right: 5px; + text-align: center; +} +.page .pagination li.active span{ + background: #009688; + color: #fff; + border: 1px solid #009688; + +} + +/*登录样式*/ +/*头部*/ +.container{ + width: 100%; + height: 45px; + background-color: #222; +} +.container a,.layui-nav .layui-nav-item a{ + color: #fff; +} +.container .logo a{ + background-color: rgba(0,0,0,0); +} +.container .logo a{ + float: left; + font-size: 18px; + padding-left: 20px; + line-height: 45px; + width: 200px; +} +.container .right{ + background-color:rgba(0,0,0,0); + float: right; + +} +.container .left_open{ + height: 45px; + float: left; + margin-left: 10px; +} +.container .left_open i{ + display: block; + background: rgba(255,255,255,0.1); + width: 32px; + height: 32px; + line-height: 32px; + border-radius: 3px; + text-align: center; + margin-top: 7px; + cursor: pointer; +} +.container .left_open i:hover{ + background: rgba(255,255,255,0.3); +} + +.container .left{ + background-color:rgba(0,0,0,0); + float: left; + +} +.container .layui-nav-item{ + line-height: 45px; +} +.container .layui-nav-more{ + top: 20px; +} +.container .layui-nav-child{ + top: 50px; +} +.container .layui-nav-child i{ + margin-right: 10px; +} +.layui-nav .layui-nav-item a{ + cursor: pointer; +} +.layui-nav .layui-nav-child a{ + color: #333; + cursor: pointer; +} +.left-nav{ + position: absolute; + top: 45px; + bottom: 0px; + /*bottom: 42px;*/ + left: 0; + z-index: 2; + padding-top: 10px; + background-color: #EEEEEE; + width: 220px; + max-width: 220px; + overflow: auto; + overflow-x:hidden; + overflow: hidden; + + /*width: 0px;*/ +} +#side-nav{ + width: 220px; +} + +.left-nav #nav li:hover > a{ + /*color: blue;*/ +} +.left-nav #nav .current{ + background-color: rgba(0, 0, 0, 0.3); +} +.left-nav #nav li a{ + font-size: 14px; + padding: 10px 15px 10px 15px; + display: block; + cursor: pointer; + border-left: 4px solid transparent; + transition: all 0.3s; +} +.left-nav a:hover{ + background: #009688 !important; + color: #fff; + border-color: #04564e !important; +} +.left-nav a.active{ + background: #009688 !important; + color: #fff; + border-color: #04564e !important; +} +.left-nav #nav li a cite{ + font-size: 14px; +} + +.left-nav #nav li .sub-menu{ + display: none; +} +.left-nav #nav li .opened{ + display: block; +} +.left-nav #nav li .opened:hover{ + /*background: #fff ;*/ +} +.left-nav #nav li .opened .current{ + +} +.left-nav #nav li .sub-menu li:hover{ + /*color: blue;*/ + /*background: #fff ;*/ +} +.left-nav #nav li .sub-menu li a{ + padding: 12px 15px 12px 30px; + font-size: 14px; + cursor: pointer; +} +.left-nav #nav li .sub-menu li .sub-menu li a{ + padding-left: 45px; +} +/*.left-nav #nav li .sub-menu li a:hover{ + color: #148cf1; +}*/ +.left-nav #nav li .sub-menu li a i{ + font-size: 12px; +} +.left-nav #nav li a i{ + padding-right: 10px; + line-height: 14px; +} +.left-nav #nav li .nav_right{ + float: right; + font-size: 16px; +} +.x-slide_left { + width: 17px; + height: 61px; + background: url(../images/icon.png) 0 0 no-repeat; + position: absolute; + top: 200px; + left: 220px; + cursor: pointer; + z-index: 3; +} +.page-content{ + position: absolute; + top: 45px; + right: 0; + /*bottom: 42px;*/ + bottom: 0px; + left: 220px; + overflow: hidden; + z-index: 1; +} +.page-content-bg{ + position: absolute; + top: 45px; + right: 0; + /*bottom: 42px;*/ + bottom: 0px; + left: 220px; + background: rgba(0,0,0,0.5); + overflow: hidden; + z-index: 100; + display: none; +} + +.page-content .tab{ + height: 100%; + width: 100%; + /*background: #EFEEF0;*/ + margin: 0px; +} +.page-content .layui-tab-title{ + /*padding-top: 5px;*/ + height: 35px; + background: #EFEEF0 ; + position: relative; + z-index: 100; +} +.page-content .layui-tab-title li.home i{ + padding-right: 5px; +} +.page-content .layui-tab-title li.home .layui-tab-close{ + display: none; +} +.page-content .layui-tab-title li{ + line-height: 35px; +} +.page-content .layui-tab-title .layui-this:after{ + height: 36px; +} +.page-content .layui-tab-title li .layui-tab-close{ + border-radius: 50%; +} +.page-content .layui-tab-title .layui-this{ + background: #fff ; +} +.page-content .layui-tab-bar{ + height:34px; + line-height: 35px; +} +.page-content .layui-tab-content{ + position: absolute; + top: 36px; + bottom: 0px; + width: 100%; + padding: 0px; + overflow: hidden; +} +.page-content .layui-tab-content .layui-tab-item{ + width: 100%; + height: 100%; + +} +.page-content .layui-tab-content .layui-tab-item iframe{ + width: 100%; + height: 100%; + +} +.x-admin-carousel,.layui-carousel,.x-admin-carousel>[carousel-item]>* { + background-color:#fff +} + +.x-admin-backlog .x-admin-backlog-body { + display:block; + padding:10px 15px; + background-color:#f8f8f8; + color:#999; + border-radius:2px; + transition:all .3s; + -webkit-transition:all .3s +} +.x-admin-backlog-body h3 { + padding-bottom:10px; + font-size:12px +} +.x-admin-backlog-body p cite { + font-style:normal; + font-size:30px; + font-weight:300; + color:#009688 +} +.x-admin-backlog-body:hover { + background-color:#CFCFCF; + color:#888 +} + +table th, table td { + word-break: break-all; +} + +/*404页面样式*/ +.fly-panel { + margin-bottom: 15px; + border-radius: 2px; + /*background-color: #fff;*/ + box-shadow: 0 1px 2px 0 rgba(0,0,0,.05); +} +.fly-none { + min-height: 600px; + text-align: center; + padding-top: 50px; + color: #999; +} +.fly-none .layui-icon { + line-height: 300px; + font-size: 300px; + color: #393D49; +} +.fly-none p { + margin-top: 50px; + padding: 0 15px; + font-size: 20px; + color: #999; + font-weight: 300; +} +#tab_right{ + display: none; + width: 80px; + position: absolute; + top: 35px; + left: 0px; +} +#tab_right dl{ + top: 0px; +} +#tab_show{ + position: absolute; + top: 36px; + bottom: 0px; + width: 100%; + background:rgb(255, 255, 255,0); + padding: 0px; + overflow: hidden; + display: none; +} + + +@media screen and (max-width: 768px){ + .fast-add{ + display: none; + } + .layui-nav .to-index{ + display: none; + } + .container .logo a{ + width: 140px; + } + .container .left_open { + /*float: right;*/ + } + .left-nav{ + width: 60px; + } + .left-nav #nav li a i{ + font-size: 18px; + } + .left-nav cite,.left-nav .nav_right{ + display: none; + } + .page-content{ + left: 60px; + } + .page-content .layui-tab-content .layui-tab-item{ + -webkit-overflow-scrolling: touch; + overflow-y: scroll; + } + .x-so input.layui-input{ + width: 100%; + margin: 10px; + } +} + +/*精细版样式*/ + +.x-admin-sm{ + font-size: 12px; +} +.x-admin-sm body{ + font-size: 12px; +} +/*登录页面样式*/ +.x-admin-sm .login input[type=submit],.x-admin-sm .login input[type=button]{ + font-size: 14px; +} +.x-admin-sm .login input[type=text], +.x-admin-sm .login input[type=file], +.x-admin-sm .login input[type=password], +.x-admin-sm .login input[type=email], .x-admin-sm select { + font-size: 12px; +} +.x-admin-sm .login .message{ + font-size: 14px; +} + +.x-admin-sm .layui-table td, .x-admin-sm .layui-table th{ + font-size: 12px; +} +.x-admin-sm .layui-elem-field legend{ + font-size: 18px; +} + +.x-admin-sm .x-admin-backlog-body p cite{ + font-size: 24px; +} +.x-admin-sm .left-nav #nav li a cite{ + font-size: 12px; +} +.x-admin-sm .iconfont{ + font-size: 14px; +} +.x-admin-sm .layui-tab-title li{ + font-size: 12px; +} +.x-admin-sm .layui-icon{ + font-size: 14px; +} +.x-admin-sm .layui-nav *{ + font-size: 12px; +} +.x-admin-sm .layui-breadcrumb>*{ + font-size: 12px; +} +.x-admin-sm .layui-btn,.x-admin-sm .layui-btn-xs,.x-admin-sm .layui-btn-sm{ + font-size: 12px; +} + +.x-admin-sm .layui-laydate{ + font-size: 12px; +} +.x-admin-sm .layui-btn{ + height: 30px; + line-height: 30px; + padding: 0 10px; +} + +.x-admin-sm .layui-btn-lg{ + height: 38px; + line-height: 38px; + padding: 0 18px; + font-size: 14px; +} +.x-admin-sm .layui-layer-title,.x-admin-sm .layui-layer-dialog .layui-layer-content{ + font-size: 12px; +} +.x-admin-sm .layui-input,.x-admin-sm .layui-select,.x-admin-sm .layui-textarea{ + height: 30px; +} + +.x-admin-sm .layui-form-pane .layui-form-label{ + height: 30px; + line-height: 14px; +} +.x-admin-sm .layui-form-checkbox span{ + font-size: 12px; +} +.x-admin-sm .fly-none .layui-icon { + line-height: 300px; + font-size: 300px; + color: #393D49; +} + diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..6ef2663d76572c1cddba652046c0846e667cfce2 GIT binary patch literal 16958 zcmeI2T}WGJ7{|{fh)9tjBBc~3#o9Er)~2?mn(Ap9ZEBr}xLp(}B2uJ?lppgVc<53mzz=aOF(Sa2PT8!ku!1Pqb z4m4mU7kXf@om?2sq}YWo!S9ae1{c*E^NS7RFenVbfcWj$SzolSm^|1{Js5?-H5jPk z9&}Jd{4M0dE%BUOXr1v?U53E`4C=)kdC-dKr<_JTxzL9WhG9@9)-wG-0~V|)uw?sJ zS7D%OeXEPm!5OSztF=s@3*4>WM*O3gwg+tG?_AKVLkGQEt%*76flB;o?@xE1oX>0m z)WkgMg61SOVbi{m*`B|@chtlZf3&U_2I9}O^hxHcFfb(&>ifTVhQ2~@v0*3sf{~hF zr9Ue2$1EM}jpt#YvNsG7f5lv=rj)&*n*FgVL&JDyx7Zu=*&D}T&x`hXNSb zF|Bt_cYQX@hacFALibNx3@uqlLieoI^hrC@`R$9;<$5EvW*7{igFeiZ?%Hgb4=*_PE z^N$sB;T8-gU~r3?*b9R(7;Nh4sfk@M7$*MnzKD5UH`x@w@D&C+7}UZ*(*SvpPd#u6 z2GoPjt>PgU^4J?q^hcX?v(>ekJQsW1Tp4?Gssjf7kyU$<@HPCPF^_sM9=dNEgTYm- z7#$er7msgC#eyEhXUBFJ^ojl;5UEWqWz`see7ZFDsJSop@KpOo$Ek^z;4q2~1cNWh z1vdfq{KC@~13DNX z7p%murzZBWFI-@6w6Ql{iYzylkP9U+7(@q~>SeaaF8ojY*rvpP=uluFYM}`Rm&t{y zwf^8H4BDv&1=K~|@=wWL>HiRSx)|b*n=4?T^}8^zrn^qgO9GOBBp?Y$0+N6vAPGnU zl7J*22}lBxfFvLZydQzL8Grn`=HZ(^UVf&X{_^rqF}}I%*2Whv5^I+vwB@%~lEXhA z6Pu@ArCxb$`sdX0mE_6mhUEC^-Q@Uta(MgO+L&5@{o&fg+K~8b^Yd4!r-{@_2919M Dk2;%q literal 0 HcmV?d00001 diff --git a/static/fonts/iconfont.eot b/static/fonts/iconfont.eot new file mode 100755 index 0000000000000000000000000000000000000000..df5334af5f88dbaafc510fc0b94ecd79baab2ee6 GIT binary patch literal 49600 zcmd3P2Y?&J+4jzCSC>}ON>_BJ(@DDGYq~q>+|Iq?f^BSr4aOZ4V~1kEn05hUdIu9~ z0-?nu0RkbU0SOQYHQ}Qql!W$42c$qCfsdT_{^#A*xqwN+|9yWsX-2!VyR*A9@4WSy zxrcr(2r(okaDo1~AdB_}N-klvHRnKQe@|`~V^>$+_wOkk*$N0p3TuR~3Y&%XcD@A% ze8NIuop6M3yl|ATN;noJQDF&6juno>*>rBb&?U4Boj9_;>rqTMbqfQ6D0I$Pw0QDe z);)hhVIN8-&tKG$TWZWc4Ev{Gf61CntB%gTaD71#q*1u2>4>9FTEFJ5yZ(W?w+q5; zA8lB-YHgry*{^Z#>qz+xDA1?M7i0UUNR1mdZP~i~=HXjW-}7jZgOB>^npMWxrCoyH zKL};*n^tW-n%ymYitQNAuiw0C)4KkHtWyQy{;fEF)6ri&Zp-XX*F7r;KOPhWrH!6l z92g!D4wcUtes%bL`CR*LaOv*9opuS%^S`g?hx^Cj&0!55xBeN$sB-SbSJITtiJa!fu4l?tF$kQNfgKOK^49GVlOC?)C}g>mXxl~KC2 z_|4*-#h(@*Dn43#toW&lR69zEpgr`1|4;#dnH-EdIIpNzp3tl3Y?to|3Or zTgsH0N-d?fQfDbw%9kdUrj}-uW|!ua=9dsddS*}>R0czX^@FX0;|He< z)A-z4+*!P-cz5x^&)w~1-0k(^TgCTqw-1V+;%*Y|rsHl#sbR0X^_C`-rr>U~aJPB5 z+Ymq|hLwghnAPWQ45HBs2>xLMtet z4dSQ+0;x;L3Ee`E&@1GHg3u?77RCr;g>k}op z4iM%DbA@@reBnT00Yu*-;UHl#1m05NVBrv9nXp_qR5(mnAsjBOgg9Icak&=4a6JU% z28hG22piFUn;;awDjY2wV++YG5S=FoCkk7IlZ0);$-*hZslsW(>1gGz31Y(NMJRsHNf`Z@rO>rNEC|qI|*Y_ zDE`Pv7@b1#$49{q4n)h?LzTMCy~4;KI0^C0HOGtlfVmvB6LAY;0i+VB`1->T720_;1ojf6(@mb2*uw! z3EV>{zV0OO5ux~olfY4g;#*DvZxM=bI|*DyDE_CDz;A?N(MjMuLb2o|@F1Z$=p=9> zq4SvOVWH%6 z5;(F@GMoh7ER@1d0+$v_5hsCP3#F)&z`2Ez=_K%Qp%ilxxVccm*q{WyE|jXA1P(8h zs+|O0FO+JW1g`V- ziB5tn5=x*CN{~uIX^N8|pM=s>CqYsPCGeT)Q&CX}dO zks#ZI67?$*q?}NqK0_kjTw3BJNIs!NeS-uUD3qvgkRT0(67>fX=TH|%;8#41UH)IcNYH99lezEz_Ef=)T8+C8ns^?q_;hdT#0s_3r2$%rDG;)W`d-7`=V;n`3Ssn;1K9>}6x`9~T}s zd)$lTPw98{_w+w8Vd8}1#O;%+CheTOeM;lhrm2@q8#S#sJv04+8QW(bG^>8reY1VD z4?UpjfV<`#IJa|N=e%>~hv(mZ;AIP<3l3dy@4_VuuUj~{X!W8`(PA>p>NmuniZRI4 zCh+^I#DAF5${KvUmL;=2?Yxyml5)fh`^0Q2n^aQ`?c#W5HYnj}F5iQXiS{x)mYs0ra*y`i1x z35DXJ&;^uZsM%u9?>{Pdr;S=MZmwb!gQ&J)3LOrq0;F z05huVeYZ^rRt51rr7CKK;%dOF#j3oi#xOhaZFkJ@)MnnotCAKoJRk|;0Q3_RGPoTw zXBzPn*4^9AvOcCJn3?To1^h$`sdmP)5ftJO%IFw=dQ(1@HhZ0uaIoN980qe31~yX> zoR%x}vv#H`s3F^{cB3S$DhZY^VP3-Gc{HTQs zHgObvh(jCBcrUc>*ym8(~EX<0`6SVNI1gq_|uSXiG*7%74j!NxB$Q!X}IM zvrHE6U10Xxv!lWh)~g0ROtqdQt0%8APf%sI89BrHKw-Y1!u+yNW^pCxbJTdE#Vf^6 z#e<+{WuTMvk^H94NE-P}Hc1p`CUdG-NH%1$VmpY=#QhvX%=YAS(Xi5xN$)P{2MJQi zd#+r9w|-4eHIzSc`+RQh3CbtO$|YY+{moaWjb7Z>w|MlGHLW%Hf<&={qAOWV zOKojS&7(Dqj6Ia>V1Cd;z*-avdH6<`u7+GgPqdC6y=3(0)(Fn4t&QxnM@<9{3bVb; z@2JpR2Xq@q6N83M7PJ(OcH7$+aCp6Lqo4I;oNbfY=OpcO?Cq4@1l?%go{Y0??r`~h zu2u9_tM;=c-bY8ud;2kad%WEm1D=qFF=W|FuYiU8YJG3Wf{DbdAH&XDtseyRSkNT@|6nm`JfDi4^UoSt$;B2 z0Oj@=y$)k^3H0Jl=!eswCoLm=0Ryc!s3sFE5-cQD;x`kRnhauyC4(Rlj6XCe4MArZ z_tT)=O%Zzv_ac)$g(GL4nQW^T*C4a%O1k6;yWa4#583R#p)=V5g>&70zk4koJF0Cg zV`JM!jlGW5wj~m6wd`Bibqd?YUY8h^z?a?3#*Lgf?)u7!8(CWeAE-uN9;m)fi5QY{ zmCUaleE=)3AAJC4{-~d^W$oh_8`s`Od7Fv)6N%`*Rb+dGD*P>Lw;i-N1`hz72=Ei+ z@(x$>u@uT+_@gWg-c5X|pQWk!hyvxkHgZuDEgSV=T+@dJmP#KVjq-j52Zi;AmtVI2 zAj>TI@*y`()wnmT^U=M%DT6H z$;Ku1;kY6xh9NW69jcms$n>g^TV=9gpft_~US>&olRE%9k0 zprJ*SU%_ufXIGUkthW37RqA@$vL0nV)9*KZ>;|V~qu-6odRbfY>110EzZ0dLP8oW? zTIK&iRh4H{t5y6_kI&~RKSc%X70Rvj{LD%Q8KIPWFC za+56oh+Xw<*MMZ5>b^J$cl$-b+M{dv)#Xd`GZ-t=IaW=awbt_`aa?R&sr+Ixj}H`9 zH(E^ViCyVu!;dyEgceDBX80lSgjU$o(Dda(<&p4|5o+}Dpht%bR32K67C_Jt!Lpgo z_*m2wpN#Ls5shX#;|1K6>8ygv4GFc>FTf&He`92eWF5&5T93uhlh`lT;lPNiCOUZ`LK~T zgP!tH(@218XGL&E(H?Pff;mdl1P0NTZlH%WqDlv&$E{=0{D^#0G6f^w&J#wz%W2`^81KjJ{ zu6UxK+QaA5o_^Oj9*nrT<&S!lDcl_iij&>rdxo4=xjyM@*23VdzqRvu{=g=;NA$ao zTI%w$fJ+<&@o#;sYntI=T5pdvReW4iZBnj`&Znqq8Tfg<{F=o1gvfE|7GrFzjBH0KjGMiZINvkXnRv&_{5LVw|+{6755A$f~ zL$5u42gHY<&w1g=X|tsTEM^Q)Ggwe7RFaL@)LbA*A>s|7pb^FN84?pNGYx%?8EnR=EXfCUbf5G2dR+361<;k*&w4Xvlr(u9qFo#*3;kKScKtwoTAN2_Dt$Y@epGn9vd}o zZ0;NQJ zM;wixfd_fg(ffNNVe1Jp2NZTT)P9%%r1mpE%mJ7HbVr+}iO)}j9O@>&A5+crZVm6V z0?8ibG6ay_6wv%ChQNq2$6iT&IjvKlNnRsj<9;hAK)!4~u#U;o+)?@V_f20LX z{V-_z(Ej@_WU{u@bxPW8DmD?D>ku zZ^UBh7~E$bjLWa%9X3Lf^}q+6fK}ZIOJ+QLg^S@2g9i%Yw2(A|DmYUzDAJCt+Ey4< z5KTxb?#KyXTgKiOr1zp#gQ!#me}m#-t7kNPq3AiB&EaWmM=WOD5sSrXBWAto_F=Q7 zj*TN0Wgm&L9VqB>PQ#8RIBaGWu^eJOt48<_7szfH>ZDv86SH>3Vr)sQD{fuN&M9xF zedcqqG?lypPRY*URm|(QUfFY6KZ>0*_gslb0`YroljIr7t(Pf(WOv5ycSaTQJ<AWONQ+eVKW z?A|jFdL8T0d8hQ=aw?I2Fm!S3wixqGiNpFL?#~W7c}zG;gK72UvZKHCtuX@G- zPJV1;9p?&Ul#kVkqz%=vY$ji@nM*$t-{rE)6?ri1Qbop<{wdPCci;7{EWdk~re%D| zHkS)_c`Bic?u7T+rzPp>Yp;Dq8e32~=d$h&({D8;F%6?_Nve$CqOeNwLCw8tKC_<*$P<`+IqB(XCnna zQwnCG7w2S9hMG*;C}dNpMyV9gMpljBPEqHKEN!S}H!8~8m*6sN?CCz(fe=TwtE+#mb9*w0vZnx*m#QD--T$3O2i;e*D1)AJ37uWnC&pW};!rGT7Qv zrXIp2?hXe;=@ZXD-Pz(o2@iLv5!xw9ihPiGPTe_fuh*N1J!m%4i)>4rBQB67NxIAk zUnzsz%ooqDJJaLwdQ!26Vs#!jizQ2qG0pFDdC$FZK_sfFrhM(!WmA&P$U!%s?gbOj z&2*_QreknpUqxWNI+z3P4GY z+I(cdrjP#tTOT6Fb`OlOP#Sq~u{lORyljd8&ux9{uePyY=YeIf>Z0!VM@%!~_hVCq z{qL!($PK%_f)sW9{K%C5O;5@GMlk=+5rOdUctSou-r1M%bAUI1KMY?Yz6Rbvc25Gn z(oyhTOs7a1TwYKfAn$C!QBf%vi4MaV;_yd<7l6;&c6@Kej!%=beveVmowcoiJw}0T zBV>CkMge~=(WF|(1+7vd%gh*`lml?(S~fYC51xL+>4C$hCl8x$eO<1kEZ^SGs~BU0gp!l;PQO1p+QMX1qLbg}9)p*4h=YHmNL|;P1pb%(9uA z#Mn34%C5<*d{5xCBTma5mYj~S^@@BwI2`Q`3u>%-i-!rZkXQGTQ9wTyGdMTcMp>I$ z*Ezm3$%32*nc39W*M7PTCBlU^yj1)EZRm!U)?mj1rX$9>5wW^6(T=uuxlb!nBa&2x zxUw}37R~ju4sbsV5OFV~!4}RHSMA06YH8;Z0K&vV{B@Vcm%TYTwA2SV(091&G0kz6%}GcJ=f9NW|7#ot^?b0v$cD+ zxt?yu33WB$h!^5h47mNxhbuf_sGC&EH>*Z~D|W6zk7=&A+TzIBf51ew+Iprl_FnHa zHguL|__V*06F{^6r1=bNGssbXf#!B=iz%~Sca*gooS&sNOUK?w4|8M3l&D{m#X#82 zM1viu8$Pa`jq12|D`c^u@1UGSpX~!45Z^=J_<^kGfF(B{5oW|D4T_~uNCuG=GG0aO z!tq-mbpoxarfr`a=orVdh|zrZIQtjZvsSb}UFbOPY_2#`HwAXOZC&f~C&=t?sehur zwRHub(fdR%{8{!7=NC~Sj?f8*jTAbkSew51I&q2m^WBw_!pQd2`zyEK=N9MThOgJR zGWg40uWzwl%unZhdj56i)(8BvgI315h8`I;ST8u&yzbOsy)bfZOZ^+9qQ23)yJmRh z>~Z4AnUTIY0B8aNL!V4hVd@c^p1+{Q|JCumEtP$|FMGu}>~pe?wrdZly|*g^?efQ| z-I1YzR-j=XcixVEPW$3R;&b%I))LCuuK)e*f8IO)yaTuwK_y?rf2wUKpyzjQSbru$ zpyE^a+JI~$1zQ~cVEFgqu$V#|lVVEnbwJAmpH4>e4pRbqRI=b;u*XL{2YcI&7#p@9 zjGWtiNz0ZN>-=o*Tv46Zn>7?8B+KDgtZV4MI;&#r_^OFjtW60AWyLxek23N!coP2R z#EIowr>+*+!YOsS><;VonFhvk&3svNv(%*2FGs(@3&6)0k~g(RT>AL?sipKRA%BSHsqV_FS@8m@<+hR26`;_So?6d86Mzk^RX?N&*;^)x5na31I9ZLst5gH=`c z3@Mxi++i-iacw+)#P{-csCV2i@$61qZzF%te)dW{q~cB3%ljZM)}d>cmr%|oRzijw zonPxAD&@N<-`&p}p}7PJdzIcR$V3t`98KfBVZ@^$a$OW@!q*9oqM7i;QAJX5bgzan z0+C341kSC>?_`A~fA>3)aF8#$7!3Q75YQ=%a|LH8;s(&`?|3%M}-~0z%~K z9T4L14ek>*%jZxiD(#(cPT1EXZ1(7kv&LwKN7N@?W{`@&Hj{*YTZ0X+J*?VdnBqdu zKnB;sSJ)0z2#`oYTLfl}Xr7$u2*aa?%x;7a5#58<8CLs&3dyomLxyB?w`>zUV%iR+ z+u<~HA4)Ui8yqQ35UI1q>RbH7v1ZPjW5fPisvF0fXOi`g@7VO$danh#>ae5yEZoPNe@V+1*|2+|_5wx`Zf2qW@d z>LWD2-3Wl>7i>FH&>0{}ySC47+vikJ1@<}n*&e7LwQor58aGA8_=gnXD&OX#prnu8 zgAf@-$p(mnUuet9Tj_)g>e`gG}w4g#amZ zqMPWB57VLhc5hi%u*!3O_V-%4=Cg&gf1MLs-8;AaRSyAsvV`!UIs_>VM}o50eiAsz zsRWcy@CMsuPC}NBnux zLlv8ET@Z;{(J>}_PlZd`de~-8;}IHDtw?57+DgID+yYjoT)q2R)YHitdZ-49Z%|DC zbn#u-4j$OXS@>AT5~>s(wa+utJ2)Ju5+for0tGnF5lCilL&kZrR2mMo-VX9YI&U3@ z0I&75yuxM&l%LeD|pQ>=HNa|9TBnB{PT**Vr4&&Kg}aZxj0 ziUk>yH++ptk6)E)&=PfL-6ioYhbk^_*!W4f4vtYbAj5J+FWU%d-2jYSKVhB?n<~gl zowTuW*+*+Vv4>xMm1v*d?@I9%@l4D?h(fweBwV&Z zpaF`-XH*$-t)Qk;a#rr9=nCkbMuLq3!N{=R(7=XMv>WAU0x*-4$hbGHXFB%}g(Q3dkx1S3FivBfW3hTHvww*`2xpM2mIDCGciRI&$ps zZ67JhM|!pPlA^q%RqL&-zx{3N-?jfu%l?u){>~Bf{DVJwjy-Lv({J z+C#urQejEZ=&_w5wo02Nm5zqeK0~g;CcdB;#^3(V79Vvzu+zo`ob_$phToMRxa|Af z5Xmnel{k8w@*hQ|Hs~ERtnkbYu`*05P~OvbuI`IHq5nc?_D#c-{n$5uEMx;f74_? zMcLgymCc=L`$Q&812n9Uy!(9l?mMII9HaY8Z)fGXpDCV3kHx}xz}=6q`(&b3+eQ-O z;)#eCjkdA5cD!&mBF6^5RYIfLU^_MWpB1S4j#o(?&1b_w2EP)0`{7e|MPCxR& zm~|Psn3%^Vn^*X>ZnguiqS-9gTCb{Yql|zq$z77i7i>&uF|W+|*Wu~020iLxRfOwk zvB6@Y#kK;<1}sDuo9hs3L=Dzzj7BGBxQ%&YW9ksE@wOWgFZk3akjw!inARD@A!3Ao zU$I)#H)~-J*EYNK)s7O1_a|THnUGT>@zi?6LpA{`58p!?e1|v=K)1*eul8?j$}C=?aHzaHlt_(PbOft7}4fY?f2gq%(OO#Lrr0S%I%>j6}$2jcx3Q> ze8d1hHNG~E@6MglZu<7 zXvR}LnfXlc7H_KX~|CQW#{J1D`mF z5dCSmg>5ner4{lSf`bF03!~0Vg80;YRLvu{2g0Kx3gHdYcH2@6i)SL4Ji=JSQm~&T zMaqs`+5KQM8U(k{(IXl%`Forwcdtl7n4}WAoz!b|b41(<{J1-;xuZ2W_~PAX4@MId=1x-aId75%fOy1rsNL%$*^YmPPL za81;$8E!h8ut$1~-$TTWcm4)v+>0P$!$#ZHYZoSq2e^P)}ztUYn}Y7p-sL za#6QMnqJgm{k=>|g_5=5o<1W0ZH;?eisT9yeLdmYWGE%E-u@W;qTFj}XUn`{{UW_8 zRT~d>_JsT%7vN9sqgTb_tH@rfAbzpMle=(#JjbqG-QYmex_8+!?r!l#%nm01b_y5- z%$+cjc6b2r>E4R;wgI#l9=7k<=HQMQTJdYkw&hUqu}u~eiKf`lo5)N!`^8>lR^N0< z4TU5pzh|Ku3UGOmV)#YHx{Y^J! zFFI1=9z%2T)Aq!Yi3dC(?h;qR7Ikp@d1!TOXvT>F@oZ}v!BjA;;6d?b4BO163j&Id zuYmHCL?dYmen5o*%YfcOtkLEKWTGOF=s;n?6fk0lV^pXW5mus1l*7s*Hw|fhb_|p_ zLxG$&M6_42bPeyTPP0*PiahS}u)|udRlHb}W|4ALO*$Hd%F2h3Sr?uejp7@V2qLE3_ z=%nbTO-U|Cl8JyXUd!6+Jk45AQ?$CSP_~aXHu!^e$%GbkNt!9j(PUlF-_XeVvZ1a- zxH;sn@deVMCQn^EtBv~tiDZo}tIig02d_RHv!yWqNhWcV%A{y~M|yjlW*pI-!>SL` z3MbT7*ah|Dc+98(Z{|Rsp&QWL;SrupECAJX!%ODi1ybZ0sz3)YZG&`u*~$Cg7FczT-P@Oa9uK z=_a4srRl1uYhJT?@--0s(59Z>rW)Edl{_Td8ctRbtv{}73ghIT;4b}nw_2YbwP02% zan<7zZdP5FUbr~pb!nRWSZK50q{oJJw=cW=kk(A&cixiax4x5YS-7-4>vQYI^DZ|8 zBPtSYgXvn_#SX_HXWKNmD3Aa3ukCp6qvD5R0y=pDR!u)>-;7d|(Odxg^?@KYVYE>* zlm2B8vM)*YOg3p&Fr@bB75DkF7j-xLx;GH;S{uSI>aYxS&rA5xUee8ww)h)T&@Wz3 zhlkeGd_#1{BACN-sD1j)l=JtV)PnJV_M{eyX;$`kS}>;nR!7EmlppX#f!B)%Sej4v zgjJ8H+=f%B2%rcoUxM*`yZ8?1#EY4>Y51>4!z)Ph>#q=Q5N?M=>DpTxjC6q%P=G-S zf)Mv(q1llGTjK1o+6)3UHfG^Ag!9mjZnraN0u-lk>Oz>v9`SUdLu=;l14$sVoSlkN z3fhB&`kZOV-OILczGc~R%9n4xOcXEMy!ld5yma#~GP&HG?(R%58f}P1gKRtY=jL+R zplK#eGw@O}C0l=e;z{eTK+5}`JoMJdh@%<$*ee&^_kk>baNm6&NOiReA?I0rim#hn zI5^rKn9x+1&En>eWyaa8Les=Rd-UMGIqO-fj@?t6&5q4x6L7W%T`sMcFB&s;5$B7> zj#WLVPKuBw?WbJlY<=g=`zK%{;5)Tb#qptCE+2kKoRP&UE+57x z*#uil*iY&BLEO{ldX;h*gDrKh##`zJsaB6;3?tMs&8-f~FepF#=%bHpKgX}cKZ$P4 z^`_X#JfI6#QxqthRLD_f8iaO+U;;ZOpB0=qP{a)2%Ap+_j=JS1!`v*SMb&^3#!N%) zo`B|Yu7bc5RkT+!k`x^_;-~?p7*{>k3$mgCeR~uCu?SREzzZtAS%q82TCP>`pNR5J zkxz`LY{&B(t9)#h^=fpLb?J@D_j#3OP2s-?e;kcizj>ZLA!E`H0q?%cFIW||%B<<= z+58gHJj+MZ%Zm*5L8J!oE9>KkWvV`(tic#zS5e%>+Mop7>>Za2xJ{S!p(a9QdhN}I z&W5)pT7$X;OrFC2RL5d2PuhCgk`>XVxdZUh4Tj%;tD%!gpNzm7i6yTiiyIst}wjlWrse><+jfQsKn^F?ojKCTO4W?`fXA3lJX2_`*F`}jn zpe8j1bbzc{j#*Y_+zM8hK-ms^( ztJdoaH>BEXqhg*P)++iun|6leG-GKwv~!ckC$@^vc1{e)TN&Fb2ii#0t2*Wwenib- zE`n>jF0ZS*+}S#;#%iL&tfYHH#4m?n+?FwSYz!8^oWBd8|v(h53%B*p_X+w+Gs0gAYch(;7hI0d}XT z@J6FC8X%r}pe1$ZpjU;}w3rf8F!#F+eDEtm(q$wSHIO+K0<{*pbKnEe6iBfJj|AHU zoe2dW$VGu0w;OCC1ltW!2@}WApo+=Q))W0%BUM)wtQp@Ft}_}E^WGR(PtY^NFJUrLposmqBTG-}}W+r9Z7LIazvYE7LCcME! zIw4DS`P%xL09rp^8xB2bS&Uovv9R?=JI!WgN#f#nU!2gxL!Nr;xUV%Jil_K7RRtQM z;!C*^e?$w5dk9H_OqOTnAYJyd_;g*Cof>d&%*G6BXW4pCZvm6vC7dXwjqX0B=xPqaVUOPpzo#MfhCRr697itD>5nuoJeoLdattan`6$B5ayLm zT&F8~NdILx{45J2eEY{|F{(9h*pt~Xjl@X*@}#TV|C9fcr2qPf5pwx3_sO!>oz3~# z>(-GsecghMoNrvPfL!(qZ16N8nwL#cfv!@V9O`-Bl0JL^0&^n8jQyqW!7Xm8-?jeR z-BKu|%0qwP@&`Y>@2_yzcmis?nn2NJL~G{KZ*RQmCHPga&vN39_0DbA?+`n2$K7{l z*^9_7v9oLE*gmj1FgXYnMe*1H@d0rYv}XgTs5a>TRkp4KWOTbtZwiKPT7qmZq%fEP z9RR%s1bDI)u5xwd5#N@5F+~f`D^vU5D&K1 zscc67?YHq3>n`g(*_=D};HxJcIB~qMJ+QX z2dY|~ZoTy*D+V#@_}9M=-DD!H^$y??4<*eGY?&rUVkESa)jgI}w%sN>D5yIJBEi5& zlbapV73V`&?`Pd06IhW6`FarvWFp<1_M-7=r$-u$Qg41QF=5>$dNs@Pg*+}%3cca%!!c=i3_7Enqw@_<2=T^B4gATW zMHFflze_oL4N3Hcm@DXm)z(sPmF;4Y09@P`bg__+ipnoId%zmlycB<+j?Tr{;fvAT`(Yh&@2{I6^Me}`dTsv zBswB8{1;0PG6fXhp*N};7;C8FC0pH#cTQkzLZ?{QF=-ux%k)ofOa^pY z%h-1SZNa=u!^`BJ)^ReX9R}P|x~dV5vc^=aDoMJxrKz!XP9n&R_%isCdB81C!7aRz zK3`C-N`_t4^V>RS@JZ!Q=}ug!O4V1Dcg0f(1w(RA?BGi}$8k>6#b}J#7~y8@KvM+4 zQ5@gVplvGx^BjpfZ8wuG4bV;!XxsXkQ=7^TFtW8n@7HDx)}>L@{6)Ow)NV1*Jyq1Y zQr1&el^r(!0Pi@~7@fr$6Qje{qpI7w*e+ksdQx58P!te`#yX0_rvsvJCaq5cXaz#c z24(&5KiNG_iX(?JynZ_2b$CHawBB&)<-5)b!z17(k#F-8Z z1RA{Gr?|&G?!JH|_O!-ljf%xH0Q_3-7#)zZ;Genn%<54t6{GIph2jqpEr0Vbm}x8O zfAJ0YeQ6jYw|N=@fdip7GN~b1H!*I;`m5`u8g9MQu%aOj7)X6>>$0l4uWpioE4xPn zWb6Clf~Aeko4TqyF%o63(POZvUkrLPv3Pn>wxOMF%Yvgl_o*Sxdggno1eD760R44% z)a~MLvDSnKnpYa$?1R8jski%K8B-s3LmRediDFpTaE_4!W&>Ii!x4=PCJWfOeE=f} z!v;guZd1FLXo|feOa|kz$M+lW5*K(Ep1G$-ijLORAH$>zDnC}3_iK+?A1idG{J{@? z@PQk$J%RT^Eo>@lW8uzk!dxb+08XIMZ*v`mBdUGSp;o53KTq`rVjc3gQu z!JM@hp&Nkjd32|`AABJ53%2Qs8qjuJ%N6V6$8blqg$MJN2S8_EgKY7`Ba^i)$^(G9 z-3m0z^}>xHMO$p+jS=EBq6J0VYaguvOVc^_?`#`NHi}g`fPI1gmAqHB=ME;jJ=%J+ zXj@`GKr}?c3cNFj(^P`8LAnckTq9mTEH9FlgXy5$#$?uhxg9CB^DA_he%oIA`j_0k z&8558cDGMET>;;ILiei2s{|$SvCR>3_vaHWk*)q71rptF&2)RLnI6E3+`s!-y~i(K zV&fp!xY$|NOluqargaM2&dy|K9}Is^SUv>t*RaJzS>noGCGwZtwnSOvc^ESfxwVrN zq676L&2*V8BfuG@zG;{;P&wcBuxtFuPVx`dR(+xyxN(^8nU>^p1eK=GcbSr z+H2N~_t7HLg{!_oU7YxDz190U*g9-(!x4TsQ)XMmI7+iaU( zjwGg0)9ZAy%{-~|>`sPH9z)AM$?jzK>b0glqrjM@}J_2E+d+V?wM92yk*phYptN&f2~RM9t~|I_qG)FeVFi zOzkcFkB^xsJFDrR4vO)621L^nI&;gMJ)-H4xK;fLU-Xt+!Z+Ud&Dbrs{GS*r(?IC) ze>xP{y}QRjdF@Gx`}c?tu9l?Lh{*iTt!!m-e$73HC6_N)@Z6do9Y!)lv}r&9-}GRe z8m!0Wtb;^0AzbhxCMJ_JV*=~!fMO+A5o9~IGcQ1>YEitFfDn9b33Hi;%BTMy{wM~&mgo!)us7*P^+ zT@C83hc#|(Ypdvc1E+e>;OkCPBw!$nXlh-8Y)@r8M!7N!1EDy=4a3sr$BB}T+&(FX%RT) z$$0q6o5 zZFJ2xhRu_ya#tW048{VZk+5YI=@dFR@>_n|Il;-X;PGC^uz4g^;-QLnZ_pU?qKzLy z8&BM)jb}nzqjiSQz)FKy|Bq3lRaVoo8>+IcQu%&xuPTLdoN~vtUD@vb0$}Y}W+eAo z6bI|#K_CX9qnI$9;H!igg~5!QVImh6RdO7Vd?iUK2QO>qcD5Hr_FoiUIE)GUpReG; z(9o`$kp&Hg9>bh&aWpa(FawMqIQ06+Vg}Ay27LKvL9qLPU#}xL?A~X54l~Q|-)j+s z5-ox71A7GocL2V3segg>7kdE&Hl{UHSpmUbwfCR&*(RVyXk}+w!};F-8-2Gob=Xm& z3PnI)-$Moa^gR1lz5RcxZ>>FDzK1&YY_WZ5g8frc_-}VT`;QUIsL;%AYS~RC|B5;Y zFH{~rRT5#tR%5+}MP$hWXi3Nma4&$=h=)ajPV^pukBp570N7^&adKL&!3k;e49nX% z3^32S5msrh{4s&&(++3|r(8d0jMMPH!LtDeM1XI^{;}pd{vJIwQq=Ge7~ja zefW)~ZxHL>Y~4-lh|S+jQsWz(4=H<&vH5h&x_ji<*Hj*V<5c;P;?a`u^tQ<;H7r-FBG5$A|oupaRed!0+D3{XR|L2?8+5mK-$lvPBD zt;3T(Vg_*@$EkLl1^X50LeLDH3;Y)Jes~n%8G+Uh|B(ZNfvOS-i{0^-ww|U5HO-Bq zCP3Hlw=c-Ibdv3qty^<2Q~EZKX==&z%o_#U35#yzSM;~#$h7M08oO$;$GWDmYhITu z_O7X_XPNkcxwh7-(Wz1CdIQ=`Q+rLq=fYIt8C-Yg=Jb)rWXkG^aO!jttL)@j)$ya7 zzn1ih*13{9Flog^m!J9FvyYu`c%^A{%2cMi3oB+O+i&2W=#rN3ku@qSAJ^EfYmA+s z=|EtTMLNI90`O1A<2nmA;R}y4anvnv5B&@g?+Ru)bzd3 z3L{Q*KSTu@3W6Ggppdsw!&#f|;r)cgWPcja3$Ty@RU+_PfslW4|l=_{MH@b<1gdVV>@>~Ch_Y}UDyhTHP)t#Vo^)1P5C!ZOCq;r^4L{f z&0&9SwY>ybCKEK9+78Z-+t5~TV%0O6M!=Q>Va5_wRm*Z!%dNXs#o4!6%fuFZS&r@) z^*xk$>GEA~NYWd-&i_rOGkO0>SNu{Ge|g2%eo}2+QkhQ$$g8tEuEthS?0TYZATb53^Z^=UN8p|%V5ydB^m&5 zd)fzV{8zpL;XLSp(_ta0=4aUO0$AR%tl&24AXb3u>BCE2GjL}8Pi zKQ5$PfJb{=K%jdglRL3Gm)paGhTafwS-WLLW2733qZna{tKM+!oZPyZ@sLa8k*s~Q z$AR1Q`Zc(rW=e`Ah6DakO|=nnW1hZ(nThv%B|YFD)sv{k8ji9xsg~>m(H$|dYA6o| zYuNp=HyNr)SeDg*hvpI%eHg{6W5JM1vR8QYHG~pLgdJ<#K|>T(iT$3&KjVp-P|_<~ zGi!o$CyyC%qh5>$_Mq%Z1gaA~qx_&$uQih%10I5qQC$=A2f`wrlHIRx&F_tv>=~;8 zQFhS?x&bDwPSqKWJ}fk$x+J#|#GJuVSbIX^Av~siSI{!%><;e`YcMaxjrdv;s0~8e zA8yYYI~`i=mGH8C&*m=htZ{PFf~jQsIsCZLQ$ANF61g~~Ir974CldQkV73AREC~+t z5S$38e1PnL3?m;UX{Onf&8VO*gGV7y{Mj>UlFR4IY-+45G{x`hs;*BRwHn#RTw{7# ztvPdgjhX7F(v55Ol+Kz~6K!nB)Q^#V-19$)Z~zV?p3zK-@j)A5k#=c)T_TrE#jB^zLdsZ=hLR%(RKKD5oZ>1!p|E83m-Mu%*es;RhOR^+ z`IR-4<(QJ2G-=(WN!`;{aK7R^%BEQxGE=MLvyf8BT%xYtKK01lFZWw|9sf2(5X(y` z@CQ1fbtzuJJhUuk-ViQsIxzn$u@?Sj2T_?euo6!Y7?PA|TY9ZFY6o*s0W8bft5Edr z8(MX25yaEJpqPn;iYwPy)xsFdL<9j$T5AYkDc}ucpy9>8N$e{j>zFyQ6K{Bup7(b1 zqS#4u<7{>8$dKfX`pO@5Or%(f6UCvGUQ?+m*3vlvftAh<%4w1p3ahPyhpRWZ5yEzz z%2Y`Yhosluk9pqiu8y`uermd+<+oP&RBa`Wf5*i?VWVvPTZ-RmX+;o?A}kA=qZm0gt#XX;8FgvE2L^J>uP3**86SM zZyREy7_b?so?s3?$skP6KG^kXY;x*4}v9GIt{R`J!M|dtmOSo|$ZJIz5+7 z&u^OBctqpA1%dWBZ&elR4zJ%hPK^eb*u@aC^fN48w2W##q-t{2A*ZD}*fN)U-=a3Q z%kkS|A|F}G zc>LwNmolcw<8ZV%OBrLaA0C;zjygc77+W{vGZl(|X1M(O%D3vX@Gx^^7N-lDE;73u0Fa`W_6--DF5>Hg`F**t$TBG)h5&XhERT z_FX?4We)j{K!^!yEbz3fUT}zrkdQlRdRPKD8#q_k>JvD>_?V+F5S!9m!EzW1Z|xMn zd&bwk1BBA~FIfMH)~$YF=?zn8#)m%c;&~4ZV@+YKKOm=)G8X@m(^=X2Pj@!i_rA9T~ zZ$1lv>G-i#3KtcBHtT9Q(S3E2= zHe|m#E*$cy0p)FAU`{%8`8LkCEzfsgMfQ-MZka*q(u^60bGN7&L1{H`@!|N)>t~&{ z@{YG8>8(3f?wEPQjCeTMfMFAxarN~64NWj?Vcs@v7(e}*>2U+Crb01mI_wnAPdV(c zQvgJ?7mg0|m9u89;(XQ2x?0RH(x6n4FXR^S&*CcJ%;`VS!GAJ>h5Z`u@5jo zw7ZL!@(WC?H5jUr5cJ|Mw;O&TSp zVwsw7LF84U*Hh@}Pv#M>@VLB$nU;7EK40c88)tf)%gER^gsPDBFM>6NZN#l zj7$_q0NF$bDr*2!0ZF2%i4B1AXxf&&To-T`1=5FXZbR@T9Gu{|N{BegFvbPMXK0yW zYEuMzQoMwBhd#>2uQ}B4jDlQhnH5ukIQ%81Xy{;Wj zBR>*d*Z~@v(A7RiWI=7>y!Hbyiz?`LyU;_jg#SgrC38W(d@(ia=FQ6h#RFGwj&sqS zsg1P9gMnmII1p}+R^eaW;O5~Q$MqyQ%cWa$bzMhEmH=!i_Uwjcb0GRi2PDX12T6+iMSO?v1U-nML-}jLiq`7;{4k`j>4sk@@qXL48 zXfDi@Ay_Xpi?cT#kogq!DY#YpSQkOfiTQ{hx~&sjwc-D?-ZX%>#ln@no!jSmu->LD zopR@d%%A>*K>8f(hnxjt!7y5no#$a!V`097^l)Hjll9{7bdOZ1scMBk%?$AP0Ug(U z)Cuc&i}kiuI?o#jdLIFl6kW5pGS-|mdYhcJ1OA2nW)CM@hKi)Wpm#gF(7*826VRhK zPwWYPU<^Gj?9tJtX(IkZNBl~Wkgf8glTXgOFc%N<@x0kDv3hG<>@dtp z4C+%COE{@`y zJw%PPS|*|K3E(6;Z;RcGKtM7~V!(ulD49)A27ooOA!Vuz1R?2O5R7|XZDhSB0Fls# z`ePNClQk70LOvh=&1j`JqMVO8keay7`t1oXBb==2)_-ZwMiLQx2lig`3H>QP3yc(@ znno%sr&Wx>l`ipo36RilBI+E_SoiVPOG`uz3;P_&8BiZo7haRb8!)!diD%I4I+<(? zAWbRS3SmmodThdng6I*b6a%i6T!{8Wjv%9uJvcss`tVLhu$DQOv_~vU_UIcY4~(1X za?Kn!FnJ?Eu{O>)Z{o!MiRaB&EWLNPcGn*zYoi)aA6E$k#_n)c$(A($bmb%L0jyhE z+bD{SwRM1_*4uy9!QX#ZlHUD3Yq7Q|%H#NVI_i(u)h>_Mx&?QmcOW@8fd7ebyiJSK z@KPkSux0>37FFA4K*7F)`Me7i0_iN&u$fh~z)I3hdJ?chzRhH%QpL zp?d+>+~V#if9+tm7BrF{orW7UKO@Pv(hWy2NHurz6T+-8pnKvMa$I^UriZT;-zB< z$5l(3`@x4ed>{$iWx4j%A0p7%q5RixWQ5Dg&*hf^$JfX`MK{*o=}E*sFNfWXV$w8C zQiC$^I9j0TAaoUuq6B(_I;#WBqeXVNQSC0goEuhjPs{t-6qrQWJd_?Bu0V(&Nx4bQ zms{9?Ya)$U_YV>yb%d)DRW*2szNWk&E66YCYhf9e=|Q~3KP#3v$FbIdW)|n$&S+uv z*fw)K!~QtK+^6AXEv#ta%CM z42uFBg^P_A0H~>es=mCc4gv+^zy5U&@LG$lSk<%7B5l9w>nw&0_Ag-t3Y3-xivJiZ z&;PL)7FdhnjB#-V|1B$JIAOKtfM8K@xa(fQ_8RW({EM{mbIpKKmeHm(UD{z*r{WbA zpflS7ifok3K?~-tY_vL%E-ji+6vkF&2L`x7G}_Guk(En8?EAqPPQyY)~A_W~%N< z0B+i>fG7rDj;^6ef?NZwN5Did3S6g*AW}8L#1bL@(RfEdhI#wGaxQTlH1JA-=mDU|;3 zDc#RmGPvnh;38$VY|XIU-1g{lguKI#I{IRvN0Zm?aSyi`;1tnj1EK4W)3+#mA2?| zvjg$d&B3bO?ViqTcXhoanr#Lp6ziByc-povuPO^*wu?$rIOsG>%nnQTFW-B@Apn-#aj zIbsbBom~-GKt#7m@s-P=N_X7rsC9eX!U~6HS8LNmyerq~Y2RJtgM-UvR&2b@AKKpL zX`kM=E$ClSRpD_lzNrm>4j#B>3`SQ_Y;G^_DfPFwtDqc}E5;ZCo@JBUY^iEgM4!oz zI3>cX*dOd zPFb^Ff@>vljz$7wZ%)Q8uDGKw=^gm&z!oNa{Cpk1)BG96I~VpD=Jy&=YRYi%GGq=2 zFXz9kACdw>Arg=cE!+zpXB*Z96kC8Ui{{X)JGFk6HV-S`fwm{ z@^#iUel?YN)V5&b@4f>Jr{}@jkKw6_VPjB{=!|D1!!isscrblKx)kjpM}YGR$vy~Y zG`Lz?BVG-?iae>s(ItgqK6sXJmqlxW-h{`L+(?@3K)az9=m>33KuQZQKz%ZlRd+OV z!-I}kXZK*O%f0i3*4m0rd$m6{SQax?^TH}Nv%jg)U+V8|vVjSkU4qpji?Y+(Gf-J! zRs6w}`tfebq%^Lm@>NRw>_Rpm zu;Ih#-xwL_tqoIXw6UxyZUYy!8Z7SnN4>t5;f;+BqgfPh+!0xqY^w9f<-;r2tnY)} z(@6}1*4JbFBy<2mtX_s{(5RzI`mhcIN%EE+C{|#RwB$z6Mc{>qwt#~H0$PkOa8C*- z)ADj$y1a}j-qFRk@|*MD4O4-&I^Z0Wj|(8t`7gj4&(#sX%KsMHJljkk1}i+cX;O)FlXXaQ(YTrI{Sw>Oa+Dd;O@rgLV8SZg#m@&vAC#9jrrW;&*~^sSLu|=N~V= z=Luk;4p9wynpWslOd)N)`_RSQ%!*BnKbbvnl4qM%KKTISzaxALOY9Zf&byoE?>=wa z7188x=Wl$S?nU^7FC#au3Gt#a#Md_hnWr6hg)24^Zo%AxWv7CsTrq3kl}Kv&4ly|l zXIw;1xO{xLpZOyEYhM$LV*ZzQxqNV|i9IEm%FRd$U>{LzPFcP4wm+HRf;RF$-m89m zTh*GUjNiSc`zDrHJ9y|zoQ=6eNisFfUg*gGUP3JsvX0&-d2EV$6Z<&K$fE1*pZ%=$ z(I$0o8GEAX6aTC~_e-Eb99B>f9MS^3I5(c##Ka(!yl@cMiI7plBJ1G!H@>h-z4gRR zu08ob?)^8OeN+8`YI%bB)nBp~@8?RGL47N(ZepK~GoX>@zmMd8)~WtJo>vX*ZK9Dj zmoTm_g8y?lGH-WkIS9npG~*@wFt8^t)=dhNv=J$cJOs>8W`3-SSU;Be9}i8T%TxJ9rbqkHEZ0mKP&M z3jRa%+TWIG?e0n}?G2Wd1^QkZiY*O?!Fa>9^{lRz?U_$}?(+HnE$!Z!LdwI|mb%iG zvUeb$EvvQF^;f;~t+r-+bKBALKvtCa)W`FwwA_SIMn1}an367xrjBH!BjG_E&4lmI z==u-Q?a@14IeFrxbX(hj(+K##C6)T6+VCqV^^j+g$KRux#@Fv-zu?aX$1P#W7A=zy z207BQQKzX;d#?0fV6}(YG~i$jzE2yRNYI;p7$Bnxzi^wy;jr*yl`hZ22EUU`NE+Y! zB!3_B%S(_=ug~f#aSc%4NYy5C&)7wcC@z3y_9tZm10qc z{h&7Y#v`1#soDn#17Kt3`}4mBG&GygG`6YsK$vlz*wnJFcpjyn;4f_S5F3Ivu*aT45 zm;0om^v4~+zDrFoH9Ji6`p1>}2k4{Oa4^pa!?vMhJ^BDIPinI!L-1gISba1)X+dI( zLIW5hBn6jt<~4bB$jCaQ;4iFfTrt~g>YM6KWd(kp1pDZ&?cKD{pxYV^UPy*vXVBzbHI1Bg(8 zbR+=`99$R%0|OPKK5sF95JCglJlIvgfd9O>K0hdOW8x%LL7^S)Ftr{dzvIO^^V&umgljSxQx#SZ#TGswZ4(H*YZ0` zu~qNW=(fA}Om5)@Y|tItcDe(7^Q2x5BLC%QO5xVng-s=0U5UbZ>UG%sj6}Vm8AUz- zjX)F+uo;O*ji}-r({uN9S`J`^7zZ`~#(-cSOwChe9h!jH*Q6dfrp2#lLDtY6wD4^o z&;*|SOLS!I^aAReMdJM#CApsc*Kyo+`zNpE`D-V)9pJbF+qTVe-0Zd+R@-WAHzGt- zzHv1{{``G3T=*$}-TwX8@%pw+yDQJnZo@BpX0_FNBO=n2;{!lg1BaqHcPFfR!{AM4 zYVrnN6t5crViJiRk;3Tlgf$JFhX_L2dEttLBdnQ2G)iK$C3wjvll`PHHiOW*-nYxG zHy<9_ei+tJ;qyFlq6PI$nwILDuv~HM3%uz_;q0e=s{WSxoZCoS^*29%5Rsnh_1!}N zY$Fc#Zpkg5Fz@Efbik~!v^*VPC$|!u#a3BH86ey-M#Yz63@}K7dIxgb7tgL*_`mNl z1m3GYfs;3|W$nZeg6cE98xdah-VTj3kPEubcQyXx2O3=!FvOdH9lQ>liXw2K)X*G+ z*r2f{z5EnGCc>^2OQLcSgxi?MM<1uo-PVp%2VyJtgUVX3^2VBq+>6)B*UW(`$G=T$GGreVCK z9FqG=)QP$S_zb*^5RF*DW)5+)kgM#{qKOHgl9o-4^N1aY)6(%ME$8Jwu-AcHr1&E#3%<^N7kmYt97%aawu!%$#McF@qK?BWX;-+>yk0tF84cy5FMF7$kT#F9&xV(^ z@m*=M2S{s|6l=NNYH*s&uy6tPCMW~|muZ*6LZt%;bw)GF(KuXH5 zMvq6UTY?Q75r&O)NAxZ9G8lmv*nkOoO&fT?*;>W~=CH2n!Mf3>`G2ZvbdygE_VNmX z|0D-X7*@QZL5RkFnC5}DT$6)EUwt*JD1XXgr6nU3yf5r2teE<`*}3*v7GDv^-vM?f zcWB56KscjOunEg|aO&5~%V8?>7-DfAYc9VL6dKtEYHO7+Z9*UM(A)i}iuX!{G| zy+7C1n>Vnu<2tOoSm;9y4a?*4*8_nK;1YJ9g2=3%W?O?B>bEEb)<27~>^-O(rD|nM z*{?fSv;0jRt6@UM62H#GbJpo=! zz@hZZG3{6Jb;M~<7PNkcBxerIA_D_JhQ;NyJ1eabx81XQRm;>`YeVV!9s(jVCI)=9 z=2fZs?u{-RqRq=3Hiy$*4-=DM?5ebwusAGM~OeETk^E$;$Sgd3n1@=?Msu)mOi|cCgta@uC3+QG3^=>o08c*i0=p(H-tf ztU9yD=c$Cp8!kjnDQ&Opj@C_e0c6$_>f*ky581n92|*Ba5X*fwz0h=g%2D zzP=*icY8f92Xb?kR;}9LHwC?ktw)=aWq>CR+iE*6Kl8%#jgs9VDy8vNkzRnq8oaXX zw=6xeWA({|;<9i`-@oWFQlwWMgLMOS78#tVf880b_XDb%>hZ@2N7no1^g1|0|AYWJ zlT+zEzVYAHx8dED;VPEJswzRiBSmZS2P%GpJO$3?lzw0N=7Ruo1Jc{+b#M}>f8Um;@qU-G*Tek6!!(TK}NubtbSGa{F90UUPz546m)KSpCRys;s5zNA*6`> zi!@vj&*DMCkOtdE27gkl^zDB>h-Ztuo>2Y0sCweD(dNsj+rBvBWb&BiuZGpy8ru-UEh=e%p;bEPXV zL-Du5w1{CfT}hLus+Ct#Tp_wgyCf)DKA5P!e=7Dp%lDEUp#S^-Zfh&H>h zUp4@y6wLp__wgn}Ht!V5PtQ98p5kqKeD6>&{9aWDoTJ#tl6%Eqk3+9vrt7|ghd+X`oY^i=Li$`-AE zIHpJ5xPklyX)-}8A$M4PDG+6X&0Q|w9!9&OpthUe4I8k-AhT$oTXKcst?`fxI7zNR zwY#CN(&-GK`}-RrNMy9WO9`TOAR&5Bu|CxCQqO|=r$=z`SpsC!6O~7 zF0JrFbwrU#ZI(4b&rsHdNnI9|G<$aK4T%lM(T3k}nA1 z0q|_)0hf^(mCz-!De4N?0v;2d(Pr@(q9&;y@~yqBnx4^k`ztJj2=-SI=Jl$WY?WLV zIo@0>&`zQD2Zs?wOHl(IKq}Yx)y1f%cnobb@JK%leif$5@)XsCB@CewAK3j=?(Dtp z2O#I^-7jnXPf-@9_df-fp#Pir59Q- zS$VNytZ|jv18$Sa9k5%x#;8&57kC7*Mzy}=bgYe|TK`ZD!2rA`4J93j(^^e+VWC|^ zkAk8k7t@`h+7V2d#UofL$U@ZCDHjuQ-@B%GgxBJEvHofp^%RXt2;wDLz^^1%HFH?N z^|=R~009slLZ?Sl6G{1ohVJeiU97tS=}(=Js{Az#U0pl63%e2cJ!o%xcUnKKzI58@ zi~G0i2}W#XtgB&U@&;a1fB&^-)IW&*JsiT^QJ*QXq}T=geHWyH5qpr~mj*8C=0q;3 z-FZjX-S}Dj-E;abE7gC<#ZEtcPhmfQsH`f2#L2SShHl0kpZxTHVoCY^*UtIqGMKdl z#kS(3yY%s=e(C&=^QscceCmcKngVs6!MKtqRK=iV$NED=erU%%WY&$cVPm7 zh&nxcAlm5k)bt+Bc2^`kI2dWjFw%YSk@vm@J-8jqJS`g@iFsbA?_@#1#|8Erz6)h1 z-gw~!^^LFkDk_>PDtvXFotry5r?A&pQQ@skCeP~Z{IyrxqjP5^lhcJO0}TQE;TSuH z?@KIM9}C!hfhzv4!~gmg3ZK65A9TZ}ia4V?Zti@S(JdP*8W`R4tRy^(r|mybxIEC< z))=7gV3ZXEMN0nAuX&GUO`+3x)?|>M6T4@ z-u-okN^{lD#N_pSG_3ZjOA&5={bXWCl?IA^B0kk~>;b;9mc7niuZ!~!@P}DEhG++~ zDHLUN;m(NK%f4K_vkf;uC*DE{*eB{5_y>;lP1dQU?DhEM(qmBK^fmB{kmcjZg{#79 zUcw62c|5L1jA?!Vcp@2Q9+pFT&AEIqZy?uJZ4uQz%EyNxK#=6O>BPp&aJ8n&w=696D(2ImTW6yOB))O_Aryz zj}k1XW=1|j($~2^0;_HWntGN{yRjsPG~E%ZH%J&N+IgiA*-Z#V6pL$!B5`!(oFpO| zN4~aX$7z`k39bADjKv)h2h{#vd>hry5*-L)b0i?2#PF_g@xXP@Iuc0Sqp^$P6a2@7 zXVpF55aS8lHZFdH-Te*GU=R`D@KtrmUEU_@L$7H zM(>F1%hen=vaYJf|I4kxFk8`jllQWKd--yAfN@LG%Ldm=R+G4%dy*j_ z&9bsVJ-1<{h1xvyGM-@+8yd^m<>ieE*V)M_jpgdOWawa*H>_0e?_H0`0iN|VHuRue zRfE-Hv925D{GLYj-}c+u!#s+utM|;!A)6;&ir*Uvy40Vhm#-I1HgWwR$gb1Ag!>Kr zLzKn5gDUKGAb`I}T?%M`Y@|`0#<@;K4XwXG#a7VrgXk{e4D44Bt)fJ0agn^fjs$Cn zir-=neM^isFs-d_rZ)Gh$Ws3s+dwdGMcdV@Tej|PpRw*)PK1TSg)MEwR)AAtG=K35 zcQEK)!Ti?+9w_qi3LhBzJH$(y)62k!r*G~Z8Ye>GIid7cpaV`+Nd`Oov2Vbo;WHl(kI^H)ri*n zArlQI_vhS389`3c(iOrlKBeP9+oP6n9LZ4*W278*jtcY1X9VGylc-vh#jl~s z%ShitN;{YT&hRB1cggSwZAUguaNNYEjkMjuqbQZx{RNLvVFuaU^O5Zj{ZbTv`Ow3^ z6d+fjHktv5`+)8jp1tiO&kDk`A6b7zyIBzhc+@=fB}8bNkpc-E1BI<${DI+18n}s# z#h<}iPqyaoKrSP@oFCTtHK62HWI64HZ(|zs{tz(JFDbdI_)s=6+W-L(C%24;hhq zCxk5KO=S{dMFdv;(zM00#U_mn^Olw$fe_if((6!%EQIbHFW(0Zi&obI#mwVN7G_SpFcgtA-l zlgGGi%htVYGQBjoY}*%Dqzexdxdp&c<>~jxnDhu*_>j}5-MWMPvAO!8=lbm_{ z#qfc3n{)p?uxZn|n+66B3}(ar>`~K!?eWOE+7nN08aVOP_KZngwP_%}{ea07&JJ$P zzKM$mSO;Uslq>e1u9LPRdtj|59q2evHv(Vuo{~q#p``qTfdJF^-Mf)XkaLiSV+Dw ztatog_$DZvbVb|3H?ZNJ)*`2tl-EshbNOqz`||bNck(OmB||K2n6}k6fB44^o^q%6 z?irTbdk(g+p!*<@{PYk0tlIKCW+NiG@?h%=^YDGp1J{-FXP>`p^2AeEh8F)Gpf#!J z51`MNmWE;Pt*~G=D8bD3d^8fz^(z%d{~lDgB((-o*#_a4tNa&hQeMYkopZ@ z7Eh>}J}??61r@{vI%}vJK*D8%NeV@E;ri<5FnnNR;eWAKJ@LY`kN2%*Q{UWjakC%+ zUZj3*-N9e%^6lDT=j@l(ZE>5;tFK$(yz}HU|K`4Q^8ji?@pgj^8rQ;0j)7>;$&EET zD`z%v-p{))zw6?AU1wicRmzre6?9Ye=5Idtcz-ax5(tZu(zC-~)41;TZGGo0@$Rd? zN)&v|J$aR?K00ts=&W)p5}IHdgFT|>i2B9pfnoNeSjx8Hf?DaS;*u|HyM4J< zC~wKN8=im>?CmG8tqMt^!zEVNi0T`AnEG?8&uT{P30TqekOx+7vRS$RBi}B25Wt14 z{gme2_!88I|C9hvCb{gK)YL|I1&G%>PxK#br-H0X&&31a&T4{HYQYXag}k~?u4H-G2+tN_)e|vyPbo9D->mozHb4zjl}e%kdD47&qzo-T+%wT|wJaLVDB|*v$f0`PgAQbd*b(#Y zG}(iq*y6X?EcR&UdS?y06QTZA<3A~)AJ_tfpx!Vj@{pPAg8DOdUX40jLuv-(UC1E! zKnAfv$L)f4i{MljLQ2S{L%v-tRu>v6n44bH0G;dttx0l#>CacxAkd4RB=wVaLJMgk zt;vH77_v<0)!?r}IPN1fwDKEF)^^rd$vpzwY-1?D0lvQHkTAUydDF}SgdzUib9SC{ z9+xN_OBDB?M`R8D*dqn3`Pb-fp+aCq^+ zr{5$qFl~qqWD|V~V|y7|GWdCki2P>CaVAOiv}*UTX%bCz|G(z(WQjzAZAc{461)te zitsLYL|3#aKw&}TxzwN!k*d4!8vVn=a<7%*?ehmHUWB`$v^nYmO#<+dQs59h%W*u* zB%%?*&;d)}Sn##M1QhYffDiW}N;82V6`EQJcm=VO1V9Ui^#lklE(9JxZ=Ds)-$xBR zvtc)~ADjzooUCt`dxul-1}pn2`A~1S*E!;8x>9nrHaE1id!-BZaO$J{yXseX_EV=@ zFgxJp&OcXC$+fdi%k@-(d`OmREF!4C;t2{}?xDEyPTGy)cFvFpAEA zm2sqG5>LK3Y)I1SV?3T21B-&d$(1ih$(T$QaRd+9~8+1{S(ZmaN@lYgy}~f9c@waBpt!ny-9CPwW4(x)$qn3&tPJ zfXH)r4m7Mm`~|$sJSrQWCMxRVFy;eADXkWi;sKf`JjZmw z`B*!vV4w2d%U-GJbRvwg?@teN8!NlwK4$pX!bg!cLEe|&Ka`D$=XDHhxDj2ma|3g8uzmzQ$v} zz@NpdFfPa*F$B$U(H>D95{U(9_6Q*qg)5dOPDhxmI{#C9gzGc|gl5}cG)}O-gv_eA zEv$+(3ffbDoPSXLG4=$xWlLE7*Ml`k5IFmcCx=(eU$*)qu8Vg$@n8RDj@#Vdw}lgd zZgcg8`}b2Zi~YN<|I=eFx7X@Jzw0mpW{aawgoq3Y)hZZ>2!@>qkd+;Z1X7lCw5)O&WM&NWznmfF?LIk z$M9paMr!|2>a*Tv;8LDKo5M&dFSL0auP(F&v>OU-%F1Xjv?UzxFSL!A5337ps#cT* zw`KxG+0?^YueTZU2Oci8IgHaM3vC|9pDVNlw0~4+i?A>My3m$Nkh@iA8%u_mtI#%; z)UXNT%4|9{mmUob9|=v2WM;-PGjqmbV{>|ZekzqM?iN3X(%IZZW+v3y)KWaOIz5xl z7VncgINmllHx?SpW~M^}xM6x~DifN`X7;B?=9>1+&CM=pZl)ii<4q%(X~4Lx#A~Nv z1fRopv?PSUfg{*XfaPZp;yR{n=WyNok8H;E<0bPbhnm72-hcM}`$M=oTav?dbk`8f z?@dT3dH+?ban+1=<@=u}2UyK}NfQI;s|D8njTbPLuf+PV!0>&f;Pai|> zI1~$@XE6tJG8c0LUeb$I+mDFb0E!ou!AMAE-VoOED&TomvnZ=Ub*@^}pr~hY*1#Ge zmoy^@sue(jC`<=?L?<*ccxc!XWQs3^S(i+f%h+3Al4L>!sQ zPtBSpFiEo0 zsX0smVJe-=8K*PZ^gIqujSAG{XD3Fa1KE+xXj;yu$Fk|%K0Z5kK+2_4*^zxp4!<#i z8OP_+b7F3KVk#}?=0`?wBPlnZJ(xbC%wFJ4?QM~F@dS*10HKyj~QX~85J(bak zoc0Ylm(CuX7)gt>*$I4qICEIW4AmZEIGviGJ7UzH6O%tRCG5*gr%n2PW@cg}Cyq~y z&E=%2iSd2-$jxT5b2(uyb0jC=n;Ch3`cQf*d&Gtp8BI5)a(J)Q%t%@q&Snnf(jtg) zVA@36`P9tZ#M}`XPo6rIIwIg$tCAZ@&Cnal=$hG7HZ6@!OwFaUAnT004-`P(+3={N z^CNR(;9c_|ust_FJ3BRz&K3_?a}%I!DqFZtn3|ZG1=xD+`BZ0c4)l3|7G;?TXDw7)Jr&F^$zATJq zGNVFna$?p}WT>-KsUvOL#l$G_(z7}_aybm3k$DWsBVukJs3Q_V(x&Ou3|=Uc9ZhE~ z*)%cQ^r*Hc&f$bAZx|aTr4>oi)%%!i6()j>T4E;c>;0%v2_8(YD27hD8dPFqex* z`OO1oneeZ;ZvkT%pO{HaX=6#5%8X-RO^g^RB&q>5m(phvoooWRUN7Lq09-viQ9{xYJ CY%vA^ literal 0 HcmV?d00001 diff --git a/static/fonts/iconfont.svg b/static/fonts/iconfont.svg new file mode 100755 index 00000000..f6f082eb --- /dev/null +++ b/static/fonts/iconfont.svg @@ -0,0 +1,477 @@ + + + + + +Created by iconfont + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/fonts/iconfont.ttf b/static/fonts/iconfont.ttf new file mode 100755 index 0000000000000000000000000000000000000000..ccf533ec5e6b778fe8ffe17753dbafde5ad3ab90 GIT binary patch literal 49432 zcmd3P2Y?&J+4jzCSC>}ON>_BJ(@DDGYr3LyyK`^2U>nL z(1}Sx4IvFkfI#TsqlA{wKItea1QPhjY43mDU7ZVdm^my>3+JG(o(JM+$4pP3Vw zAP8<@KoEuY=?fQ4y3@MnkAlE@Q95bf!nWKJW7Z+qKMDJbS8rT-WcI~t^MW9a!bJ^- z9dW|C)py?c4?zfQ6NFnoS-*DWnn2Cc=Wyep}Fyk*%@MMRY{zhZ?WUC**Y+J?oh%6VZ^8K+kNoB_ zn`eE#_E|yr>7XDeEdrih92g!D4wlavetq~a^4V0f*WV7i1f~8DFPnO(f85^$w`8C2 z;?w7Dpbh%PwaetQ#T&4z3EV!6N5Jn6;ZH))-ltL*K7uf8?>om`LP8iNuqo4KAAl>J zik&0uh{F$CiL&w@j?qzu5+*3+QtV3#yGW1)ji1e*Lg|IhFZ)2qF!fIFTTUB z^M;4M@*KK`4O{kcR(?179ogQi&f(Hm*2C$VJ)Uc?>;5;&pC0~v_r3nfefM>rJ)dLG z?R}j~WuRF5UdR8d=Nx{0_cMLrT+rmmG5IW1Du7l&T1XiGbV#alXikixl&EhMelbc= zF7fxVe*~yhM(LKqcMEqEeqMN}@Mz(&!fy&s7M>|QUwEPLa^bbY?+b4g-Ya}q_*3Dt zf>q>2xu_OBMPISHm?<_Cn~E*P_F}HsQ=C|wQk-6#Rh(U%S6o;;u(-5%aB*evnL%k# z84L~94mJ;tADlc)<8w=4N8yITU4;j~bhlS=w>Jy#6h6S+J}P{UyGgj4j=LGfx;^gJ zUFnZuo!YkD)b>5C(*+ggb@1zx26R zxL3GO_?7SoKFLI8X*Bp5k+lFBEQb5~yD&e9uW32SQ<|lQ1HL!tGAN*boZe zcM?X4Q22q9FkXbh51oXOBNXm%62_2FxXVcxO+w*rCt+L(g?pTY5hfJwbrQy!Q23FP zFzSTDeNMvo6AC|e5=Np>xZg<_lS1JqPQvIE3O{uc#;H*FnUgSLg#z?7N*KFB;Xx;1 z6bprioP_Z#6drXFMz&CR%t;vILg6<~!e|!?PdbU@Md2AIfddGI=bZ#zAQYerQUX^H z3NJee{6Q$Z>LhRqq41iMz%zux@0|qhAr#(p68MNvc*{xPC_>>KCxN#Jg?F62JIN#K1#;WH>0g<`}>;MYPi>LhS(p=de@ zJX|QooCIzz6frg^fv*e23MYZX3&lz&f!7PgDkp*K3q_0#O2P{Kg4QWP5(q`mIwi;i zp$J;11nD3Y(@uh%5Q?C6N{|>r5%f(7vO_3>rYS*+2u08?CCC$@2pXjX$s!a%my{r5 zgd*sV5~Ph#1pQHh+!2bP4@!_gLUDqVAd7?|=z|iZl2DxNB*-VBIK@ejR6-GbPYE(h zD5Ae9L3#Oo3xh!?K^_Z(AtymH3xl;zf{Ydh(QlL> zt%boxCqZrtgXlv_kl?~#vy&jpg~8EIf>aj<$2bY{T^JneBuIK;aGaAM^M%3jPJ;9o z1}8ZQdVny9zN2KzKD0bEscGy^p5QZhK|BQp-~#CudAa<$vQBwM`Ma7>r>VDT2Wt20 zqx9SKr(CbQH@jc;oaCM53;165_xZmca0POK?*=`=t3umCL&o~BE4(!#M4pT8Z^q0I zV+-P$_zM+dD#uj5UbU@yT=heV3u>gA7iyboA5U&iK2vv6iq~uP*QBq@%*|Yv-M^u! zvAyx7#y>Tk*F1OBy)BQop4B$0?YZ_NI(Wy9j<<80I}@GHbTxI|&>iaD-aXi}py!ib z-h0{TZKK~FbJN(w*tugb8GHY@@VHsyUK)Q=pR2E{?}`2i{e=nJCRR+`F=^Z6`Y8=l zE}A-OYGGPt+IiEr%{X9Y?acdT`DPuwU&VfR&fb4c``q@qXUz-GyKVnV=11orJpbMW zix*t8U~u88g`cCvWSG@&i9Z)(kf{ye_fv@fFr}H*`FJ%;X1iK>Gm9kUh#B^Y*;F>E zrs`V7@yx7K!qHq$SH@IS#ScX`!8((*RUz>r=89z# zXIQiN4J=ZVs`B@>STjfUhjX=tcDyGPiibkyX^JdsoJ)Qm+h|60S$8ScO|rit^7EQT zUc)@`Y{l$@ytS2~m1lKrs!p3aV*>-ssI2wf+8?Y4;(Jm>)Ck4ZfLDuEcvJOZcKo~U znBl3;yn|OIEoOK?62t-MCnjWYD`d`8;wP-LyOm{qOieH|+sE?wiR4qQjAbJz#37W? zG5mC=d@OBtJ15~_-nlT+*~biQrXn~km+xb(OjS@rwp;B)Nmx}9tS8^Yo{sag*B^Ga zD4u=TLww`>1xN6TSLbhJ->%?CESSHMqv&HCT7NeG_^`8i`@Tx)(vMNe=$uQ@!+JwY zxel(0u1H99v8!+?okW#5RlbUPtk391bOW6KkaGsA`RhJQt+~zH(<*GwlGHOu%YyNA}#h4N{S+tL3vUu-2v)`T_5st8KHRxfg^(0w6Ju34ARd%b9 zGpvsk<_jv!FZ*N`SAsrAjVD^XT>M--0D4viI!Q0dZ|aPs(UZw0iQ>#;P8IXXx=dDV z1<{$fpF@b*uAW>ptkh-FyGr^%f>iQ>E0^HS-_lccrBB>GpPPGv@` zZ8H0uq?SsD zfCGyfhlDF0QDr?@pzxdutbOSX>*9E0SUuGE?25ifIQ;s|dMOCL0Dy<80tn zmXt?sGP*|%2f5nbHBz-~{o|2IC+79ATh_;&y@*lm-6>Z7eWs$ix+0M9xC~u!iCVxH ziNzwm0Jx;08!k@*q>HgRyiF{M%Rs*g*iSvM5ulklV=e8B%}6>(4FjT+O?yiP<>h+1 zzr1abJaoz<;6{1y{c2mV&~V0<_%spF&_c>D_nSU; zol~;G@5W`_tR?w$vZagPfl^MV4E?22<^Mrdm8Ms!75q_;&*v#UMFs3N%B}Riw}(AG z#(bW9zOJRE4k|RXz2V!%e~1%d|JI@nTEUxAG@z0r@14Y$!oVYDloI`5?^)H%LLQoV zm6`dhS{LWjy2Vak)E6Hr)Hbm=?C5@@ zBjG6{)ac_uj}8|oKeQapgP9`;IFCeB7B`EwPlIXThg_q!(MXYf)qKa;V)Q_F7D)nO%U$nr4z zOobF7KCcW94<9DE#A6{7v*7ddU?Xh;J@rIQBLS|R6~P%rd&J2J<|s}545BSvM-OR4 zl{Q9?Tf?F~Bl1njbbWf1}wPWBtJ$;9l1@#S{J99zLIU^|{9JV8qQWf7GK)=I%&Poa7$g zHRQC)wMk#276xbiot@A12R6DrqThYQ5|@_+T;eE*f9q3S(+n5Wy1T3)S4dY~6GWf? z=V~~>l%Hp3Xf>XQpO=0f)4iA5Ea9@M=F!Y_yXAn}`khygG1C>4-7f3B(JfUM$VNz! z*@S9OT4jN-`Ve%5u=*b39`28Lm`6h&dhPK$AU*_r&I?aYi!Ci+F=K$5!Ms|gl5E7L z<~&IX5pMtmjVPw82O6qP(su4hR-5^iC9Awgxj3b!iK#b)NY4#BUgWU)bqnhDCp{&AfJ8h*J9}3V1{4!8H^b| z-H)FDi!gkTQM8%Ku8CbK9%3xSW2456&7I{0>-KDuPVx!;+ikK3>+W_qip})D-rJWx zAyOFmeyw-Cjyj$7QNDq)X3EO(o!PwA*0FnXCMX+Rgcut%Zql_Z#S2Z^R-moF8o97F zsE}S=QWWb61$wT^dcrJXJqa}n#*eMDi?jJ+sxNKpYQ$NfM^l}o!~Z^6T1u=NC)0}4A6YClW>Qu~=7 z<^W6px}#0g#OEhK4t0{>kEv#QmxlL6fn<+z83M>|3TS?1LtsRiW3Qz?VF&R2#|>S8 zOguUe4^$8BtZ!%4;$q|;>#^ETKhc7weh9REX#c(Evsir$Up7IydWePk=JQez8B2PC zL60?5%{tQjdCIM!Sdm@xscr@}_Ci_XH)64L4DK@z#^ty14jZ7!df@$m!^RPdvX8{rb`*3tr(wqu95%C(SPrqCl_UIz z3uHG8wNoyRiCH^iF}66?5w|X8XO*_mKJ&R)no3>+r(|dG3g&fNukAjq55>-zyRXC} zf%v_)N%9Qk)~l31vMXcvJEMyD9_a!Dq6MCxCa*FKM^bKk>hrL!iI_<{RUHKv1Y(aD z*ztMCib{?sXet**^wtMiq+aBBLnu~a^7HSHD6bd1-7AM6MEgHy$+%UbhhZX~6l_@> z7|}_k_kI!#y8WWp74YBoyf~tn^2-NwO%J)wzs0&K?g$8G*hAXZx2_ONc>W4_TZk$^ z4`x0U%%@B^A#6=CsCK%Uqb8vDZKKBwcJ3Ys-H!F>yii0ahDh zjaErDd`x?sjCEwJMeO!5bJ)=R$l7?lFHD3I_Fae#{7^rE*9@O3eu(#k|5MI% zLuIkQW>irjZtz~{C-hLRpW#c&nxDh#ry~H(7fBt~=kC<@fK@w2Uv=;&Q<*PbGBGo$y}$v?M)!_0`WvWAiHKT-M!T zx(=HfDMQFsNKA8kq7O&h8k|p_an~Jm&!1%ZPxsvWCz**ZFEl%s=+?N%r01`@_IXKq z{@QuxbbB;K8o5frRkmT`(aY}*;hg2;D0b!WSPW8?<+HXT%AV7}CP%6Mux7oG`jHm) zGwn51W8KiJRO<^K!p(2uH#773;Ex1s)2X&FcP2A4UK`M4zLJx-mo@wJ|w+cP7 zADTI-&@)$iy^{Tlb!O%K?$7s+h`muaZq!jYbZ<^(?Q;qk~V-Gy=SdP3U>ryc?6Lpi9!q%QV=^l^QlZrhQtMRy5ELp6NX?~x} zd-nD7BT-E?<*UCfo04os4!G%5FPMOCri(Q(9fQkr{tZiDam!Kp+AHt^N;I+g86@r2!wyf z6Y}Np&c2491H1wJVfZ5P4e$oCdlK-Kj)L!E8b#9J@`CaJd1nibvP!{7bQsPMhd&y; z0DRWAUFn29}W*mG=^sW4`p<_(3szEH?_xhojb#dVWK8J;ayAmCzS z#>)ejiSrw(t(^gDqsp=g{$8xjESb59jD44_=$OPx_XJKk?3COg$!YjnugTYf!_n@r zpvEdUdzcUld37%t1@vPvgL8vzkhLi_?c>{%EXa9~nGLK?PzP4d$l4pB1vV4D_hfG(Oe&E1NXxK5%(|}Y~fsX)$VCT!nlN4 z1%=ISY48qsf1xcSZa~$6JHr##Z&z1?a`F}&U(?89Lw8XgV^g}s!;qEc*dqNkJ7=eA zhM%dgsSqRT*^bsWlf<@l4cL~Ot=YZJ^>iansHqA^ybzya!0m55RN(C2EuM88f<^v@Nw--RL8YjAd3xsJLM$$Y#;D|_yPLH z4`f9fEV+4zFe5f;P%MQ)GKj2@@hV~$j^8|~6KG8}ZTs9n$2guvjOL5S*}u4+HKYCM zLdSV$bH$OmDX`ON>spsSL1uqb?Gv@l&CB`p?kBq8&$53wzkmvHgibhQq|iCV+W6Ji ziHp@=?y8g&Mz*`&-?;rgw>TF!e6zNh!C&@fZIks<&os`b^}OladcS{m(8@U1&?BP; z>qY09H=P=+7e}sbs(p)8)VI2K)eNtkJx&}sGtw9P0Zp(P5%Uu%Doj0M)ALuf_`f>7 zr=_x&_hpY5hkZ`g(RS?twdZzupk4YnwL3C2&~h})X#Z z?GFxQ>(#I|E2LXaKKT|=q`dUL6W6t4$QHoUiuKxy@t9VHpoZ%qsNwOUt~fh>Jw=9H z$8P77Pq{^sZaKxt*Pp}rIqU7b^k7BBJwpm-0e6_oZ(SXaANIo@JJdVw*LZd(uD5}I zU_W~~9#Zxu?BRV77wOQoON%LI6UrgO4bHFi5S8+sl<(^24bWVIguP1d5o98X7>=g# z-Z0|P5V;PDG~w$6N6}39;;162IJ!r}7=cKnHUj5X`FE1SlCQkNl5jcw;T4(d-iWth zqb&_59a`t^@juF-+gn4AK57lg?4(V$X1Mjd&&r|SPS&ws2fimgWZ|rb9s>GY_XPBc zDw`X}qFS>ky|Z)Y+p_%j&Trn2#xhiS+;jgO8s4KNK?X2&9jU)J4KKQGlFmJ0ZQO-( z9(AH>k3Nd1T4RGO1`V~jwN!QyDPYiA+rz+$q}xkC?VY>2^2`-G|Z)`36Ty6GZB)zVc@OaIBH@#@Mj` z=F0kabtX%U1L47#&Y5KWZC{p)N9UtFL+sBvO6?HfSShQn-w>n?AlIDY$ zFrUhf8>gS~+89AjEP}L%tnI0D6vBwShx!Q3Zx;d}`4!ua6m$ki(yr~x+x9tSRDpfY zKDGyHN9`RFyUI zR`<+pf7L_4o-8Ijs189&!;zpYwx0w}aw-Ak6THE8nUj#E<0i@5j6!}brohK#3tao0 z-&tqOV6()zznRNE8OukfrS$mg##@|i_ico9nqa?kT{fNR$^%t_u^A+i zw1Z|zN3THKsEq9(CTx#w^4~!{b|4k1c%XGuY`kfG9*wX!gKQ?u7IGl-DP7i&-Ktfa zVi=B@)fqel^!y`RG3xtT+bD0VNTs^!>MGbg>%2(RijFba2P$0B*26Y)8jsMJYDF?D z(^d+G=4P-u<;q>xqMmkE*F`l@e1l^8r;6{xcJRP9&ceq!mQbbWsJ)(<-ofEOoU%ZrP6Syb+?fh(s}DJ9A{+!SOQ#|Tx$ruMsvJk^%v3kumQQWo$ zFsogdF7}*aefaEi)`xPFJoLaXhU8Lnx}#%OM@QO-14|e;_#(v}ozzyt7;m}f8#{h2 zDNTy>!)*s&*U0@94Iba(fF!Bz7@I7fv3AXNQQW>}?HOV--?(tWCeAl4Sh$f}q35CR zDc1YXI|7V7%yPKFtQ>2OXXE&~xTu*g$AXN>>%YaN$FE4$X^EOM?v(iEgB6!IZ2T-- z1IMTvkYTy3mu-Nwt^-D{k1)@MO%>#&PTJVGY%pu%1UkFbWHw@Zrvc0|yQ%po$VzBH zGR(mASh72rcTU5_F>w<}BYtVTR3Go4tr(lyH8aL{#vaDTC9%?A%n3ZWdCctuAWE;X z@z%qh7~dLeFD>TBAxvt`h{d*Ug>Sl+-5;~sVW4hB0sAdzW$2@L{7s^q-5Fchap}Xc z*u$^CPP9+&ce(hQcsk}FL?PWK5H8yw&;Z5aGpY=^mRHj$IV*QkbOm%zBf&<2U}V^D zXkf!B+68?xqjosnmTqI%ogNRn!^^zZe7D=$9~&{o?u@cKm^D9^W~P@t1!NV1D;}$> zp58ZY&G*>*><--kqQ$!XB6zbT9Xa;+)=w1W6TMPTO_y}Z@WqzHS8n5ESP-|`Rz-DURq68qQ5(H3x84{w(waRcUEq!DwQ z32gZaV5yHM>@X4hh_H4-k5E~|5Zz#l_7Jd@R9F%;dTghNtJ#WS(iAw&Y@Im>ogSdf9!c?xsa{8`t39UA$amP zXM+%7!(83+!Bvy{TrMT#%8xtyw+;4Fl-zw&*qj-*Po#e;pkck_-RH}8-5GV~7~N-D zD=W?ULh)33EEdKC?s|k>CljsOHj)?@k4L;{w2jTRFJ5vK3G^U?IBLSc6z2YOq#eG&(WEt;`b} zQ-gSox7CPv!KXfhWDXd?w9X(75hMKj@>QC?Neg?pw#lWha+FZKKlwUOhnyOTr`94K zvJqH$_#V>WJH&ASx=nTiY6{0@XE$w+Y^ljSpf^#Bj*&LWry`xyCioD=W}(J*Ch>tt zSI`{5K;TmY)K&_uRrY^pGdS0j#WK{0Mt`K!-Wg$U$FNcHxb;FD$dKjMlK75z?s4`j z`{yur38F_#^0-s}a6>5E*qjOO_)hDn#;DO`1~Q(2revzQwTZsf8AS_tG6A#6h&GOD zz5kA2rnxa3Y6$yNZVyGN*ySg|BZKebBL?`%@ilRLckE!VJsyug{?=Pgo?$R`jK;cI zvD)Tvv@u(!Z46LTorX|hMUeN?2#Vh_YiR-UOET$hJBDhHLQdY z)Xt`MT4L7}mO%s})YTr0SEnlUh3o3LT+nTirWdqWUpJFdp=5QqtJerXTjL&=BDn%a zZ&$cF8A?g4yDtX6DEAuLnKG|iw@|N0RmX$vT_L~61^AQu$d&Q_X9UcIDy1OjBZ2&EXhwXc|Ik;nnmi^kY zZ8?;EY?H)9q9Hc)HZl{=ez6;w)jLg6Lm|n@?^&RR0$g6G7=BT)ZsncS@TI5i5Obj+ zQF=GlklFL}g#ygRfHs0h4YO3z!3lG=0$IzVol-;pp;sH;HJH-{SMIGFJ545_~ zG~>j8c(yf-U@90^@Su1zhHYlk1p&p!mqGbSqLDNOKcK>ZWk7Es)@XAAGEosobfB`$wa^xuV$?^o<=RGDOycODBH{G z>-@o*WI_wNB+V4%XtE~gud8Rh*-%F!+!*p#`2y)sgQupIRmXjSM6$}3RcDH~fma`j z*;1JQB$K#FWl}W0Bi&t2GmhxaVbup|g%hgF?1K7nJZ4mYH*=uR&<$wr@CZ*P7JzCx z;U#nM0x9wgm7xQewm~{a4MVr`+tk466C0v#l`EQ7m#Lpr;eool<8jID?U~bzQKZML zq8%TJyL`Ia>u*^&Cg7Fcz5V;|O8)8@=?0(MrRl1uYhJT)(p3=s(57D4sv6o>l{_R{ z>rPY=tv{w?GUMc*;4b|Iw_2MXHGgI*amC{jZdO^7Ua%vQYI3obVVBPtSYgXwDA#SX_HXInM6D35*aIXm9_sQ9s% zfKHx(RnrIBH>1>KG#9{reIQ6p7;V(dq<`Rh8lTDgs45@v3*?qqBCEd-w?F|IH z*81>EIxGX-^D=(4mvu9wE&7fW^o!Th;i1)4-x1xh2B!jj(gVII@Otq8OY=#du8G&^!&OPt+Sn?az)`Yhara30#x?N$a& zfZ`NRT?iA|Bc4ulXwBSpAPGd4vr|?|L3@x;Uos83d+BD*H!odA`La!yh~g!iHeD== z7jODiCYPJt*_jDOqjk|}kZr^MoLnv&G|i-G23}64Wb3a_JZb$ENO|9r2j4OYaWq38 zd-=lqK9c2+?z`_Jsis;XfXjw8rNgplzoIBvg*U{;U;wSuG8zGm14{a`HoUWzW zoYVzQXP~#yEoGzOE35-jz{ssu&2YBu2ZP%U>H=7@tuS;u@~cenBI&$&mJ~bFU=yEo zP2uoOKtqMqS<}?i*VtIYI~?)p6xKF2_M`Aw93R^0^5KWX8Ck62V&oXsrOj*}m05RD zK0U%#1qa7kpIK+_9K$AbkX^g0zIH~H_1jv)K1#y*P7Y^r%Z-c&P4wR#+5 z7@?M_Zgo(GLHY3~pL}BbIesJlQFLRjH^olo0$sR@qCnZCLXIlaAha_C6WA&Ftl-3f zB4z+r4(-@*)Ga?5=4L)Ess@xWW*TZ|Kbpt63Ib15(O$_&QgqmeqXw8_T=iHl%8CZ` z?G5~=B2ZNUFR1t?6>c4CnO4DnCdxNNJ~N)O9nWvA^s$-N>(Q0g#n&r$^9s$H%zqXB zG#asf`vQAH#-twt-u-}|w=!y#Si_OC_(i07mX4&C7a8n>NEP5$)~6B6RDC{KgE7Lc zpty^*UJ1C_doCAnn=b2PO@zwy#@lu6b?;2D26YRVJca$ShQ(Z-wDq(lE22wt2jHa} z4FBbwx^^agHUeuTo`jgk@O9#Y;tcRs%%)A#@FzE|LxPdcRxu0Pg5*D>4!%$}>RNes zN=a}t0&5sFn6f3DEzq!;A*Wu%h?+8hws0ZY%)`&*w3sJJMiz5--n3on1PpK>1i<=d zvOH>x@>lhY$y8P5t0ysktfsCtl<~zG>m6HPU0dTb4Sgc>)ifLI*XMh}%l#M0@ao|3&F0(FA9pZJJ?Fz7Ck7b7IESGzEoDNkoT{|kz1HQ@? zT@i0pM|Bpi`Qc{Bj7!Z3%P7 z#=xQ@)P9yH6+s2@LdfqzBV|cJAhwMdLnhxNHC5Tgt;fE7ln7$tPyOX7#)DH`vVy@E zoEt*KK`&j-r*z~dGd4NbF@^t(HS{##%h$jq0E_N%L;`XuNia2*C0%ki5jmaJDRB3xc6@9;{2z zf%%Rn*_LfjcNetHIvTu{u%VONRkAP> zUcDIApt3c$jduGwwB~5q8?97>EK?r?Pl&j@^&!#Y@xxQaj0h0nwx8uV#Iz0t;v)jg zI(Y%heinuo9FfDjHz8frvQ>^ou^oX%w6h$;P3;xK#bC5~CaG_AjzjT_0(}qt4lHp5 zm;f;99+6Qo;6!=@)(7Q{-yU^_gfOpk{90YnL;A17;b&PG;oA?N#i-W2VNYiLR1zb7 z%aX26|IhwQlK$&wM#$yE+$YOgdnV^+u3bys^tJOhaK2&wd~(^(x53kdXkI#51-eRc za;WFMi+k|}2+WBTGxq1+2RFN^e%HG1bxNU-Di8gE%OCytzQ4j<;|ZwoN&-cj5v`F+ zzq|g1m*H2zKFf*Q*EzRcw_R+<9e3TGWiKGR#LljsZTrAx!{i`T6vbl)#0SKU(4GyT zqFSK;SJ=7|kkPF+y(t*FX$i7DpTb}UbO7`k5a7vHxXRTPhkZ}>#S|?ZUv+DDYDr5* zYM#nou-;9&x+()#{3X|L(O)YUgm|!}MrG6cZo8E?S$A3=$mX1}2VOaG{|V!Lt*OnY z9sHAjykT9pehEwP6F>4!8TziY@Dg?(+h5gcbnBg;STTrE$G?6zbdw3N*4uzfJeV{) zuw|MYiILDsR`*y^*>;)iprFnihy(*8O>TBbSDX)By^nQ*RB$0UdIS?jRA}be@*aj@ z8VpcTq1&JsA!jIl(CWntbdTY+EX^yr^zc!q0a)d64LzoNSX1q3M}>8l=+!LC7xK77 zDfE`J567h8G3bm^j?OncA;cTYHSi~g7E-8L`~l_c4J6SQVy>VMR$Ei8RkDjk0&sC( z(8WSNDk{C`>;aF+L^{JN)etDJyW*>2U=|xD9X4~%+SQtAz)3= zpDDubvFmuQQ~@Lw$1-|Wf+jU!l?reCHihTf{EW2~-% z7j1Pf-rmnxf4f-IHgPS3%k+6#b}J#7~w|jKvM+4Q5@gVplvGx^BjpfZ8wuG4bV;!XxsXQQ=7{6 zGqTk~f2qzItV5%y`Ac}qDV<`VbBd^Sq^zf`3Oj865#DjEF*=JiCPs&?M^(3Vpjdc`4a)l9f3ka=6h{ny+V5bPf8~P@6}?GV5`?2E z8cnKD4afvrw)C;-geK-YXJJ73pbFqB5?e$XV!j7M3T&@49PovKV+`R!w>}rnB92JXa0Qj}uGuj|! z!9R1YnN_1)Dn{Lb3&bBITK@K*G1FGm|Lhy^`_eE*ZuQg!0{cU4WKvzSW;u42ski%J8B-s3LL0Vc ziDFpTaE_4!W*u4+!x4=PCJWfOeE=f}!v;guZd1FLXo|feOakMv$M;+B6Bl?Np1G$- zijLIPpTeXIDnC`2_gjxypDJ{w{Lzno^pPZe^rJJL?-Bz+RT^EqnnbCGywYwnSOvJuqe-a%(3jL7HLg{L(%&n zYxDz190U*g9-(!x4TsQ)XMmI7+i06#jwGg0)9rM!%{-~|>`sPH9z)AM$?jzK>~YtM z;(M(~d%`Hja-ReXq-pG2{QrF>@qV1lxi+TbBw~2v`5lkDPok4Tk?u#)McN z5a8sH4s9&enYDcph?>;^bk@eYVNB+0nA)BHA0IPOc1FWL9Tekt4~T{*bmo>hdqmS8 zajW_hzUa+2hp)f>yRn;Z{y#BRrh?Gp|8yv@dv}e4(wY+#_wNxQTqQ}X5RrMst!!a( z&+2;)NiLf||M}HFIfP`0Xw!fIzUjd_HCT_$SqF)1Lb%{XOiU(cMn7xsfIilRp`x~T zI6@hW4`LbOwwHInOAilGPabEHz^;R)1_9GLaviC~bRDrintB5FJ}RQ?pzcxR71zde?$}&-P9#s6>dAV z|0c1%zW>JlQ`^FojEg8&xn%fLTYwy^ZfQ9_y?m4wRCHaG#-7@7$~fMQGpq-7Oq^zL zTC4T_HABDI7>0kK5qVE%k?v0x(n4Bx&p^9jA^PT8W1`rNe$GO@l}tb+G+V zxX~8G$DM}~d6m(AYX6OB-`)BcX8NX6p>+GuC8Y?s>8 zHNMU$^@-RI0a>~II zagI~&EKtFcHgeW3Pp4?xDVNE|2xTu)uDfhD+waV(_~Z0$CHM*gm#(Bu%EV=MfyX{L zbpEb#_Dkz|I)}9)x3d4TTwHcyAQoU->6)z!nkOZUl?Ji?AEQPqucl=;RC!ya z@_pc5RSM-e<&JB+yxsW~z}m6QNba#H4%WqkKny}hF=05tR|zu;gBdr&L@q3<odG@b*`~OtmTD!Y^H+AgZVtdmB z`=_Mv-|l+$A0w1erkP#TvWrUo6?G6^s5E@CB*KQR#Ci=2$&v-ol8_hRUI3{P4~qny z=sf}-85#=bdV4gK2tkPcjqo4Pr9ncWQU)u=RrPxg!P)F=< zSYSx{juwpS-vn#eDxvm^WDe3SY_$I_W2b1em3HTkcUt@Hn1Jwg?r7)*=r_Fc?J%h3f{R{oGos~ zdc=$EbuOVYKn=+T$r0p4NWrpDRu(0;4o~`s8N_)Ur`mBA?3bkrK{IeJ@LSOP;ZcBR z1X@4*M-B)Es!Aj*cE+1px*Gbc8tX^(L)Y-P&hKezC)+7ov-&`$^llo{(3I(#I|{ZF z7TxGs-q(^N)2h8=?8-?V>#F*WxgE0Dy}F{7W#aqiTAC|Hr$(h~4QMwFtyKx13sZ@w zbKRYr-Af*m$*U&7snbrZvYl%c$Bu6NR?;h4XG`wD#N`uQe&%=2I(nYrm8Q}uQ<(0~ zFQ1WYy^edLi<`oSSE;OYOns}aF?O7yyW^2mAX(Y#%t1X}+##-llsDjQ%_BB*I1mbi zBqy4M2;Yr0Ae(>0Cd$$l_R+3T)Av9tj5yK#5EW=B2x<(1e2Nxz5c>`xDz(uw`}(wdkL2w+p*&@iC=s2 zf@V0Zu{LECi&|oB%D;J<61mNj#;)vW4Ew7q?Ipl6nV{Lwa$wK6^)0m~Rz0I>1Z)`) zW-L)ru{2k)%(`=BoPCcqO=!ZG<>-!4-$RL)FWvc;B)zrs+}~!}llPx+*{?AygF-VvXHwf5U(-_MXHJL%J~7%L&!K3jGLJGEs@iR&EFP}9~6%-i}M@L ziHjGU8{Y=Q=tw#6lv;xfRUD;d5^m&1UsV-eBD$7Df6h-W{hlJ`{b|> z7M0u017TOH=V$G;nrSfI2RNeY3FfA^%&9eEs>ripI3PmxH#EmY&Z9tpawAx^XpD6i z1I380P<+PP+??vLSLOCRKcy;CzkKtWrg)uh1Uc5NprQDav5TsLAz+a*5zfU}^NsZ( z&ch7|B*cxHIjwA3F6hy?B>OamC~UI*r}>l%@Mw<<2y|~`Qae`Xa(j5t(Cgw&Yc?;h zk5poD6eBEg)f=v!om)F29&(91lC^L4IB=U@zXmtd3`vp1aKIm`sx(4w%+pseGx2_} zqzC+?x)POG!%?;-R+D`ox+5l54duaL6}w;dCPP&T%d+b5&|Jcz52ILREEsZ0_6m=_ zx=|)Qj=J9+W+aKxLw9lpmDpwPw&` zz(Wu+Dyu^NKv=|6vilXT`MnX7J!91&$}So~H^8KosT!l+hlM6om*h5rm@_yEYfnf# zgvYe+3R=dT-Qn$G73QV55noFJwLwVxL+x2(r$URp9A35`+S~=6HBN3?FqKRnhaVSu z%9qMSA{VDLM}B|%L}K3w%vL~vCBb1Hf)fFi50D*@VdSGE%`}^`85PuJ@F)a|zj!81 za`|$ZO^xM+rucmwm9?oORw3Jvt4~j@HfKz$GE;q2x?%P1(wS4MqV;u|+L3abxA;uE zxU!nO`nvU_db4`!46|xleSOCfsoKib8<2H;o7Fbd;tL+q+uGLK+t%948tBZKNa^|< zo#dQZzt7TFM)1+>#oRZNiNtEkI_m0U6;o#-rC-b)ZkN{9By!1AymHD+q>S}wC^>RK z^&6VcDX#Dn3QJ~xPEW0f%|xoN>qsP$-&jpqjw!i`6W30h*g17M=gZHbY^t?BGo>;< z6DgIh@CJe&Q`?^4@usruk=aV1d63NQ5;(7HI=Gh zP3`>%thBdLPLsS)SZN(NT)Ezj5Vq@Nrb>D^B)##MnCIQj%4k#M=cX%KdS|(BIh%Ci zi6>*_zv?(UN3w1-tUk<;uVWVn+48Uyw@#{xS;HJl#<4&EltZa9E=RV3r zPiagb#66J!kMdVtAyso(S29Di{?bzUt|3MWG3&3D?Ug6y%03;AaT>vDu$wR<5l|$- z4A%@?mSE0C58%Qgh4JxgM$9NVsxg>S7=3l*Zv$_K6B~X{LIreUoaXBgP=V>LMr@Z5 zbSbmb_F{M9ST|Hv_~dX=io_ECC6j)vRs<@2Yk+Q2m4hGG{qDenfrNYMDF7`U6pJ0i zTI)|)>P{rTTokNm4b0itHG|Dbr{}O~JsaoLA6CD2L7+9xn^ncS-Rn1wQKJDSb})o2 z{S1p2Ev1?ds+d%9&?%`lw$$a`yQqaNb$gnP;2bs`HD}gjXK(Drt&*uP6a?_!0ql77 zF^DM~W83Yc=|4e$vk=p<`A-^ejt%U#0^mj1Y_y(QHv$yMCuMLwVsZ9?@+xmOD#$KK z?PCe!?u@|i=4)opUd#E~*|XPJkMr4MM$chv&ge0-f5cA_f4E%@t8$PlqI%55qThPk zdM_X@>cD&!C7_I7$9i9}{-Dq#@{y&C$6vZ@DPx*E4o8c#lra|j;gLCO8CyHYx!IUG zoX^3%kXzUK{hMx;n98B2dKe#A?q`Ai)d|TbtBgmt@jGz!E4Y(P>lp_`dp%>xUMiW^ zGoE--kBzPzf7VP)WiPkm&%_~dwB3$lZH%7-vape@i1zPNHVrB#4Om2K}DJ5pOE~{tc@LY+wA1r~WRAe}C#p z%Ab6L#U+LXQ6oOh>lHtJ>d8+rbsEN&FM?T{_#ansemj8MSlJAK6RxO)7!)uOn1-1ya{>-m=D9gVmks(P!u?SAhD^G^AT~5=1(i7dukW z_h4Y|B12lUxx<;o)&XLqQ3?`73j&q4_xjl=bI5lDLQGI&fv08lyn{r9gxpEf!xF&R zz`4R!9mn~FM;&>d*pTK5mcvkZbG!J1)4uh6Ae7F1(fUucZsm(huA59VKJ;-H&V6VY zYYJoi0XdbFvG|vq&dSz*vfCta$+?Qm-Dc9wSoqs6pWnUGf9AR)FW~ILBS)`5-QMPW}kuQ#nD!)V4>TX zgA6IMAQilwjWa+GbYKM$%J_}9sOBRRo8gPxj3{XvS}6$_K!+2@igbh(v4c>y@CCwp0Md$v zA&chCXuhZ0Mi7Ya4v|0BBa&}+z7Evxcd=?U<& zRMonL-$#3;S!}85H(l1TB33ZKbR_(p>oOf$-!2Z={ntbPy9ba{vMoWRgbgC5Ycp0* zKN(BtB7Dn`HIOh4Lq7SiMA*pLAbq+TovV;ED-*i|muHVeMo0Xi$he~$W;5WyZ~cSB zW;Yx?E)w#mBv&Xr?wi@VdhwAMolW~kIeHux8wdm9n}coM8Q68NBCi~;E9!1Fn>N7-8xmKxw`zgPXmWCu-)B~Yf;H2U=|+>krX|Q5ay2>3NN2pdYuxo%WS>=6L|el_qXBq~ik3)q#;ss&4q%2$@3Hro8M3|+ z#8S6*o8E#pU4{S8ME}zdBZ8b9`J_#F$jC%-1dvU1pt3qJ6_6yFn%Dp+kEU(e%XI;F zktcn~<~9Uh!ohyWRYJr`hA}Q6K10h4Q=1~#lj23ZJM>XDE|+&0hv!hpMqPpmvs=-oS2XJ zvD-S%RUQ6M>um#gTP$4J-M($E2kULh(n)vpXa4v{1kz_)KjthL3x?5p>>Llf5)1Pk zpoarH8myOI(LGYWs-hYGG&8{C`?X#3Njt3L&DOhC@f>d;=zRoGQgqFt@>p}$=xuP; z4)_=Pn?0Ot87h+gg5GWKK>xy5Pe70E9%4`M17qlMVYiMpRTJ?aI{Ge~_o!8Z5EZew zjVRYA;c{>pawa2MfACkXJ66LaOCSgMN5KFeA6l|z#Q{;wrW>F&f~czA9wi(Sm0WJ5 z0@*4(I_bn77v|zYKAtn{WmapAiyeYFi9y{GJYFVA&-!aZbv?e>OV~w=W=Fiivizhq zi5nn43Aptom0>S^b&vN&FwkZXNoQqZF$^>e(2fWLM!w#=Hd{COg21aCjJ!^>Ue`2M zuYJw;{Ewh-cP-ZSb`^l0+PeIJjfd;&O$&f8))BM^`blNd1JAxdTw zlmTE(Y)F|Z13^f-7X;((R~uQc2|y(Dq5fDI=44HQh>*|4e=}O)jVR}04x}b-wSITp zs|Y8ny7gb$vynsu-+?{Xd_sSU&jKSwsHTz1N@*2iaD_`eR{|vTyNEgmG}d{n_3~m- z!@@p?a|YB0)rHrj@dk|T^WteVyG|w>14vVfwnCUvv>u!Ap&)t$D#d_nCKsYTkt4_` zWDkyypgz2l5v*m-CG8Q*l0Eu{Ndx0%xLh;F4NTfVP^=Bp&zUfxZ^Aj#7fB!7rQP|V zWNlCb>fI&~mUejft_& zydYc9mjf_mMN`;JB91)(<|!|5s_>0oYb~uI=w^?L91cNS0;UmbYxn zj$>!y3}--wvmlLZ%Z}qAN3t_m3422*B+Np~=zs$450v(n7P`2l1qy`@I_bF3{FN58TV$w8CQiC$^I9j0TAaoUuq6B(_I;#WBqeXVNQSC0goEuhj zPs@AS6qrQWJd_?Bu0V(&Nx4bQms{9?Ya)$U_YV>yb%d)DRW*2szNWk&E66YCYhf9e z=>fdOClpJZ<5+8dGmG&_>)p6ee5UPY=*1QCBhD8C6!i7c)0Mt}KRbN_F2Z4g|-}nXxc&&w2tm>I( zkhWj-4HiQN`P1NkAGy#xy`0R4n4AG$mljPZ3S%p?0|VS38trC- z$jT)k_WkfQ^6xb=!%}Ibp;4CmdFB-P$CS@PX_sY*efl9sqs*@mlw^`A!b(wU1VXza z*#z;$e5;SG>tX#`1mm~B-!b4ie@u1k0ig9 zxm*rlI&|}`=zJawz)tfZA zX}o8H=4ZJMW8;7}HYko|6IJ&l05@$`KokQnN7v9KL9T(;BVZyJ1+J4u5UCnrVu_Id zXgn=+Sxc+dX_%L?HpnpGFv$4wEg6o>Y}uUQ_{^r|gB&-weAza}4+0z8VtmMGwU`no zb@A0~!=ikED18_Bok2bGBuanqr0(Y|8QgRWaFH^bw`ABZZrieD+c<6eDWqROAk#Mg zrNaw+vu8b5pbauJh&J_jc6Fado143lT!O#i+HfELJ$^HxyCHg&Gz6IIG{FnBX_8}_ zu!6WeO$jKR#u=S(1Q`IzczvzjBv_O-vouwKhlUI-trM(Qn@|vSppTH+jRVBnDj?%Q zg~SpBqdZX z`!YJsJ|k8sqr(uinL`dwr7gP5>_Gf%YG z?V{2Y4m!;eGlaY@a2s31-Qq9pDQ|CL=xF7hVk@l?B9EiF7}$KAb+ZpC0H_X=0l zVRJ9#U3WM`CR@;8H&z(@X2mUWj#xuOXIDfP5YcT?eC2Ye(jE6YYTX{Uu-xI<+1fN7 z@5*(0+ILm?;NY^E6&r8!hqkqO+Nbtz4f>Z?Rd`&CZ)yXeg9ok|gV7Zfo7>BKO8qVF zDkw+giZO6h%+&k zOaPh$2>IZJ1w9(HPryh<8cqSAQ`W4P;95zXqmjVa8xye$D{k*gdiy`yznKXiJy*x? zG=GNi&iTEDxjjadnlc=?1epWEOZo5fSL{W(Mea&eY+O9@8fT{d|B$9eu^ zM~-}q7r%8p_2C57L=$_SIur<;c#SoUT}dS#wJq5AJ8uKS={fNBqj+j!*cenKI^!A1 zunYqY9!%emE=7CD5#YQ+vJb);4X&2fh*v|eB2Q{@bV;F@51u95Wx<-DH{mfQH**#F}a_@M)wYH+uUhR(!l*LTdys(l@?`vxG zm->5~Y+%A>mteKXqU`kc^jB6`6@M_LezaRMDUHjke3jOY%KAk=+U<7N4=f5>6j)Xb z^{cqrrkY9-u2#bP|K0_4Oz}0Udx4tCyl0H0r35KCHt)lDwq{iWOKS zEx8eN5qKe@E#P2)fEMEm+>-*zw7eXbE-zz>cXaWs{HFZ(!c<_b4mii;;{r%@{)@22 zb9Kb8@_&Ff&o+_H39bG?d?M+*f^DjxVDJj{L45w%L-j4oyo4sr??WA!P~J{%QSQG1 z3O#=wHt_EAWhCI`zeHPZN&ZU}NP7it%aV$^x{CQc%3;E>k1}i+cX;RLF=ygbQ(YTr zI{Sw>Oa+Dd;O@rgLV8SZT7iY z&oOq)9jrrW;&+2_sSLu|7al9V=W$@64pI$znpWslOd)N)`_RSQ^zx02Kat&kf@d37 zJaIqczbkwjOYCJ^&$)}|?>cAeWzpmx=B|H@?nU^7FCjOs3Gt#a#Md_hnWr6hg)24^ zZo%AxWv7CsTrq3kl}Kv&4ly|lXIw;1xO{wg3G+qxSHCV8#r&`Aa{0g(6MIrJm79?i zz;01&PFcO<*1wwIf;RF$*`t1RYt`x}jo-Vf`v#U+GjQd2EV$1N$h<$fE15pZ~n|ktTId8GF3xW1rBU`$ftEcd-hBK9*Y5mZ_I#da-%@|5S{`S9^;hhL`?wNjP~VKJ8`-Dh z3~1!}A0WA(b*g`g=T!rHi)f_HC5)>J;Qw5P%-bDW4g&Et&3Fkv4D87Zb(6v*ZA1zq z4*@fjnIEen`4o#KImcN9b(X@g_XG3?CS?5cB$luQh#8Y3$yh?vj`CL+&8r)NNNNU5 zfc4ctAU_&v2(V{qdZ?w`>Q3&F*8)}(;A43Rmhn*oh04soG%A$L-`f7>!Kh0OSvuDYYo?Sb7nC;@n5*#vpme=|$D0yNty;_=QsCO9VM*}p?<}XODW_%)mUqEFV zvoE$=wtHmtcj|if@Jl;uS)}fC`}naNCMIq;_E+qIE;8|$1-c9r;sSU; zBe9}i8T%TxJ9rbqkHEZ0mKP&M3jSO4+Sit8?e0n}?hTfe1^Qkbj4cj_!Fa>9^{lRz z?Vd|~?$WvcE$!Z(M9RaKmb%iGvbQ0iEv>cHEvb6@+ilJE=C&i}fUGF-sgLKAX}J-j zjC_>;HYHscO&!TdN5X?TnhD?E(DmP<+atHXeB$_v>9)50rx5UeODgrtwc(dh>LJe} zkG)GZjj!L!e#xH+j$6W#Em|fa405DpqfS$y_FUh zKYy#m;jr+dl`hXi2EUU`NE+Y!IDaql%S(_=ug~f#aSc% z4NYy5C&)7wcC@z3y_9tZm104MeZMyM`oo;KvDya-17Kt3`|`g9G&CF6G`7k1K$vlz z*wnJFcpjyn{i8WR^F%m6uX5JSBK$G69kLliTncqv*x}4 zlTCgH3;W>;4f_S5F3Ivu*aT45m;1P)EQvdUeHWWxYId0B^pDH+570-m;b5KvWGiYa~jWkHlM)JcMGPjPHF{H^^;sezeWqG3Y4l;TNRv*jZ#FWdTZwz z1{i5ZwN>XwHI7GoZyxB_ewOO ziB%hV@-nxf=hyUhU*&q}BmXtZtmBJ2bcp9l)y=S)m$C=3g*X-usf*$v_7q8G>g~5# zEYD#raT%Wr-ezzEYkdQ2ujRLwVyoV((QQ}n>D>Hv*q}SOZFC3v<_WzVME)yJm%^>F z6Prr9x)O!+)N8Q$If;6M(~7(w8i6S8XVVgo8d1eLrswYIwCu+UF$QY>lL5irpPDDj zIy3>XuSq>}Op9O9f~=uCXyMyFpb0$l_vpyl=>^m`ip2XHN^(8>uHm?A_Dx*P^H)!7 z-Oq9Rw{D%`xS6fjt+LhHu1AQdeElkd{Q1{tIR7*Lntl7O;q`5sc2}OC*@|EI%qpw( zdPJlt$NGV=1`b7Y?haV>hQOOn*W?YnC|)-L#3T|sB8Ac832PcU4-tg4^THJgM_4n5 zXq3cgOYo9UCi_WYYzCoqy=RwOXFfEz?GUV?!WVetL<{O0G%eLPV7cPh7kSg+!r4#$ zO#K7(Ik$nf>hFK?03torYr6*l*hU=eosyeAX5Ph_>3~^dX?Z%pPHZ7Ki!HK@GC;Uv zjEXPC7+{bD^>*a8FPvRD|9{_Q2)tK)94D`1OWTPd1l6Z|Hz2&~-5nZdAQyCYzbGUYvg@o(0-2tifTf_|at zEhn}4GoZexkE#2i#Jt?>&)We!iTdorK=;XCyu&?wWW-;+DC)t@?g-&#D;EFSv4QO|cRd1j-yaQ|U&#UP?pHOZ4PRaeCO$l{}0d2j-Iq?YR%Ji-2G&Sg!XkJDj@#2N!T z1tOJlZ3{P6j~m&e9`+H#2Q+BO5AcGb?lGFw0a@gZQ))T?rk0wH7gYc3)(vp!1 z-WT>HR!n`}>|FBt!?5YMkPSwEZRV-d||z&Ffg&aUE7(EcBs zv#r4m^;?tz>z+Yb_8!!YQnj+B?6;k(SpJ5NRWKoAiDGwCmpaXqX+k;DsClc&j7Rv} z!4vMs4*Y&FDru%9GA03y&x4^1N?^>Ay$L#d@qf*JgpR_+@*Z@N>JJ9Tr zc+miZsJ-jrb?3KvY^D~Q=nnTKR-WGD^Hjp)4HqJ(l(tuPN9!iL05aSr@+~q z(w{2dcmP0dKzcj14o(7(qP-j;7^LRJ`8J6W!>Wi&;_~6J+Py~ zWZNG5*Ir(&g*sroB+7A4m%DQCtmKVGi`6_AMVzF~?cH$Cz>e0s)}5^_$+3TlBgmYfRR|zJY67m-mc0m) z2PsUG^5p3tS$hy`w*;VoK$3!m5ab2$l~(4NePDAJ(0tj!LYYlG>_9-6jZoQ9;NG z#IgGr(A4;+MQ~{Vh2M;VMHWG?5!59zpPZ9r)(n3NiAcd=HYToK3Z=)r{o40bm z&#&Mv&mZc+g0KkoVpT5~^;@h3`5Un+XzRhu6IX7X5fbaSm_@> zN0iVsDPB&FxV=y*5D%t*E}VG(CsgmXx!n~m*O^uH-)e2kMr*Ta1`&N>ENg{ex4@hJpfAuWLiYMCOhFb!8+4UYY>(yO$y?M;YOMO;%&9E<@-k6e|NvN0>M zwn;t@1~YErw!+yTJ(+uwvPJ73j_Hv%ZXka_noQ72$Q@E&3`Ch=bC(OahtaMmsO{!= z!v^dy$SfM@mRzBDYdquvPLeB7?QW>6bUH(|4eqG=al+ebP>fMSX)xe2nOvp8QbP?h z$&(?w$rC6Kx=o5weyw6;@JPq2%k1{DXq}t;jHJHu{g>5OFb+d6Z@X=oWO2!hu3TTN zpZ-;pDQib;htVblDkiD}Ydl7q9plx2XY{-65YG%EXEnN)1)>gjDIN^ZSL$*`1NDfZ z4b`{Qhk!c_oUbEGWkfuiO$00Jc_m%c%&Z$ zzY0@jd5UVn5{6KT_w9ZvclK`g1CaC7?w7Uxrzne4`=5eK(Em;R`*nW}0Kj0G4^dS+ zyb49Ey~5?xZ!dJT-)*!T;7)b>J)oxDpcMPLp%TbR!D@HBE`(mLi@Tj3y_*|lW6eT0 zm-!7fM(Sl|atF%;ZnHtIxK8V4XMHqiw+Ey3Zi!uhUqxEP!c(ZOXkd-oWCQG{Q4#;9 zGo7weIumhoT4y>L9?s>Y-u#=sth`V$*0@UT0k_HI4%jVTW7H@w5qJc#Mzy}=bgYdd zTK`ZD!2rA`4J93j(^^G!VWC|^kAk8k7t@`h+7V2dg(Fxg$U@ZCDHjuQ-@T@IgxBJE zvHofp^%RXt2;wDLz^^1%HFH?N^|=R~009slLZ?Sl6G{1ohVJg|U97tS=}(=Js{B?`G8@fD~#NDC8TP zK$VF~#GJJP&vylEmRrf1?!p8D5p{a@K(x{6sp&nM?XF08a4^!4VWfNiBkz6-dT=|I zd0I9+67xJ?-^qf2j|=QRbSKJAy#D<2>g!+gRa7)pRQT#TJ2!Q9PGYaIqQYC7OrFu% z`CG5HN9WE+CZ`Hl1{wnR!!dRg-&y)+|>CF zqgyssG%&j78A*5+Puah}aCxAytua8~!7znPbiHthKZKD>V^)^H^|iPSh((Y94H%<( zSia`DLRwfWyn?t6bTN=%h+L^Pz5D76mFB7)iHU3ZXjtu47bD#M+KI&WDh(9-czm+w z=>2?SEqjfo2XMu z*=zBM#YdsU>1*JZAj`**3s;5Jyo433^LSj37}NX!@I*4qJS>OwpcSNo>20?cp0Ptf zGUPAEMt%5?>N^If-|))s)Q0kwvNGTU^>AEIqZy?uJZ4uQz%EsLxRnX+4Qzh)_RXpa z&w=5U<1A6umTW6yiyImj_b`*!j}k1XW=1|j($~4a0IO~ontGN{yRjsPG~E%ZH%J&N z+IgiA*-Z#V6pL$!B5`!(oFpO|N4~aX$0?Z(39bADjKv)h2h{#vd>hry5*-L)b0i?2 z#PF_g@xXP@Iuc0Sqp^$P6Z}VnXVl%_6ypipHZFdX-Sth;U=R`D@HKV(DX}aPDHES! zC!P|^t4M8TiToQ=ma~>x@L$7XM(>F1%T*jVytb;x|En#*Fk9Ywd4Kbk>#q*T6Zf!y zd)YE~fN_h`O9$3TR+G4ndx9Y#&9b6FJ-cCrh1xvyGM-@+8yd^mrR9wZ*V)M_jpgdu zWawa*Hmp$Z>s^P)0iN|VHuRueRfE-Hv92BB{GLYj^ZRV=VIIZS)w^eBkC* zesmXc2KFn6R#Bq0xIkWCM}jp(#c#6*zb!@^nATP|QJed9WU>GCtsoe;yzR_c9S06v)w>=B7hbbw!<68Ic|L82HI}sQIyK; z{-VdIFoSIFxn(sCI(Lim|%=gHI1{+CuVv){d0)>R|LoT|~1Jrp+(@)|PDgqAiUYUDw=^!&) zVlZB8L}1k~PFXCQU6Q@B@&!OHi_G)F^9-Q5 zE%~1|ukl^qbo~3b+lBii={~`Jds*`hzBSFePXIl9clo1FDyHvn-{I{~at~LW)fImc zt%u9c>OKQsyP@$!kDb3?D7zUyd6e6_bnQzf(~AR3w|%b3)a8~fwi_M1%M?7)`n8@RZibuflZxnlq6I%zAi2i9oPfsO-pJ@7^EE_oPz zM+sKCu^c1$U4JFa*B`K2V25hp5HE0=G#|@JWOHUZZ~^N0Zbi;1-Q={!pe6)uBJY5qt#}!_C3gL z{rEZNavKtJ^LO&DfEP|V{6fHDWuLToE%<{J0=%jpy|W84&s~U8SPJa|b`5~$!;*v= zz-u5d6!t2C)NlB*ctX|mfzd!Is30!TSwqzT5-uA|QYfkm*Vhsb!v{7N z{+Dai-_GrJ&VF&-X1CeA>YC-wJ5D_PdH2Pe z`cWH-w;OEGxaMDU^hbM6Y^d2$IlZ3qe!+d|ofqEYI`f*UQnr+mC@)lja{&5(=-g+F{s*of)Tw-;NsJ_0NslTxLtY*}n zfE7&-d0^!xo0a=N@@=vQ0bJPHPio$UFF}3yjRbfy$z|uHrZ&1OK)l|5e93`!D#)tz zT-XoptR`5c7VPjd`0FWl6bacVD6P@yrx-sG4X@)`&5j_$BBTu&+JX`_nyi_O@jjR4 z-oQ(1SHN2VaT*guLTwP4?3ZXE)rgoh4(ctsmG1Tfsfib9jwx(Ob0Aj^D89I840!oIBtS7_53#PlKx^Lj0czlx4ZD#2;GAFWWPLl`+ns_pSlL&}hkCoc&S6i}<&vwl zxuK=qE1kESQy<~qQNPNwpE=!v*#S3q{@IF3uAN;~YLG%@Aw-o`FR3>A9mY^+c|~W) zpbp#ek8%UnLd?WB3S+1Nqv$kP8HYlT@vrDk1vj^f28#KJzOht?suO9{FwW- z)ib1D3TVhV0UJ3;&JH3+jQK!ON~;B>c!1^! z&oW=Ut5R+E+@scUgUk4`|Cs8hRUwEj~PBP|6ybe z(Lv;0^UEp?9|6Qi{uTNjo9#scEUkz7T%b7aI6NICP!j&xEC?4T3_i)J@%|GbKib~T zO7wr0e=-@0bB643vNloQ=6--r3$RZDz}>8-Tm*(3y@I6%YpoA17ElMKnuQ%&hxx)n z4csaYW6sKfJ%P=EW0Ay5gOTPFjU~3R&EgOEtyXL-^KIpElvwfw3r)ZcUh_DtP_QYn zmc^OD#>TBvi;0sQe+tzxPpHqM7$-g-2VY``ut4LRw6OrF&}%8S09mL$&fk9lS+fQ+ zi-pBgTN^3Xfq&|_pnw0Suk+Y1@MrM~j0>_y3_>$put!vfL}CG&JwgaY;fke+(-9`C z&i~XN;X1_tq1pBqj1#OcA+suOGpizvg7(y(U7Y{#&w%<2Ehn+suhTx4H8Cefy}G#lD@_{`Ju_setR5?4qw$8&8aJZJlRW zxs4k+D?kXITWE7D!WB!@uH&AyP5fzRG1YhX-Cx?ekK>00wi!2NY;&K>>2!@a*Tv;8LDKo5M&dFSL0auP(F&v>OU- z%F1Xjv?Uy0QfM17A66CGRIMlrZp{RWvZ;r)UT-tx4?I+8a~P*j6xuwFKU-)EX#cp- z7GYohZJ{ldAa|?KHkJ%BSD|exsbS;B71?xZHa!v=Ivg4w&Pf!9vM2tJGLNJ$8R1BbC42g}bO#C25L&f>cF9@&KJ$4cf< z4mE{4y!Y&T_Xlxxwj_t^=&m7{-Hpy>Rh#`K~c}*tbsK`E@?&*R4af4QJ4<)h)!r?@X)YD$P`};vo4t|m$GGS zIa|S2veQ^UTg6thHEb;#VC&d=wt;PAr?WHICbpSvVO!ZC+s3xD9c(At#m;1BvEA$g zY!6GZAvVlLSelKpF}9bDvwduWO|mIA%`$9;?Ppn*W3z0I9bgC9AqKoFb`Cq2oyX2+ zN7zwz0lN_OK`v&OuuIux>~eMmyAowuuV&YLKseEXO&?L)3HqUusA%K$)yzxzp3=}Y>pq!9+pPZlj+&CkRBPI71L9x@kwPg zbs&=+$Fb2&c1qZro}4j_W0GX2QnQ!>!ely^Gfri)={X#n91*C=&x{XC`?JHDk+hsm zk7m=ky?l0bzm!X-vcr3o9DZXMGmg)tXT{vq_+(nn%?%IZMpAArdmw#Sna!rgr^lzq zOb5o(2gmUA>Fi;J+LPnCS@~cpODDyH+068qoEjd^%uUbosgV&rH9JKwH9R3@(^KQq zBY4%x^z=w7YfR0~riS;@dnzO2Iqe&AE}cCvKAaY3vg7#vQ09<~8LBEX0Al+7H>r9}{9|CEWgbE)as@!7*No;-Chby&c$RwXx_nx;3D(KR!v zY+4!}pPWr+LDp$`FDQV%v*A%k=7wiS!Mo-_V0&(EW@d6coh=@)=EgzURJL%PFgZRw z!4J>P%G0R>L`B2+RB8-eeP~wDHdC1cX(cx|geM-Jl}0j?_%5GK9h5SoqaczgH3mY= zP127Ura;PZu)D(+ycxPJJu;LU8B1Hn(%JO%*?Z9|hsLMHedD9)X+vg)*dj&>Mgnz7 ztC=|FNao=5WF|GjPo-vfd|4RFWJZMC#Q2P*$WUh{Q-|BMi-}R85!*dvt zhsE4pP)8(!q)k()X}nM-JCe>?vT0(p=@D&DoWT>N%|mm!93DEC!-e=_CY>`)Wri^L z^wZj42c1EuF?(^$=L!d`g+W%>H4SHJf=s3lq$iyxwF{^D^r1<9W@MBHHia2MGoe+;?FCI1o*nad zgx{N)6-H9CDN!3;2E5!1-n_lbkQ*PH9?IYndv5RC=qSBP;}C{YI%}YjgbSx<9Sg0- z!sCj=naNDnqHT-E3=0%6VJ;Vs@|*h4FyUWu-vY)kHa?x2)W(uBnHj^t8Xq>UNKIm> zq|hzmf$@=aM#Q|I-D{go2T29T-kSBgMmXnC3 Wq_gOFI@Jmyj-<^eJwU6rJp6x-RUf?o literal 0 HcmV?d00001 diff --git a/static/fonts/iconfont.woff b/static/fonts/iconfont.woff new file mode 100755 index 0000000000000000000000000000000000000000..d06756cabafdf52e80d4b9e3119ba1ee45a2ebe7 GIT binary patch literal 30200 zcmY&*Q*b3*6Ky88IkC+X+qN^o#I~JGY$p>tC$??dPA0a^bHD%Ir`uJly4R{+)&119 zw}+CXB-jtI|HADR4B>ydANT+9|JDD0NU3Rvf`R>j{Z9q_2hqCk#w#T?X14z{{{Oi6 zf6&yzkkvP_GjjY-3j_m$oB#tuprKyQ;j*yxG6w^LmIni~R09JmeKe`7`>-@KG6e&( z&;QTI`X4w^iGR&m{wMyYE&azy{sRS^I8?l)ovY`6+T(v*4-5=kTelblv~@7~ug^jG zzd20*14j5sg`JV-e{&rJ|LY_E4==jNSZIpATO z_nrAjG3|1n`_WSauJDJdAzuBw6M=GqdHH-<_25Fma{krZZ20Bfk2Czt;n_%$AwuHL zs47BffAN(;bL3nb!{*@Om272n8`n+mfbDhJ-LVv}g_c9N{SvhV@ zg@3nx`VV;*>_`N$ZCg|yjvvBJa3j6Jfi-7{!+;kp_2%IiO8 zr@Qa5ty#XAdC}6c{w36kG_|*$zd3T+a@S#}dSJKcqM=f-`Rk&k!`P9v;jSg;Nn>F# zdm3r#L?P|cG3}h;fLw4zXOK6C*{x?`eWE2mDN@y}T(&er5g|u2wp72oWl5$qTJc5I z@4yc0oq_)8OhZKhka^)^Cxg^E7p1=*yV6v^cx9=?NO(GS?OF)XKb;`Dl;L_VK~O;|Vb$m~d2>hLv3Cqd`7_m2{yn z!JSlzIj|f7{4wE9J4he9%kHC~0)%2@OvuO|m;wR88B_b1ibw2mVjW{P9hj?9BkqlB3nd8i&oeaXJEa;x=XVf zj3@!>lkafa5!O#N1q|^46)1K%t|;zut@t8e6ntcT4L7v+#TKv8Clz$%t5~i8cac_& zkxB}DioWI>?#3mlA7q+Y=z-cM6X3?xv{(_d3U9(y07|I4P+CC56Y!CQGu=64%~BR~ z-$tzkbvIo#>~$rDOFTU(Tv`npEtEDk;zqt*{L@gD#yFX_GGZFIMAnjiAN@L?VmhP* zlqdJ+EWy8vrag(M1`d(?bC?m|Wzj-K?kadme;Ub3?klNDq6cIwB(0fGC^QVDXelVZ?c?Rl#J^GDeb0mYVS!LKmsb=4D<|Jn+fxV9NLocXJ8kVjl#HsJBu0j z`h3dq;Wbc?nlA%A_tJL4yCItPX}kvbN!^*18F*=Q4%tvzPB>-&%q8*3`WajWaD< zutsahD_0u(2AY$?r1ORmSX$#dCzN}P?E#_5PylXdJOfLzrorWTV=6!(nN${UG=YgV zvvXDX=$I2wl6*9)BR1R6n#Z{?)rE&TBQrkR_FULGsvK#&9B4pMn(;F!+x}eEIjo#x zJOlVmVUdxUqGxul?%Z6iPfI5+AU8j>)P|V~ye=tYYi5zKQ6pAsV&woxaH&bDEoE$E z7RHyi(w-|IDbBAh9bpD5R4cL$E!|*>DJUp8MU+3#hGlHz85f^{z@nf{u6GB?i+sTT zyN6TVFxmLSZ$e$OQ_uRZ3p|vA%-z zIni)bS@42k3Q{;YQY7iX5M@82v^aH+kIY&rI8kS+laX@FV@@=8Ixb8~4zTJX^rGG7 z1>JP$W1hPAqwd(|r$Bv>OB(BO=^krq#Q=0#X`D zZjhi-Z%sMcSU6drkDeGjnz7p#;~(1sh(X7$Kv-gG!Nw;y;h-9;87ZsSR?Vpjdh4i-u!sLV+jF2-zp? zl=ItiZ%R#pwpTpcQ4n7(=GMdaxcT>IPs#WFdv{XO!9=01gUL-A6*=97H}(g*c6icF z@(uYmNp~C3&}hE$&`S+?fX{0LelY1Ur^p>~sz_KAIIz0UPI3KiH>JP93|1};Qf9qH z*nFDxS69+^@-=xAJXC=aF19%W_{B+#8Z9OuX1_~br(jvWMY*_J+LdUBmSkw(u!O+l z$d*({hYea}9N(KqqC*IH)X?0DILQ`aC09yiU8@auMs2*bOp^_}sD*@5o9S;R=Yim# zAbtlHT@#X4v$9f?7XEAF$Mx)^2<{o_er=4TLW9m~N}1rD^vmN}WvS9loKg46${JtX z8s8mG5r?x>&hdK)no$7>x9vG*V%pX#*sRAxDUstye&S2u>i8`$f8K#U3s{w6!i)Q9 zT1l;iL-V(v^P_#*ilojDuaJ`F41N$qF7Z)!{dPE811R6QDr3NoatOF!@rxOc>7*$D7-bxOxRJ^R^dU1FGv$-B!8fHqY{)6` zjWY;$ac!PsdPG=s@@K+%#^E%+=w>mc&%hkIw3ambG$%-jiB)4TA&*3?Q>5ihUi3ty z8Wj?1c7)I9YCYD;$Tjak!w~w4qROoKHpcx1_d!(3hLWo|!P~2=kBpJO!25xFmci*v z*z{biDo}l^!;c8>BUvM-Ah!o&AC?gt1*omn+Cmi2Y3RmV+;~2|I7}y>(2_gcIFrkF zKYgTx9JpiY`|bw(Fn@loYZ@J#R4soE(4c_brwE|G@6nA0G?HMNi9{k8w4*yxA~r49e#P_H8hT}blA%>NJr3Zf7>&iJy= zEKN$NjMHyf4gT(}llHz9NhsD+Oe&1iqgl~pKXDejx^lEq^Z8I_ItggDdpcgWvscA7 z8%-2uEmS~w)}6!Tz)T>3kxQ=o!W)y0&62OBsZmJz?w4Fl0@cEtWdB;$6I(FPZH3pq z5aTHWeV3ItH`6Auxk)9vRY+E8lc6<|jZvCS&rhqfaCzh?{4TIb^j8!8tBiIGTUkH* zsy7Ma0!VAs2_Tu5A)2L?_BxuF^Laov;S~`KGu4#n*`0tw$eKwniU)vaYL73_kJ)jr zDKtlCzNHI|hUFf8hWFhM7-2GSK~iQ7LK|>G3S^7|1cDD08OW+8hVnA8?g1<6c1Dd0 z;zVqWlT`vE@c(qu-rrtkZ`c^YCaW|X1q9Z=1I@4VL4}Q>R0T*M`8Zx`aSCPf2i*W} zHOKus#R4PK8;N21LjK1Jc+cg+B!wg>G1tB*-SNoX7B`V3MAjbXWC}lsn(yo${YN2d zy=*kI*N|;*SNe9KyOkNKaP0D(ZqnSgq$c&9F-Z2!$3@!N$4#sg<=k+8g5}b?%;jb# zaQ3o;_^d;B4Au-2BUZ}TIMeN&hP_SrtDwY_V>VL%{vHZppU97&=dN6yi{h*xJC4u~SP^X#UlCY(M;s(naHH}p8U%x=dA*xwUt>3-qN=BR3&v2-TXa1!}Z_{@5&{e(P@N^HYp%bNf|9KL{I)j(^r`#V6polMBBh4+> z3`;l*;3m2m9f-qd(WCZ-z>O=Kk7uA#wQ3=1eyZ15@LCO^3G=7mHkx(@BalNlP=YiL z%sVi3b24{)HjEShQAojRcP#yOfbQp!hX{K>T&M)ByDkVUy8R4Kadco+we%HS>fGsr zCLGc3$e`S2fMem*m-XfusYwy|rx)@N2xjx<44cRfGxwdB({RyC2hh_`HUpt$u(atr zksb?0vB;rOZR$>MOuAH!a3kE~txyf#_Bu}@w~Y3+gDh+}1E4n3sCRJ2xm*J~&v}Kn zG`OWqVC%;4Qlw^vV7%D-zQkBp141~+2HRmjRGC4UTc4tSwlhFiEe^b4BSW?` zwW@C}B8xZ0p9Z{=NjFj7LNpH9P?PYs1TERhlbCF@oGCSl(kkT|mJ20%Le==_KGbSFz7Se_P5gQ^Lb_i%xr%hGPARf zm)XLS*cXBKQ~{oXJ~4l}ao^VQ!0z3~DEy{)@~}kULx%&!iIa`9!GU-N2W+)|nzOYG z%~BY`V5gwRLi~nZoAMrU#=X$l{U;=D=shSdBzG1Q(u+}9tC*7XU2=Q3v3`lmKzLtu zsB@Td>$Ct#IvC*Y(ag$#Y<=Bx&!hNBT9fn{QSuF7@N;DQ;}b+nxm4fhettkOOTH^M zovd^t>x0#7BRPebL)VWvx^wsd_4I0;FJVU2{}b(JHo_uJdZ()W8lWF@}tcsPg%bO=k`we%D&BRogKNnm^X#*2SyWX`*`Xmheq@{&i46kHX zx38>Amm5d*-UAmLr6QIziUEg(mdlPZO9(^@o1rimhI{gR-*qI*q5M4JgW8cgUBK_h z{UJLz*!C5*$l0$uGbrlxeP$_drGyWbD{3G4S~p`+V_!_0-UTtzoWl#R=OnGZo2c(v zfg4SCSLPbMWv01r$Q1Z*x;B#$w)8fbWWfdd_84Wo3*)H}@VAgIqgA{| zkJnruGGltgC#R)_@bi$W{I-mxo{0lx`+qoi1$dAv*yaEuu?!&MadClmnL3XP9>hCu zqN{*=UL`$d1V5*i@7LsSZ~Y_#P*MeUm3(|nkwLDU)sl2*jh`MSaqsg6aU{7$C@Ps% z*ksLM)kGgE6-;#gPxH?|=>@V$)-zp3y`A4AG68KDl>rHX=cMB9yd3JHBEI`!7kQ|| z)HS;gpQLK3!y_7O2E$&R<#0%yu#8C=LEHc8<%z?r6J5k#Bcp7cGtu2OKqu|B<@UHs z7Fbwes9Ln&6I&H>(uQGx-@zWTcLKWeba6F=_KX1J>mp$U|n-kU8H8&{e6W4 zhF)gbG!POycFTp~mIAp{!`d8(&dZg^MmgYV8tXWKWn<*5#0w|}HFaapQqS%{GfW3C z{E8(6ZS$8~1y;A^@M`<>LO{y$K2-GyJ}DqYkw0ZDE6gZ2wL@5sQZC7}Yt0(qD$y(g!}V9Jgu7j@dmIp8#jsrV}#cw(_L2pefjDo2ZxSO2TJ4og^4)=yF}Js410F zy4D1_^3X)7^1a@)7T}Pm|9Z_U*eK7Z?JqoO$z2kfRNC`Wo2DO^^fwy+#43s-3k#yA z{FrXE69^*V`R=W}aN)q7W!I+3@>&mDaA4(p2RZ2Pf&g02i&WOnQIgG$+{a$^j<}8R z=OFVACN9r_^)=la0H6fa@Ur6ooeX+o>3-OGLnhwePUIp9SaXB(>#2Du4hdpWD1$`^ zQJ8W^lez`FS@H`EjxfAOCbfysD5x1lS$=xu2dvy^!b3-0^QD5qOLP!#+--f5r>XLE~{0Iqrk{rY>p0?;9i!`0FJaO%3sC5mq>#fED{o z>)c(vD14?w^+tY<9F5RCMs~9{pPu*ER}NZ0H25&47vucNa@W0W;A~%i36F9oz4N0gv7hQ4@Em8*a=w|kqMGWzsr770ZmDdSKI`13)qSOi2CrJF%VKojvtAS!-W+}_>} zfPw;=@Z(nXo$3W@D3sv^iUIW*n-hT=&Vs{r%;S;lLYyj9MGvZu$ABnU@E=BqqRP+g za`+>@)X9A48>`Q}N(Ay<(wBHEuDt zfj}7PkO{9J*1A(@OKxK=h-*!JN=?4hxeBdq^{7!wKUoKIV8$u-X>>8u>@~7s6TK=( z;?8Uze3H@!W|bwLe4uf_x>(tXsb*9N<-Py_@Urh4jmdugd;&_Gim-wj<4Dj2Sk*S_ zXO)!>z?s}95dY(NWUxYGy(qL^uVh2Gb9WZ-#RlRb=yS3}3eD-$Sa ze~zt!ftZfB0V9=yE0IRwv#6K$2Q~Y&br8{dGv)ej>$l^j)ve5!T%-B2eNOA2fgP8{ zOw+bsx}*ro*AyQf?(c+|mb{rWh`(8)J+zQ{l33+VD&|0cx{o3F6^PMG7$it*`gl3^ z>7wvezOoYH>4@PYF2`%RLl7R4y==m1Rw(>;XRg4exHS7cvi2P(lse*7xMI!1;ID?O zf=smHe4OUR^aBuo)+%KU=?cBi>(ZK8T|M>HPWp}cD`1*})~S=>nnTYU{DlT?4~GoV z6?wD0!k#eP6WU96>7rV5iHgJXl*Zn|ADY~Mt1aqdu>FbHLq8j~Ngf~U(cY`s$=J<% zys)Ekfb{S7(8d6&&e0*&yW4Td8EI}awzXeVcjcufOsYR_b}56*C7XcQGqD&> z%~?^s>+KJeGTv4vDldi>JW=cq)F3%dDYK}h0BWS#4x>0pd956;yM}P^pZ=$$#RD_Q zksEOf+q#um5oQ$Pg;vhX~__}%p_gW-3U+tPn{w<|}NFiLR0G7WnzxVs+yh={I30bjZ9_@0m5Mmw>&6WiCp6>0m+d zNyhYHZ-&KmXfTXG-T3VUD#c=3Y&hIhU}0423=gMbs9>UcC3y(Lf$%|f#;AM*<8ym4 zo#_QXpMAUDwb`vr_%gtG)hzXI>pu5?SL*m_r9Y>f0BooAL2Szr*Yt!_6~y4(GTqTa zal1?JVjP>QfpR9LK|23j5VAnK6!{Bs0+U$(bdbj5evP`(*=F!+76it@Pj)@yj4km{w>tH_3E*lScSh?U$dLpiH z*NF7`mXwEui169!M9Q9ol_;<{({W_2UA;}!6Zt*-awUGo9^LEG zlFp2%X~eaz3DI!WCJN8+LE{wN&ww0)2i{InEL!mKK1v}e0Lr_={oDu}E^Bejy6X}F zU4-qo9)Xpb7@7Pa2FIJ4@8-T3_5RS_SWTHkGH+Ctz_ctrV=)L@k0QbwOdGzP6HtwIXlb_4bVndMv)*=QD?7W> z^ddE8`iT80{v7afkoVe~+)*6q(+eP7?szC!ASzMak)c8uFBa1rm-JQq{WI`!%k5BO z7w3pc=W~A@W@}DnK!Fh0%u5y(l^EFmm@US6mG3$V6q_m7Hl3X4Zbff!yGhlJAoc^l z%f0OV7ANWZ_k-pD>6u2%qal4LUa{<4AhqsC@t~08P}^x1RHka@gny))iSi1}?5D+c0jcqMA69*j5)2LgA0f ztwO@!DP`bK^}%QiSNbH$`+L)B0UhIZ$SGpIY!W^U6vN{N@vCL=f1WJT1F!_B%@#&W z0e$ibc9Gv3*3M+Ez%Pl&p%yzhVBt>k?T3oUb{N#HT5)P5SMvC8z{Gk^-^9WZ3I#d} z|I5}jccaCbPEcq^0JAEH%~uGUz0da!NoPI5+Vz1VHHUfb5>(p7b6?iWxy8 zXNcQB@595{MqhYG-{8z4;X!xY5IJX-?`v8u3k+gq20t)0FX`SFIwBm$nG<#*f341! zhFa8cw$3gEdv3MW;xhl;s@ai)q+p-)rlFe*;tq)qV`jvVV7w9z#Aa>` zo}4`0X!;FyDJ0!mR%B|$3wxqGOsH8wo5mKcg>_Sm2oL^%dzhd?xeqA|LX2T|j5fm>ii1uv zyo*chTpsp3yFelwyONiax7Z&Gj85VRPG<1-xg)v4Xrkt=SZCcFVAWLejGVEBve z48)%yKfoKHa9?5iX7ow~;z4983WG1SIvlWd)!cV5 z%UcPmlAID9x^UjlS@gbXUu1Ii=$J?5$_B_Emjux(HSS~k6XmkQP;gP+xh!kkUJi~Q z4l?)P7DEYZjGj$hCt6;(ALI;I>0Mu*MvcC2WIZz=9w!l%7_f0^uQir;Gh^SJpE4Ak z-o$in+eHdAzi5#xxF%!bvbDjs~Q3_w9|2{=q04rIadq$RPUZ*&a@F zAj}UN6YgMeJXDWwc4t2tkQ*JCUA|R4t4r{sepvRG^%N%Uk41?( zb`%cq`4}tB0qc^z;fF`^cGgE}l^xG2>UKsaF*iibtYIQ6IAE7iw=|%ras*y0^DG5OJ^}jj{ktRubee9bbOhS4bp`%Y-W|Mu6v^GVvnggq`MA^jK7@lFN|t%Uwz6Hr+R;YvXS_sfuUk zLUM^;T^k2SJi^c{<#z2ltr`*t3#n3NL#|6C0)DN`8FztFr;6Sz=Y=9${!l90l6$_s zE_nW;cghnm(Q!oyDf*Lnl{$sqX)1$*eW;DRm3D?A$9_=qk@to466L*6VU+iq&;-G~#9NFIwjK|u6@QKAwh4i z_07~6qwS1VySev|IANww{Ul$WP=>Hw?fhF0W@}Dzy?g-WjF`M62Ge(tPL9M~b?8p% zymA@WR)DIb(HQ#7qd+yVT}gx;t8AvjHABX@*E{ebwOj9o3sBwD9(*zkgjICwsY?LZC851LGbtGhL3{7JRAGTNr#A+ zTnfGX>b`y(AGF#UFiig_B=0k1(}bunpn!Jhkao#>B;mQcgEMdPb~WBdJ80DJ59^Xq zqTiOa@|5Qj?!px2!`pZ79OA1f4LaOgcxUgNGGK11^j6AG_Mz~+LMhrk-C$Nx$=3Fb zodccK8;+o_a(7Dgex#@(C#EdEVm{;?Cr|S#QvNk0se<7pxGqIl_HoeUMZ-_ zdC3|*ua~T($K!2}URt28nR#hg3jvR*j@z!MgK1Y$2Mk3w+^*cfzV&F8w1e;NUP-tp z6OumWgY(Fktf*$(l(x)-DZ(OZMoU|0A5>qijTPR+Wq-rneP`U) z4Kfkeb54NFLT^v-*{SPrGa7})Fphoi7Zlp!L_v3^O~lVj+h0q~kILZWW?Z{arcN9j zr`*Bi=f>drUakD#hjS(R(V3t41>8kNCX#3ql0$t;eY3v{91?4t#s6Y>ha3(3T3U!l zg}f27g4v24`tbun!l*ch5fhDxMD94yNIFyYHpOU%&h%86XnjaG_yIP9i+k+w$grc* zrm7L3Ap7SP5q^`GZ?yh)G(BgnT=#Ww2yapQ%-Vjd&U$n(mw-JWqXfaEuHWBn2-F#H zJ%h(mIrO+O{`~%jgL6h@;3FljCrT>qn?NoR#_-***o4;I^ke;y;q)-IgQr!QU%BN_R~^L_U4UtLo9TJR!Q{ znM8NIp`A%Mzwpi=o3QauOC*+P(mzpBjIux14cJnW)rO);(S?6bIl+xW{WM3ID#LNv z&&6ccdVuA?eSi&4Db=7FZ^LLh9|jLNwG;@+pTO=&Yy&WEsb;8HTGh17{Bb19xr)WE zqUgJo_Oa33xF@jV)IPI^=?O?Yt3mipCi6FBRU$T8oiRSx8(5~y4Um_P2t9h!deu5c zMm#$?L*w3qbbO4z80nzkTSm!3*#`4X7cepFGG0y8EzU)hq5eFcngSB#RThR9}+@Wx%T;XTH6)}H*si?F)F ztW?U3;tbRBN_ffMRhXfk-%0h_(e@h_pj>34@*$Cw2qj_u>SDygoO26W-@sa4{bo_+tEv50ab_q++O9Y%Mu zz&O0zP2J^+C}B(%|8>Sn^6aahQ-|$uAWnB%zOTRAV`hQyAnw!6$#-oIC)4~qRU^M{W$Gi zr|4@sbU?X&ZOG{z>9Mnkj3VLQ9HG={x`mBB0*ugL{)y6G9#uu!V1)^nLxRMdQ!1~99L0Cma zD$TjQgrfj|I3bnT6;T3d8j%w&itVzSx_%#($`$? z3rR`^1dC@WW}-gmVzi213r<4f?NdSVuR>-rc8IGiQp) z>~|SD-HO4Veu11Rk)xWq0eSz89RbNMmd>@t7xqC0?EBom@ayHivgnJcD=lfT&;BLL zv4ZCx!k?Y}PXVL7KEeu^a9;z=BQP>cwh!?i+AUBFJXPUfXa%-MF`u{#3Oz?-T;YYJ zaM9I-2^UF4#&(eugzr;rD%ad!4ZKRPbaB%p*7%c->Iu-)XNaY@t`Uua3C33F40UrR zrcm%G84tK^eR=Q(iOCJa2C8sNq3mp71l>^BGumC~#T(r(E5#)Un9D4wIuEF!J9iDR zHfih}grCBcXy_&G7MERDsZjLBaF=BXz3#_#cm-t%Oy|?Jr?$B*PRihnZ2l)kFK_5A zZgVFVlR)}-z0RLm{Ue;)g4=bn)*k%CQC#$#m>U?RoOohaD%P1sl$Svmjp#B=$x4_| z-U;~#tOdckKQ7#{X|B=+I~#o&74lQL5dSFp=3fs^cMpc^*5IS+fJ`su-H9#ZOY67) zz(r|g|3txpi?QoOTkm>o69?1FeBb!TH2y+rH{ZK`laGn1 zo<4=xbNCX&1l=)GSLjj{e|(dkpgUiahO^kblhSiyGYOK4=&XafB1I6f+>|s zAmb-C5QrgG{|lkS+D9CI!?$C2brJ4wFB~I@Wwj4Q*8TR>31SGcmP|1P*i1y1=ZJqE z#c~Ny-ji6-EE0x8w+gtzT#fQ6?hr=h8G6Gh)#y*y8Oo^~8K$ui-N@XO=%NbniT-3w z{>Kirqb_Mkl-JOmfTPl)<-(o8m@i$6)?Z1-rV`HjTq9_IS2~K5``+sENLdrGg2Im5_QyZF?>+1#^_rA??lylmXq2qp*}AhJx{VDG zxY>1l{VAQOwu?BO^%~Q5&(?tbL5AYk%QEUR$4fe3v!ic`+CM*IZrS~qwFbDDaQ>mWv{a7k$I14zD|=dVl&*b;mx22EDWgWT=tiVWm0-Ry9MAo{A2H){1pd9wnjrS)#}%XmyhG@fL$ zs7Fsn+~ka~DR~Ep>j2ZBBs@Vi)T$*$jWt_SXe0)55!N-^NJJMofO;vU{h4R}8|GDC z%`>NapiIkcbKpg8BB55=cS)Z{RKsrvzL{F-ElAefTt16{$3C5-6`JHZdv*6?{A-fRi zp@idoM$vks$+S$inw>5SC6Fjv=d;qadRmsVokDZYI6<@nMEbIn=F2RFDZh3j>X5T* zLnS2dgYKS>i^$Gv^6*<8Zp z5D#U)Cf?%T6t>R3sCo_}0Nt?4+1KwVv|$0P%tDyZE+s!8;U8|%+}}E9Q2*po5)6~U zJEB%9CNg&)KdVqFN){;ZjZ(IJM|qVr`mRF`IkjbM!S*74W5OB%0V@ACmE|J zn;AzZLRV|5^8a+}iw!BeJG29qP3XpJZ~)qy=5{2SOwBdo$eK+vN6`(8%i7nVjBRVB zDe&LnAAGjV5T(R<*K+ju850>o3ROTHr6?r~_p{CpMuY77{W`5TJC7IeW6WPFE@J8* zMa=@XM=E&j(^HaUKBU@^tXVR%vfh#P$DH+{ueD~YX30PQw5(izWi3eAC9=PJeMn7bgHth56;Llt~k(Y8lLZ*6sRp?RTohH9%6o)x|e8ddj#wm#!ZF zx)LvmV2B#fAbgNWnH33)n3U;zk&Ff&!v0n`RuI@TtLGYROZ=H(EAo$i##l|n((eyH z!fRqE&#$`Bptt=C+nwH;P|6z2ce;~qLww;*q}9#o#hd;jyQIx#&?DDl-Wr>G zyT%$-?4n!TU&0zAX1W<0_RNL(7biB@F$K#zVzH@Qk19{k6|<)4-j)ZvPHBc16*lQd z)lEtNd)dYK6bRY<*F}j|sdmows)crxn6q19)s=g*ZV6y|xgyn+;2Z?*8|Dzp>JmV? zjK#;pPg5k0@)3==_}f$!?BMLCDZ}Utod~8~CEVpx;+yj|Cg5i_l0|>}$As!q>T@#7 z(;r!~#L^ULVzwK#$Rv0=zzhC4En5-95vB$R5}}#@V{MJ*0Xk+R{wvcc(MLif#&UN2 z-sM-)yFTetl?h>F&0+Ma1xel8@x$x~!fxPfz|s1RU+wkB$;G1fCGjT4UVb0S7MNud z+E7Fj@_HblQ!ZMWS&rTpJOyhOhgd&}%P|knSdp~ildNyT-YB(!(%BO=^jSYK`JoBS zL(?;ktXuF=Jjhk(XOSyYM1m5cVpo4Z5#;P(}j6LBxCI>Uj zk19*x&ihrjNMD}1Mk;5P`!+LbO7rF=uU8ci#9cZ#`0f|x-9T{ZOJ-7gzg7&0?>D$2 zh$Z~c5QuT1`uyW^Y`N!|&4q*T7eNbQ_4{TvyCSFddy_}x0t1}qji!^IOZl)RLX#8B zz6&KxIim_Ff>-ffJDb6sy?Ez9J$!l%H-8ra9VY%yM8juJxk;+(m9e#rYv(m%h_Ur^ zH=X_=aV{C;PS>YJeIjDFNZW8nuU3pN?i7rhmdkmVEK*_Hb@o(Fl1menX_zy&Gx^(@~una~4Jkyg`H`o{G;EOdLoHXm7Q|8f0Up^c+)z2A#*;CLdBAu#&nr-z* z-*d8$_pguEr@SSiI2AgZT&g7O)|;5SEGwUnSBufvQU>3c)xk4nZfB#BFOtY z?-!0czt(^tC&ie7{&zxYi_N8y=xA#rajiehmCQoDDU3EF$6P;+XF!aV1B#sfHt{f6 zL717q+`(D2Dzt|sZyWx>V{J=krNDB(F4*~v{sHej`91KxDDRN>kD)#~V$_!kUtztg zJ{ZWAxWMF$23kdWN5)8)p*s%IGReDP(co?pA9+4Qcsd?WkERQWCmn5!@0QWJ1AW^mKm$lqL?0N7_X5*v`+?S&hN)jVwrV1^N*wT2D*`+$!G zH!+Gkd@n6O51Vb z_$;0O?&WCtzTSV4`%JJc*im~DQh|xasknMClU510lVs{ivT9Jx zmO?Pv4J(*ggt!riS4%NeNwcS3I zOUd-L(nxy0RD-XEFqGhkhS9z<56J%3Q*ukKLa|?pcd>gOkm1rJ(8^YRXC+E?xP4-% z#3b6Y#qIG9lUE4({6}R2dPSXW@p1Sw?TGb|ucV*)5SPmRgvI@|)_-uO>dn8uuuK=qZ_rxZNv z8|K)mJd^i4Yi34f8COdaHGIq@pH!cYEZ_<7Xp=yki?ExKR_^Z)Z1iawWQquV}>D6Fo2wBCo8)Jd?C4TZg^!(IjW0&P5^<s#oSvQG#OU9MBjytSXM_|I4njXwMf-b&57G3WpUzCM{*?`$lA#g?D3~V2?bV$= znjRim{l-5S4{oDx$o^~wZgr5m4w#95_G@T;2~(^FxZYgv8MSaJbd{8JHgh~{HPUMc z`p=BLtCr&1+a2o$V!^kp&Nn)k9q$R)8Au;aLKt4Xo1&{KD17|QT6zEdD=%bLWVMRv zQ4@()<}i+j!R?}Gz;Uix&8uL^ZtuRyuJto>d2MNsv)_$;#81XV)9ArM zX^n{&9W)iQ2=S|8oXAr$z|a1u#^mv@r155%`}K$9M+_W_*_xZ2_KVd*VQW#MqtVC# z>QJB1U6_yZJGeM5bvQv`V3N|HP2)g#k(xLJujO zkeg#>oAa+a8!Xed;4j^eS;=fF>23@l6_=zEuX)d7F#6`Co7U5RxfKq z#c-S|Nhq#!jrfbjW>H8Rq3}_0vcU=h!bD3sx=Mq(BR}OLXxdsaes7sKLY9WJl-SrX zeaOAZw!voc?qb7ax}li2Mmi;7v!ls(tk6`wltS~6%Llhho2CkV{Wrw~nH%|@rCcKh zhpnh-MxOwwN_0pm5-^jTR_)%RmF+9=C+lnqT>TYxn-siRAtUYXOXKTYnQzG8Vm^t)*E}xxCAQHLGp7szk_drve#O#(8<^dz|LAOGO|KpiVy<#WBwmNH$&GZ)H@x zl+YU?09_wjwz!3kB&8Rn1otu~judW8^f@*g1z!?p*Q7muthZs+>MEt?I`4Q1#7@UX zE^@~j9{to1uwOBDdTt8c)G!v6JUvc)hPl%)u^@@+R&7&xB|Bjn4w6XEz|N30iWxG) zPJ7H{n;^&XvdUQh@+u%;C>8svOCl>HBBQR*)Ry+#a{1Y5jOCK8Lb_tq)AF(+-#fc} zCM>_ol}YCDH*?dOcs+3Z*xAS5zp~X*B>FGDNl6yhHIqJnFXc`yV-v>&k3)UX{;{U{ z^6F=8vrqK{ju#`s#-<$ccr%B?R*cM9^4!XN81W(LV7K4kpkMdkT)#Ebh(k|zmsf-N z$48prc0_y_^`w-!YI!UJt=T4q!UN6$7s&Jt)?gz|F%{5m`p`A}%J_;R0f@zrT~n#f zZ=;^;Yq<7Jn|eWEp0ZK=Zy1xB zV#%iGGWo=A!0y7u*okMt<j37VS++t9%1nK0#J~pfmskNZ`+D~!y;~eYx#)qcN zB8VR;W-5GfWLS2!(!uGa9&MVXBV*maS?8>`Nw7jdyyU`Izm9y6ynN$Ptqf_TKy(9% zDxma|$YguZ6oH$o9cuJa2DyQ-Av6_M%vNiMr)lG!l)mUe_ki)E%>2rL?1Z~-!L$Ym z1MRiTo6gENM{-f5ucG{jOnj_7klKK+S3b`U)<;X}s78(0t;d{*Ft5Jgh?3y#grZdU4TM(TB5w4wc_GUtEM}Q` zu_2OgVqPG~T;xevgyc7ARvME)NP<`z)Apf|eA-NU52E4vBc5EZq+;pst%9~FQ;ZC{ zQGMCLZ@F(9w2lgj0^EqPqVR#+#cu>QUGbeYZo|j>@mPqp{X0EPWKQISPaJ96byBc! z1_XOf6Fkz%TZ8oX)$l=G2MiI?2sH(g&DBs+wjNf>UrFE;{+#U?wf^sC?P4myR9Qw~~p3T&erM`v!NbmPp?jkO!e$?Hm;ctKnVwS-1DQMzf=YGyGv zmeN^Tck+^Vp9+!2DZ(#pted)k{*|79l6M$6s1R&Ehm)4uXREZ7^SC&hBty1mb17KG zR^stx4M6`M;(JdBaX@@|Nx5)8Dgqc8&xBMcP@*@SNXW&<9DO0}$P-yK9hr3Z(=VL) zvCk9wg$tha{|9@w`N=gm%w;P+w3!z#zkin`?YbY+Tt*V3j+94o`yaSnq-!paC1Q9P z1Hk>HZiS8Y;d2i?`V#~$K6?7W*f!I+N!QV{&%A?*to?7}bx*Pz3=i?;EPVq(yO_>8 zrNS)s>dYMrpM>W68neG6&r4!oZD4Wx>>YW?L9jb4k2AntD&_gAo}uCpk2YW$qLM4f zic46`Q!4~c>Y>2bJjND>1|HN_7^u|ewi07VB}bY?I-??j9<>OhdVAZ571fDUt@R>99T}Yu){o+vuEbR>?ETn+#iJulGNE#~ z{S0|Qt7cJMlZO>Ds%RPwYpI3whb)4Jh~1R-PFo1tDf?9@t?c&^|4++$dqwCRa5YCs zn{%d*y?y+N^J!AibtOsXpLp!w$A{;JS`|l&)#JgA7X{R(!zh~tiG{9|0zX!)Y2Rpf^ zp<1^9Ooptvfeyly&!SV=jSwmyw6PyN7@HmoM`JUO?N|!p3%B3)f(T1Hj-45chI67G zb!Q$`%(l}9FTdgeu{~QnYNi{tl!*Lt2SL*;&1Lac*mj}7A9Rgi_<2t@R|icJqh&G850iqj*4NiPX@A6ry4RmR5X&jVD-`?>D3uS<&+L?;U;uDu4Yffx0Uv=en6dn@+o2H`O5(PP<+U`CE=h7(>O zzYBo?;(du!mMw6e%J?hqaD^?~;+sS9PUhF}(Cav3%Hx5lgb^oGjAgcBh%MO<`g8>B z9C^hLwuqfsuIj){pEeOugAIpt1I$PzGd5T?L$ry|s@qZk-OC+ODu(nOK_@>L-@6ZK zXuQcvBnRrV??d=1SsP`|tR=4-PMQyfr4}wjOS{W8%)}v~PcX5eF=4V;A1V^KwK`vOB zIt(QiYR;Gs{fptBsJFWR4yHC=t|iaj6l zYbZqNLk|I!+B34G>W)+>Q2&K+^PwlGPQ)@rjl5|`$Y^xUri1s1TZr!E0EEfI71@na zxm<2EAo;b2=A2s59T6LvdHxXLm-Mj4yU8pinVkNT(W9uEM&jq zeTUipA^-jR@;za8?@UbHTqcVdJdS<1ckT9Xwrtyi12E01{&T7d?dpeo&!5@W?pFL8 zc@wHI_3w%>3_8fDQJQ=qS)lbjn1yOVV1Z;+lq2~{oT*JLVoeG{$`na!_-{)rSg9W7LYtGui4o ziH(^Z{Fwk`V9Z+|#DaBmEsqO}`X8RD+8}2ev35{FK_Lr7R3G$x$<#K_**cPpWnRj{8_WcW}UxqmH5V&)i3={^fxOJjA1>BFgm(k*U_P4Eqtt?r{ zqB79%ay2$0;u{kMUax#LS6_x*Uk+T~0w&lY?i*Jl`}At1)`IsU6E-huAyo9qkMU<# z-_>ljssGs@W`8J0Es&pnMuxDhd^Y5moMS!>`Bq=>3N1v4vobJI==6;vA0qfez;r?v z{`xruLbm#cKP%A?D38}>>87cZ`&q;Mo$jOe(%C|W$D|2?gbf4Eo&HkeMFwm>LX{z8 z9{q}2W`Da2>$#(li}k-N&jOQj^-!ct3c!vBYyZlXM&j`$#esj2F;Y8|9mp2&A;x-Z zqN0j~j`hP)j`0P2i_fW{ZbG26pbxrfd44~1VcJK!NqJ!(`E7Gy;;Hshwi>mo^QzHk zbuLS?gSqX7{-kdD*HLKcBv1Cv#3>?*HMxHlgjCq>pQ(pTjrfF+9obHNU7(LVLbAo( zxnlOQ$FjxF>?2UD&4cfu1Zqkqn;Kt$*4p12VSyb%T-UkuCAy8z;k|{=2=}oVh28E| z>`)e%pDD9AqhN23!NxLuZqlB*1j=m~%$d!jmlSg@TL$G68>^oxxnfSKuY8uEUA+gDc14ll%Xiv63Y{ye)hejsJW1{`Kvw0jDE+Kf@?hx*EZ|*z zPkR~2*DU3N{q=hb>yTtV zAnGqLA9JSRIa4U8I=MmpyN0a4#0L%MrNW79fD;*Q-RCGmj@oDCQG_0~_ng&)te!J_ z4basI%?jyv>Q=}Ym@>o{!txolCf3t8A-~i71E%!!WmATqgpR^S_t}a~uiWP-SWDK- zp1p?f^h*#5Hmb+ue}bWTbYvN+_l+C$O&319e1np`Kx+=0ZFVdj&d)aOegb{X*|meQ%CXbiyJX1>Yie6@Xv;vXyk>T` zr3q)dr1oZ-qoxd6Tiiiz6VAE)iR4J@V4s|HWicXA17fju$La54?P8O|x9q=|;=wJL3Cw>xfkxG`6pW5@O{^&ZhZ zd|V|qxHjv#6vCz&rr|`}nsRJ#%i*h<6LYeum;2J9pF3c z=c-d}=L2z(QFsH|DcpJqQ+M=yb93>`)OBOk_=0;DtOW7a(}Mjw&AWj{Cl1$6I9Qhr zM?3Kx5C!q)wO`Wn4rhCMo`2Zk=gr*o{I6mo({I{ZJa_f!w^MrC8E4!^r5|6p{xbt? zpLA*D`zJRwUG)m|9(F$aQ;j@j>jeI}YlrwFqE{Fr)iIc~CXCg6rsZ72cEB5S8=F@~*;Xp3K4~rO zLw#h!61!NLzNEu(_xnkyBUOpCCyGm2ibgvX=fTz^dwUYe#At6AnXu`IR!EVQXnbTr zI%TPe=Jo!q(`7^LnUnR>)=;`@#&hdjw`<9aOi0y{2D=uLj^0Arpq2@eNTSII>?@kx zOXiQRiAi>$du(m#sISSSW9hPk`{MC+H%3FMC54Pgxw*JJO^FAdF@4L7mU&TT7YZjG z!uD}UEYB?%?Z~irP`#zMJB(b^(n9XeO>wV(!}1xQ|DmX4WW z5)BFePitQS=SFem-LI-owWOBREw%1rG#br}G?z3Z&3G<*Y;2D`w(&7OvEu_w@R$TU z*myaDIm2Zz0UL7!5-?d72iSb(__8rq4gwn>CJ+*5vk4?DkdI`)C4_Kf)ZVIUX=Y5~ z>}Ip`)0Dbft?t&Vs#ot-z4!mu;-<%ljZ9IJr*rctR+BJ6RpXxs9jdBG7QI2!n`p8+ zvAENW)3T~P%eA~5-L{uW7w_uE9e7FQ$0~u{M^?*nTh3IlOX(~Ji{Vn6?kOJ?u zlA7DPS>auN2dr~kd=Tv^w)8{AOA2&_^`Wc)PiYaQkHu3~67e0c9KRO-5swk+s+^aM zO?BixI;B(VVgzbklrjxTlq1cgd2ku2oYEy&c8RaWdnv@4+=-t|lk4k0gpV@y$_#in z;cA9B#xT03f^raUfClR=NQk+}&#?7YQ`o;3_8O5U2*az9oERm>+2fDZe&;^?4qndr zK7l0YCcm~7&}W7g0p7oT+kOm-Wd}$q@U6(F^Xz`0fivzmO@vx8ugQYB3G3 z;NRj_GR|hXX0C;6Cmt0?bQr1eb_zO!AWpknEo)ydmRW3fR!U_hGc6T$6Q>5jOyf77 zqo&c%5+gFLqiLWDSZsR)=rEYp+#T^PUf#A@rnzMJ%Ql@Hf{6T=Uzf3aLgAZWb} zFWrwgP^=Gf)*|?N4szCyDnG?wLCJbQSFsfMsFs^!$5r=mGq?rZDwcyViNeE3awd3_ zs@Mc}%S)SGG&4V2R}G|~i{p^?ro5?+w3+r7{b?`x_ZJ7DNQ9Y{(&cPXU`O%Q4(Hr_ z#6uiUS#`e|4p%lO^I>>_0qBL$+!A{Dt*~1aF<@d?<~A#IIeYw-%{(`LTjvMoUH9bU z*PM5EfA7lGy@P|jYgYAwcKKsP^n&6XYBXJ zNN}3jb&|&~S^sV=fWxn>ZiIBxsh+d;-ZVCL)80Rly(#>~wp`{{;|D(Xzakqd0y~ze zwyx}@+2t`HW>Grpr_-F&mKV}B3I0v;+EVyzM^AULuRjuvh6i3=p6yGeh~iB(HbYY* ztlLri#+5t%r?UHaE2N9d+M7b{(T|BdrZ;L$gLNPOu+ZjdD_nd55$p0=Kb$JdMWmH& z_}`GEPNdUSPIndkI+4_$%JpxO+rxJp+rRJSGYW-mCkXI=Dk|$+jj3ZzNvi_s$0nu5 z^xeJi20o3Uf0x@?CgB8*u~y}t3;iRObqwCH7xL!U6<N99O!zTppwcUm)wqd&FLsTK&*n|s1^YV6dO8qo!L7L)$ zoNODta~KJW>P=!{$dcl`zw+BiJ-}JaW82ypP5~;Bq7k}tmAgsN34N`o|9*dL)}XbS za$3M`vePM_WcwMo6$x`wx+?IzOL?;L0Mtj&<4)aKSp}&eIz$SdZz@Bjl@#HYK0x=$ z@?g#z8Ms{as^qo9e!I$kI{_Ep$>q5bdu+8)RY^C*q)|>JLO0YfrK!=7wkf2i@Z(Kl zPfr>h=r%i5l@{T{A;>4K1r!0A?9MI2;nG~h+Tsh9;t7PQbIXh{)&39_?Q>X2RXAi( zZdKCcU^9bxzO6J2;ZCD57ee}E zKfz|;z*F&P%2P4wfMLEdbj*NF;L*=~Q97M$XNpJ}GcW};jFEC({p*GTF{@t76q#AI zu(u+ig}uL~zwSGzm*ppam$7%&b{Odc3Rz24H|c8+l9GHcm$atklJE@UZn)!im+M7^ zrY^r2xm_`wSXl_2jd*2^k)UTM z63t-kA;mALski(yvb2qO1cI;)J|kkEE=}mPDYBeRMu;<~TA_cwLBPqSc~X>ytZ64B z(HQRbjw;~mE7tRWPL9yYblbZpl_cujvIn6(TgI-#_`0#tZ3t}}9X$u3b4G8Nqcv(b zQeuT0=TOL>zfOkZuj4&iw(P-nSzuSi_?%I4hezkQ-8agHChwgsGr~36=kDYY-bi%Q zsm$K6r#sT6ET;OYnrB43J?5cfqN_S4&md}xO@~xxK8Q6Tni8EqsY)|KoTN|eq7mn= zqi`0>BneJ;kSk^>`BD}H5%bm^A~BL{pUxWpf0GIU2dpFH z!y90FCsha$>$(1gFmN1N}>)(Yipj-XQ)*(1e)%A-^PpAe<|HtH+r0- zBvXOU_x)x>rwFRd3;GrFTc&K|#l(HF9<#O)C+1jNu;M8U0UU%)g_%&fe3h~3;?slm z(=vW?u{)FGVl(@G?Oh1}pD1whIsi@|O|yoYUHS_C4Y7e^#B!g>UB=zPab+xJvO=~7 zp*aB{Z8JXW-YE>>LYE|D{gX;*g0BP$KhHSofaaQs=uw2+YvoYn=LjFKF*p=#8Uue-~ zKGWoOIwY@(B9oA~6+if^Y4c9fx?f@KJE3~Q)N2=Icg*8fe5%ty+!?14;ll#2dBhZ-@T$DxaH@vl z)$3vo@XN9&BDYsWM;7}1EL&of<=`s|8AA!vogBvlrs#wQS0FmzG?p4vFv)8>?%BY298nD)3?8h##fLpk!=26v+KmOK0| za5#94pRp2I{~U`cyy{o7IZVb}yfB$({g&8`;}=sta}OP38*LIE{QG1^l2ZD|fvZt9wY zm|ll_>^4I)O&E^B1mEW!L?;|#Qlo8Is&NULe#1|ia;qHd@Z2qbZO7`;WMBElW zKjm^^?CW^qdVbJ$>1<8lW+65 z9C2ceii*}6_XdbkmsCCJ4hNJVlCkElkE#tK^6FBn8`htCWv43lhI!F#HqU9C-{u#w zpkUBE-ItGizTnr?c1(TMPrUm8<&*v-4^C0^ zYFKRTsC`$kKKkC!`DNMXpn-5((G##h@)4zFeV2dyN!jbNc`~N*Y%V7{VdH=QH$gz8 z>x(Q`sub|E6riroGM(_JjG)bbk?tEH<<`E(<( zd~Nw=hs*8Ukpa=I8G(fd=dbE$>R8>;UOw}$5{Weqng$EppM{Vjc+oQH8-cmGb3;R< zhmzNjx(?|AcEAkb{`h&t6*9bp^Qj^42dE=GtWA-dm!VaC&W?&8T~cChnH?=qjSjMx zJ1>J<20v}vB< zQ%k$CBExyrA8##kio75_EPE7E3I$andyhyoLFr+EG=P#IIEgDQ!2=FS+^KkF$yzFT zWnO+nCL_d7N%AUqnc&fQ{?mvDw;>4k19m#`a{|%QDvu8!;9b1U620Tuxf^&gK9Oo9 zcduLlWMt$&&68nD_%z<&kjWd_A&2n&)(41w_@Nk7pLYZQAunhiev70aG>a(zFOvIv zrAK%KE*{V(K%^b)K*&K1N75v8jb}D2ZLsJ)VxOeA48@LWIAPWF1iL?rQ(Y;6{O_g% z33UN!xGHn==9aNW^?sn@M!&1fL|0XI^^yT$8U?$u38Q{X#)8VtWK>||!8uP}GiuLI zvHSml+@JIZViUG;CCZ8Qm!Bg;(KKnioRl^Kj5??KKJmfHpU}EjGmN;dpH@fz=CTq_ z*K{Lh7z2Odkq>|o)Ag8<)(s=8>xTX2sUGDfW0)>tcHRS&ZJ@59i;gY{6d>aa%bO(4 zl$l{!7UtT})j5C)ab${8k$Rt{I5ymv*kxkIF9yn~0A1iFRw3?e#iWIIl^SwHF?4md zqB=Lt#!S#D5YtbwA>znzBTC0JMboh9;1e{Bu>y3O;Ose=IWy!Pc+kHphrdPwVxcbUL)-=d2Ge)GenmzO}l2nRI0sB z1Wt!0hT~)P;bFf+^N{wckY@}U9%7y;0&+Wy8R3lA2$2UP&ll2tnQ*fN-ehxobJ7b^ zCR~5?rwLQN=-G-+H3;^cfwuAA=<6 z?5`c8k67*yb(fTDo66$1b0c6(myNr2R`vX)_&Cv5l;3MH_eB~VQHyFKl>mRC`YGMn zWcB-vPgK9e>fc5;pIZN&cKx^FlU08M9JfFu(miJDE&5Z?=d2%2RCLgAcofCsFoJ#} zO^+hi>N%e?Ai{xmSsOD%;X>ib52Gxkf!iH0k;y18r_BCfB z9#16GY>03PxvMzkDJ(q`9yV0XgGn_Ae^Qw~{e;StkzJWSAP=YeXl?#UT_!G-9SwTO z6E;-U2zy)sN5&xy@>nKM!0Hm&SQ{6!`k@?xVJ^+(xh`&in?reFftt`Xra-FYHD%Zl zIt^Gm0b(L%L-Q6pDs=H~#0o_~CbB6t~_3{ZHUplyST_mkVp*z26 z>;^1Y??3yT^#@_F7b#>Aa=41qYE^*my=X%u?TG-n)9g!nkbuhU$}79?rWY!u?>TW7 zLiUYxIPv3qs{57mqIKy=&>L;c_W;^E_VwS8A?5vN*M51rN=6}B8~XBUyXLKLUHDZD zb&-%K+LY^o`Xmn9^+MOi#<&0(=Nq{WZW_^;BityRP1geq(A$=0FVy5|)>bvmn&8V5 zY0@V@GWjjy2X`{k2I8%V;6K`2f`|sG@VZ@hVbOZ$=uzvP@0#&=Ydmf?l}bxWrLAPI zB_0nnmdlGvrQZhF9{p@_xqNo@%y2$TUgQ|;Cf^sK+?)-2%y1pPYu7)0Adt)c8$GQx zo&$QtC8gs)FVYgv1HIYOw-j2!^gFhXbR)kD?;>-k00jyZaDs zX@s}nt)?74gm*zMD?2?-HJJf=;;OXO58tj|RUj82m3RwHz~0xC$A@+gj5S#ycq=#7 zxBE~nmhfq|zUyZU*&KK$f_nTywu;kW0_l03_JdL{40Tp(X1kRmlUsme894lpig;i9~7g7av=(R z`If$3Py<1~6GGV#or|IVI{J_+a+`>!zMZ0W6P=u7zN43kFj<@D6_a${grbNN?V3zL z%{ix*2#b#_l)FyIbSN_Vi8@j0N_(?3rkX}cdW&5}v*;}n`-IL3q`N4vEp!#L=^FV2 z{tExRweI^uu1GGM6TT03e_v1(K~se9T4y~YMAPZ0@C@vKMu^o>w;76+ch7dMo$exk z`97e(u@~?hgf`8q>kYoWERuquj;m(3ExYl$urziM!p4jlMi|hvGp5fU5#6dVf}VtO z8C)~-*7^CFF1n40UxtCSVx=VpSH@aoR4O64C1#z^z#*>8&$RCE9|1H6LvKsI7oa!q zcDda1Hlm=v#rozJtuuu?hQPY#+;hRK%Z14OHbr#n^)qIS2&yKG%(v&kn4R}K&SY84 zE9puO1#zuOmj*P{q)w~DmZDAiY)i3&lqZwBQ00uyL59j&m=LeoRfK#-_#r&_Lm`s~ zwrwq;+vwNnzTi8fL||yBaLt_dW$QaX<6bv|3bus`wuO}ZFu>`RONWd|#2A9$p0KnP zo*r2sYO1hc#+i@6{_HdrdU`;embJD_Y{NYe^tW1%j%lS7E({^-&Ye52?OyHa*r}JwTP*E)ynX%dKL)YP7;O*Q(ZZ)@=#^-dku~o)4pi$8iu6D>C_It!t* z7A>UZQVg={G@kG~WKbk$@2x8yd|MFSe(<5Ud16;#pP^ufj4<%e-*)TsJpcTyBVXur z$^tJFi;aFs5GAK%c#!0jVPxV48!yYFvliBF!?1*Q<5yYixxodw8QfeV$z|LcZasG< zcQ)zw=W!Pj3;k;DI$NjKq7~MH*{Y(VtD1%0+*foLJWBnf_vxJ%qdy8<8Bs0SUc4 zeK{ysD2~e=X!&%~_q*$%zCdiwBi4!5n_4|Arii#_s`to{i72z#DFhvRFz zb1##v!?CqJi^a$3DY+iLa0I6hXW8B&Y6uDYb3hlF$9e>_v;tUo?C6w)xvr!R;5T+g9Y#^BVU( zwP^Ofr&fGcwPr1vom;U@Ra4vNFWdeuIe9j80dU$x{n(Ez@~1N7UnoujIHjg+Pe!)aj@#Q(aT4T7n6iDc<1po#=9KH0g1q#hSLFb1T=Odn?W8)ym9+)c>RfI3_m#@V6bD zvN>6#q|V-*?GQ0`8pJweZ+LNItb*B!NQJ2N@g0RB_P|XseAIxdgx7H0BTuv>bLZE;)#6kd)^-(AUqV=)EWwztWnZyrrH-^zgY_aFEfTr+w zI0K=86T;ar`i7E{U%k)T@DDe;HO)QnAR4{z0%uH%yP$Fx*294ai}?+^-0(G5z(rof z3gP(UKRdpL*gSW0z1(!-U5wKCzIsVSCXJSyWVsX@vgK^Lp)|;|RT>R2Mb$KkqVcoI zXqpU2*#L^VhG;MsUHTeCbViz-`o^%e@96W74GhE9A1uAJjTb!zZa#P3&No(@t57dMZG%I%0)=fu8Z=b=4@}^z4;K)aU@X--c)+I&3s|)oFg7wZiuzuw>-A*^1)JOIT zC&X0EjsA~xyX4n^mYyk_cMHsY_>BmqPLU=}w-)GCKj!(5j|}eYq=c+;@1?VecD9O; zV8ITrQ&LhuG$EVvNK-leg0!DB!|S-7%_B%=waL`zQJKwC&SJVqNR&)1qT37*`;=v* zHgshrI)BL`Z=@=PrM)FGl z0~w5X-JwTmq_)=Rb|pl`Co~@T;J^_%syTI+V7T(lCh{4rfz$6-uR4+yo^AVg6fO1SH;qcM%V}|ZkyFpF`EEI-44Dxa$x+5`A6r&5lMYSb+u%J zs;ObQZH-q88Q!R{_xgD+&Bt{k!+@}gkjx{GD(i?edWBy&V);j4r|9GX6tTWBRPu+w z(Rs5RR5Tt!bsVk~A-^^)LlDoA-yK$=&4zzc4NCjsD7JrO}@54tp$ zCsP{nHNc$>))u$pOR^9&+!94l&(9Yyv6(!)^-H**!P?j`5s!%PA!C?EeB5s0-L9m} zgOh1{ELNAs^42&F=z=#j2dJaOGU#m&f<}v%yJ{a4yC#mAQ^-ZHcB|+xBVb>kx;vpI zfexo~(2}eyNao=Mv~JF-IqML(G)=+huU)nF0#vLXE7ta3w1JG_a20F*OfAeXYj z>EOjKzXj%kU|D*R3gWEBzx;4kJ!@VJ%YfcQuo15doKX>r=Tk7ACrLCMaJS|MH9#h#rK_ES`~2zCy;U%4_s{aBhFUJzs411scbl zqj(VJf87gGwGKo6cmKtXP}$ts37q}!`Y$>|tl%tg3*#v7eB z;?i*sy#4)Kp2cLps-K0cNxPu)5zC2ZI58hl zpA^j6d<37&WGQC7D0QX&n)wL*gb9eE-5aZu6EIMepbjmCI_go-J?rO{2dtlyJzi>G znzH_BXG1HX(@uGE}Zy4pwn2A3eTWN=_95>ChXn$K_9Ldh()tiJw_kDNvc zTu+0`zEkhme z|IYsptep%%5fm^D0JMV#+<2U0VPIfje#yYV!aywk|DSlZM49)IWIZg%?EfDKtNZ^4 zuX&9B|3T^haGDXDKmY&5X$BKW4;91Z?Ehax*^AXbIM_Jd4yDTp#?>91a%T{1)(!>+ z06fbdiU0rr004FY905iFumT=;rRCK=QkIvYA0+#G}*JRQIujvoXc)F3J#$RUOy&LVsx0wf3|oF$$nP9~lwP$()X zFe%n4?kbEcPAs%7$S#O4EHBV7Y%u~cmNDWobTb|^&NM1CqBRmVhBfFmQa1EB0yva8 zggN3mq&prvPCJr2xI7d*N<7v*d_D?3G(O5dKtJX{gh3iXmO_L>Bt$4gkVPy-=0+w) zj7Nk>R7oI7wn`34TuT~DuuM=*FitQ|h))zxs8Eto7ExSLv{F`5oKomhVpIA60C=2Z zU}Rumn8xsqL689in1GlI2pJgugZT^qFyR8M0C=2bk4;X)Fc5`b`Uj*Es;FRxcT$eURSt{M=$orRxg}hspUb%p~)hXIlqFLUhk5Htd*xn=kign7l|H(CgoUT{hfjwUv zQgw7IZp)sOG?8A-NfNR`Ue>l$Lh1oFWmk?7dR)TA@E4T1_2rXB{SW7?>7B+dz7qv`Jwzif`
|q2hSViV%dX`aB1@ z^7Dwylbb3@N#hj9PwZaSI6H?iA<>3zb&k_1J|wPeO{2x!8YO*Fsv`JQtFq#cZ>$SG z4z}|#N7`(gW~5!W&WYr-DLm&TjHGnp?95ZbWbD?Xer$yaDs&km7c+6<1|XnPgiPWQMjOH&;rwD9P5ElGfyQX0#P~REs9!;JCClkNu8n#{F_( zO=z^}4zDd|oOH9G!+RExY*~_B;wxq+=?S4xEet1QT~2Q7XnU@(`7CJ|5v@&H?-{iB zypaPf`B7~rEWX9^T8Qzz=#*+Q z&&pt5EN#=pUQB6SncY^oE>O)W23lVlITeBlPPCdhku5r5EA|z0at1s1Zyr@P#JLC} znX7Ba=o`-b$kCx!rI}dXvf-j%n75S52tC)*K--dqrR3@{p;;<|%aTl0PB!7b57l}QerI!0roIsw we(S@aujq^ml`?>~HkoL8$@lkF>C^s7A(t(dw&MOqSvw2B*2>C%`%d}-0Li%S{{R30 literal 0 HcmV?d00001 diff --git a/static/images/aiwrap.png b/static/images/aiwrap.png new file mode 100755 index 0000000000000000000000000000000000000000..36fd481b048959bb4d6fbbbc932384e0a4b424e9 GIT binary patch literal 3032 zcmYLLcRUn+AODKZ9@!FE8E5Zo?(7*(PRMp787V8ev)7N1-HFN`6**m~v-b&^XJjS( z@MJ_;kABbddY;eg^?rR`pZ8~f{`w@|FgKv5xl989pf@tqwK~_#=b)j206<1G)l@z= zsQnG??gK!}@&{xf{~70bfQ2_4e&dFhUy$E@FF$`iBRHJTKhV$9`>qE7A=6lE6vBFq zUE^SG4`z~pern=p#YV+v1xrS=rVB&)shNxu_=~65EL$(>=}~a^6eWMgbaom={h4?4Ren+VD&WVPyv&YK*q?{(-6FA1Oy@zZ5^PZ12UEgv0{KC960v~ z3Wk7~Jiw~IW2^o{yq0MddVZevFlxV~u&Z^{c!6jc?sx35+ zOAe#J_;q&xfTDD^^KExeLWm5tM50nEj=@QC{S)L&z}0o>bY-Z@UmJjhpvd7<3F-RF zXf+7h_w>cJEefyOv_%gNl0EAfwHrXu+O+Mi|DW6F6+gjGO|2|1Pa1c?>|Oe7BTi9m zP6XSNyT2mUj}P|0eq0p7$T?yRC=S224eXhgTp7xsj&Ys&mS%9=NPBk7Gb+$w5}D+Zsb2rM3|64gvA2@ zcAETJzlc(iqrIXRhJw$wwN4C*t^u^CQAPj&w{?Z25PkJp-BbYR7Ga=obh!6gIVA{? zE3Fr2T4|4+m11B*9c?fs7>zsnN}#h?breiAZtx8c)LHtAHje_q(km|0pFyh4<~@Uk zKjUu~s=`*$W(+ljP7f7}3;%QinQIbKFqMiUFZPhX)sTWYS%|MUfz?vLBwf)^)+!0X zXJg2-qY-#PCEh{*L%LcIkc&EwYc`O5im9{I_(InJ$CoQ#j?sTX``JB6Hb0HMsNi$p zyUQ9WO2w<6tGpPe(=_4TGKBA(l=2;SWu&^=VQ;TXP-nXJzZIyatLnP^=4Lf{Q{ukc z(A!`NW{e(zT8SbU#^=JTV~8|Gnok;*^IZ{AxHL_zOidnR*(M^8S#I)9_`{`N9h|Om zvhhem3BhsZG!|*5@C0~=vJi73Tuuc4r`>(( zC{>X|S%^0k00N7e%f5?ih2eFs&%8Ks<8gbZlPcTg{di zBQ7bX-GbE=PE;Y%WmYZ<}v2^*&v}f=p(lD zKAJ4!Ea5C}s5GLYvb1uplGbL|rXP`3aoakgR^LY6dZ~i?OLmn_CHBUKHMcdYLajW$ z5>@Vp(0UbVcajLPYd3ATfWNXVdh4{*+{H)h##J98!S6#~754z6*dhFOaO%-RXjAS9 zPgAl+WksXl)@`vxG44_YQ@pH)gHL64 zPBya2u*$N^V53E;cC!py+M?HFcZ6Zk={6Q|5HUcd5iep_t*5MK%V^5j%`{Z^Yo#iE zO1O)gbb~+LE8EH~{CFE(XzEus7uvG=`$~lL3F5@}ciFxH9THMcCj=qU5joX66$AX# zd8E5ZKP=&n?b`4Xmn@f@g_v~wu=>j}{lD}XQ@pzzmV}mkyL`J|3PXhXg-y)X&9?Hb z%Xrrm=BgD(ch^Arm-WGCcS3swsz3g#P~Z-v~Fz?I|Pdl=O%-zv4+!ZqKE zy`^GTg0#8iSg&7$t+_VdRMS*;sSIzPXEovus(5bkMBaZF$f|l%buORyY)dPmb1!>M8igEU8k&?} z!m;#=b$DZ~3q}o494P$#=-q_mM3bm~sT@tI0`9Kao|a|fp#1>Hg|Lx*Y(afN+h#fG z^_J3>_g4JO^8tnDVug0{_dmDyz`t6q3~SLo&yw+(ua2~UTVPNPtAf&Q(&@G}6K@(z zf-4VPwmCF^Y6d=E(p*wko|Buia~e=4H1;(1TeSNXI>pYz9}AwjpM{+bgEI6rG8B0t zMh9bm;pi~z5&x%DEme}o&$_8ios=TH4x3I|R87=MYYo3w&)-jK_zzZdiRnadhS(dC z-^V{wx_om>}+_I7hB@kE4HRF|lng zwv4?(idjN6TiZ0j{QA>>=`cad*;B6VumoRAr3r20X+sG}4=W8POu`+YyyiQGP_wX% zztfu)Lxno!OC(nCF(MUDnoa3VXA4vd+4CRr`O0aiw#Y_jq(2FORq~r<;!x|TZ=MhP zt36`tPCM@r{yqr@^&PXQ+O*B7 zjcgGPGxNdRZtaU|Z6Nd6ptYe{iH+Epnb>r+KuX)qH@N(c!*Xk~B_`Ffk~nhM9#4wS zgsgDK`}KXwn|b=KqN)S$KIA##x%O>Nve2F(yE5ln4#PeAsp)t0^EcmZ?*G&yN#_iT zlA5A%aQw?p>hGI9%6f@|YFda_h&{w_gwoc?Y=g_h;l1H4?(pWH3V)B0?3)ixG}}o2&=Sl@eKop z?yDb9vD?9t^h)$Le?9A36!g~(U0yq&o33vK!`i(0Q;w&psO&C~`vVZ<-1g*{W%_ zXm;Au-qe|b&&H2w*`Ll#ZqGcvLkKR~9iA~bfW$@*ou==nF~TpV<)!t<(Z_8RUe{Dn zS=0(VSvxL+54jHs7jm4wIPI_HV3&?O9NU;64(ARR=j`NUe)4@(a@)L7uyN>?SWsI{!EM z+zhSE00_GV06H3gUuWlf4S)wS0IWL$pqdK+hhK(Mr~dhiN^Yd9g9w>+Wo2V)aB+6N zK0r7ZQ7E(zTs$uUz}eX!*U8Wyy6qf2=lBl~%T9H3c1Hf6h!T$`6D|+b&jqv@LW1Py z@=rcYsXGUQabY26C&YBjrMUlb!TC)u{I9siAc5fAhNhH}m66GXJ(z1PqkP>ckhn+)1i}N619iUPoSd9N=}}@S7|a5-3ZyuCjE;`3 z&Jcoz1^@s6E>BNY001BWNkl712MrHNaim>Zu8YvRP!9cM5U;p?2 zGw8ilD*T7|$MOEB6#G2}4gOyAApfcF-@KprFX=u#yHlz9ZQcfz-R{45<0g&N_J93; ze8c*_VW-jGZ#VQIO;`_)b@Y5S*n!u|^o0x{nd|M(J*U|MOV@vwCwJu}(dcEkM zzh3mqKL7mZFZ%C){*(Ue{rT_z{)7JOzy9mJ@1OGd?|;7Nk3W9BKY!3a|M(}>rw+k3 z4N#H3m|y7c9lytI7{UFu1Z9%Of9W9SCx|>+p2@%Q@sqZVo(Yl%8jLC*IU$|AUY^J9 zEgV<~`d#;3{SqPEMBwV*?!RN7?r(Jdr1PG({vX0{|E`S(3j-I)Oo%05q9S^j>1;h+$Py&KaRG;YdpqSe5J zqWOZcU{){_j)e)KA?x;`KWtnb@v^#Y@aMm48|2;pS3;s6J=nHVhREIg#>bmOm|&~mQ~E8mx0tm&?iG~Y{XZ`X7pAwU|x$v1O{ zJ>cp;ao_K@zmwH?DH zLkH2%4e9K;#IMKv8DwZqrT7W{ipNRhr#r z$p%m3?PWPoxwE_8H>*4u7HUso- z`OZJO?yuG+zfTjx1VnbNeBR1fZNus4x7WK{pP+bDA!&aX|I3aPft3iH4|b9$ViBfe zW^6P$S>*oa4Xy4NvYiw2-P)OKmLK%O-^qMomt}$hqm5*<&0EK5-`@Xz@k$ICRvuNg zNwvTJ*=T~k^pk75Y;T)X|8sr1voU|ReSTiKeLe-?*L!{cE)y`x zqRhm+rfG$Pg`9ER{!8xh=|k|+rQc~kuKC^VU%O@{)1P1e$vLy%g00jR{^l8oJN~YM zUVS>=-(!N74i_mqQF1^$2hiuZoZcg}XyvH6H1{Wc$(+BDB+upuSc5E`Wi?L``3iv&tq19#g^^ie`WiWVJS3RKA-dB=OhGv z_gD^RvT&?~mJ>!Cb4s87O~G;1sQUrT&a&_F_I|nzaVF6JgcIyfZBOsauMWJX((zZ?Tba&nE_g}TG#P>VbJkic~G&LV9A3f!N4Q(dVs?)7c zFJm6H;oiTE*{-1>f}C@?I#otXnhqQo*$(rA{vF%*+T}lgyE#)z81khg`-aG83J`6W z^?gw<_ddU8mDIO^ZZ^V$nEd&8{n z*qEIuvuzi4T^y4yzbzR5F1!3wyMC@Q`B=a2`itISy~|x-giVd(IXu7{JeTY=agEmJ z#AhADi0cW{O&u_Ty#AF}`$=ljf7k3l z%WwW^In*zf%-DxU7zn<{^Ts-{;Tm7|ylc`|sJrXoqA~wOT3gEjv>> z`r|L$y50MK5%2kyQ@&)&1Z}BdoSCATAXz&y)Et)jwQ7S|OGpsavREX1GxU0%g?^OC z49VFtfB$AP03PCVtLETs=YY~f`Pyf!HTC-s_HXZz9=G-(G#tKzVO~i6BFr!_X1kW~ z0hM4d2)mx_^gh&);t#U`tT+89?(OmC_@<^8|4-Pyj|FXPjOkitjxN~-Wr3pGYm7*p z$p9xeQr0~+u)i0A{5jLL51&61M&MDrzvB*4J!$1T!|lU8pO{u#au3t&wy`KvglEzo z5pHmt@MATFOV1M~YsQGZ-Js^00;Y_Zp!fT8B9$ei_S`5R^#6aOCn=$>{4t${Gi5UB zL~xed*xE!OD)hpyIN3*JUV{GGPj+tu~)oOOG;-_Bm!X|&JxT%Y@y;jLiGE}jA~mW^=q}^a*Y%)uWNm~ρFwWt@M8jm) z-R}R`;Qvo^-ey|~3tq>ZM&=OIw(JnW??N%i#W*y3RrTB|f{{|7ecOiR-al;30zc{rESIn`zxiJd;ad{qirF!TQ9Xd=r0ts4k5roxaa{N#>?TVS>0DD9I0 zD>@_H2c81nI}w-%vkF^dy3U;m^7h08Pg-Q>?cB9H|K3jp`n)~ad|B{3c@@{TInxt_ z)e{sA=g)e-xgJV5Ra#2n5=U6?HbM2e!_IMqJx8-WSGu$dr{SR$h`)5?~VCrWx{|(xU44PWOp>@zP@F?2Dxv$l{ z0XHpKFKfqf(HOHmH>4)q+ood!oz>)FCkw0?5hCY& zLg`@ASQVs!_V{%S*|Zcty1&eH-8-$eruQevp?Ia+1apv&-m`YOVV~gFa{JywnCThU39r54CXAHD&>V8*P3EX`R87E0=I=md= z@OsNp@syVD3JHJ<_6AzGK zStkUtFv95GqlQ32a$39VF?ZCqLy9ui{*=Gc&ii@b_sNdg34m;CI04W>$d^Pg8}Krd zMoE;(_HZMs5;e5IFLxgV;z5atGBeVcyQv^?x;uj|Z%2V#^dHe_2Yq+WU$cFG z-x#Ri(SRX6H|HMnPq??z1Ma!G#Xk6j~A5Hp9PlmVMZ#uCB|%tiJ*4cedUdf)F7HXtuox z?A`Dh-?iD8;pNcGqP~p#&`EWA%w&Z+X(Zv9JuQcDDI^Ex*n#nM;7h2>p&hj1cJH@u z2zcu6yu)u!Q(D#Ih42V|eA<2c$Y5*A$I>o$E2n!w+=yd=vKMi_dfI0i5r7ZZRRR-H z*n|s=O(601KAF*0ru(F=!Ek_iRlN@J$|D5v@>bKmONh{TKWDnA6G|g{cTKC0G0o;+ zTTfudd;NE`E=Kv0U;mPni>RHShd9FfkbSz@s3{%}f3C<$rRXD?({Z z0iunq@406&Cx77W%J(UBADYjbcWCL(gfTF8dqvOH$H-I>X>gpwKflCn+|{NsTTZ;m z&V1pRS+&}24p$-)PE+!c6F%=g=Hd~x{?VANX1+AE!uH0$*G^nW5L;7?*rZ^2mEEg{2TNn+xT~{p`&79$5Bu^rbB)GaNTu zCly}SpPC7uYd4}>S4d#paqEoT_R+PwJq9?!(l<-9yOiJF6Q|A(O5T7cCj8mvG3UfY zGybpix-S1cm1&dnx-neRLPc%o>wUwsg!h<`xIpCEXo9$-Yze`Q{GRNUsXJ6Z6{*!? zQ1m}@{;23 z$%OTDZ+hcX;zsfd(e`=L;%`k{8CfH*Zn(h7KPnclT|%Ijc*+&B@xKI z1)zzt7|*wMEgi1|XM{4PIu+A$`YV=9!T`m@&X^8D6C=)>YA(+Ov2%{aE$)zQVYc|b z82NB-rUW&WROqdjb=aEKfx@v^4F#dz_xEcny*q#0*TtD87*+Iro%G3V2^b{5W$JIw?F zDOn|u8(MN!sE#L>=cq=`%mb#&4vi*Su1P#}=k&<$E|S~qxbolt*}dniA%;+X)^hA5IFlI?wQ~vEEkcxB{hZ^`j5a+F;+3K6Qn!QK0X9>h> z=1UNPYl|>S?C@^;jfL22a{lbLHT7*|jh2Qr-&Zwm(sDH1A-72}wREpV;H4Ux!gLoi zojvFu5w_9c~B_4$e+R0!FgSGhW@D@-(CFK~HH+8o) zM-J8x6bjnqQOxas77U*uC!Pa|%$wU&V_2y+*md`EZnx(Rvt`c1o5)Oimb=8~cb)7P zA0PG*UZe@Q+8 zE<;Vsgy;Lqp#ny@p4-4QcUZsacvmxV91@$?GjHmX_>#B9y6s0I_= z&Py~W*EF|{I@{Uu4B&M=u?|4^CIV8Rbg^)OL_CJrgQajDBWpEf&hlo=uBy(;Txh-y z(?c=bSjD`X=`pmG*}5e#%xtUirp1H6baGD9nQxlJP$-u`zuDYF+ddu7ZJ>k-HxQMd zDe#ha{lkbrT+?sN-9&nHx2sm-f7%Rz%=>`1DTpLm7!3`LcBx$fZD*P->^dv|Q;nb= zt-FzIyE5&cp6N{~u0i$L-S0999Mfjim*CMqh_}IJ0RGPqbLVE(ew($mNXj?Yb_tb4 znRaMx8-Im(G%1_8Yq7u2&jM5`JscK%a&1MN9K638<1X z%_dWnA~%w>GU1qNHu2GnnfM+S7cLsM5QeT+TSjKeH?#jDgyoD!dx)J)A=gBPcEBj! zV0Y+6|FosAH2sTwOaU!dhTJh)Rs{S;qUpq-@25*s2^Sp4q-lQ4bm@V6;2HLkHmD@V z18%Q6XoyAGU=$2yNBEZYI?3knzTIeAw;`g9#f^{LuI?OLoblCjC}L)4R){hhp1};)OfwkVDorLg z!xcX{3rtv|nS!K>2EXe78mao6nwXi-tHb)W2D+%~QP=*lrN?NPeYlx!1_)k!grk2F z4LGAD0XZ?*qUn%#DgKTOGZVc(X-L3%KI_eNuQf)|BrDs`5C(n$yGc;TR z_ssVEcio@2owO4t0+k8wA=s-6jgW;0G*R{}VfZ^ElzSeccmc)Z+a3gDH^+xD+|Uq$ zq$`2IjQNJe{0`RwB=f--cGhmTYwscG_`QSy2EiesUOv-Ij5mv>wXqFX>Y<}z+HV|6 zEA=>uEm^Kuj9B@2zHjK~)Ys#L3>+?7aWCSE%!3yc&NXhHRDL>$^g>QOx zge9i5u_O-S{Dvl1a)1S<@?Dqrgun9F_4J1uFsgy55{#Enhf_f zFh38n^pg3r((}jt*F#>`u8#CBtc8W~Zp`6D-!X#qT8S@QaNQUjX zCXLcq94Q*G$*>I&Q=noGTdo95tFXsK5WX3lg>f;NXU-8~O;(Y1rjv9fcwad8_mtNf zOt5c_*o^TaO;MRb2c2&k3Hv)V1p3dh)qs5Do@M?ymCtMQ^qnS4WbKQ|YUSrK(fmAi zK|W_|>c&-ADX9XY<>aQd#=Ha!*k+LPGQI0IW9_5~`L_jLx$}CtL?S|J;jb9*B@%OU z8(d3nGMMkX22si(o&FNJZFXlS+Fp|m$PG|9C=MrPTida~vf;L2Fl(N^b9=WBP(Qc- zWMv?l&1|Y<^Xi1zWNQc$Hg&8>&Zo_(8>-QI(ui`s-0Cv#JYakz!w>5haniHHO9U$m z)I(@&h!gD8^fvZj9>b>p9`t$(+Or_>nn))GzX@I%VtsUk?^)WNwAkd;bf3X21Ou`g zOda9q;^}TA>(5<=0FBX{A)_|R4uXcgPX0^?3znB>yVM2T4|UthUJ#uz-j4Y4HnHUx zTjoRe_{;ORqs)s>m8jb_J*4jer}q-OKQy14L> ziUvj{hn<(bRlJRkUD0;efK)0V&912^Q8A>%Ku`BzQ}1XBhKo3pE_1XT+Tc>Hcvebs zmiz1i=OxQAZy%0}3Dm+aL$;7;v!-<|Q7WDJL>GI!$61BpX9je z)vb89sq=c9buej|#URBN)x!31iegX$4WMMRfM)oa=X)@O4y2=%P{igLk-8<%$o2gb z@%F{Jk{K>bL$nV`-e{|zCw539cMfbefJ-FJZqFO0O;=%1W-W`q)u1(OlAk%V(Du-1 zlV_F`831a+Pzb2iWzR6phk&%!XfZ)Kv_^nWG@CW3!SH5og+PU}qkv z1ET}!NSgW+(yEx9KFk(K0>G~Lv}GuX=llFR(^$h8FJ0vOLX*scYV0O&o9B*A@7ohI zto@qa0}xt2_M1Zcd)S2NCOGu9ir86W(;Hf5Lt(IJ7BG>qpE@>%Dm{Ufq(p?YF=tA& zy%}O(%@1Qjn(0GK1|xFS;N0JD!O9L35b9g7aan_dVW(Vkk!I0~Dv*22SjgkM5LhSl zo7%y0raO~T+vL59)44;zVa|~;mr1|+_5S`MHoth^WeY#Hw;*Wt{hDTw%bv{j4%uJUV=Q=nQ!W5)ZU#SUGXtH!w^%@Bwyn#Tu} zD)ZN5a6mTN?!!@Rw&Nh`aBbLOM48#5MhrC5Oh6zmco3tlav%K8U9!U2)Yti*m`-(C zuItQzbh}54!5+{$Oa%x49M0!-b-FZ*-?5!0WNpxB80N zYO1OLtCHTP2u5-%h0#tmE4U3swOwr{0{&SX001BWNklX8cd zrWCQD-B2J?-5As0y55}16Tn7hrpq;)YB}B~VYIiF8_C%cnl7~7Y}duC3qbzUEV;?7 z$OMs{7pl_lbhOWDlp{VcIVpPfFlm;8N-&j*2v-R6SQbNeu;6-TKsX^hs9})PiBdYf z1v8ZVe02xM?|7Hh3$}Bvtmoyw-)+;qD$ku$2vuulHq#J<{d3iK>bPYpPLtCf}pfdK4uJCr!#a8fw0YrqjVJ?!?HR!#OL(B+i3vu_v~$VpmxV?V(%{ zQ#>@$Sz|fHxE!Gbs%0YXTe7nn9aPRSOZ0LsLM69i&oEBh&a1_ct~mi#W}9z3^}zI3 zRlC2tLMC$OP)sN`M)SYb?-Y_V^(O`wTH)3LGH-Eu$D(~d>8pfvgh}O4I9c@IJ!h03 z%8mzxt%bnBm==a>nV?~ZiS|p)RA%9SPfZDv7)Lej1|Y0K^`e7h+B=Pb^-nK%1mN*E z``&sKkyRL}RTO-kIoEK(aAEVM^N_fnDul(HJA_`Mzs>@pHYNkJf#LAC!n8W>({Mih z*5{PlvFvcs9K7f-`5a7!r|bg}_8N2gq-`$o^20IHdVx_TQ{&z4+I?4=3^zC>IGNik z-v%`0qG!M$cIA%2Gpd+T2AFP5RCwCPWJpA8&+cjOq2pO42^oP2O)ySS8oksNo2ui{ z2uNqe29&dB!zI?OLj--84c_5B6J|4GprpM2Vwk18971Y46#f-+UP>bm&H1EZf%DR} zZYajs?t7<03lW%@83U7vyE{jeiOgnmM!Y0hJZrpLDRptAWNl~m@6hLDC2;0k%a!Sl zk|0h&#>5P}WJ4KDcosh(tTIV#n=fU10?Jy4SCzzK$CXHZ@{mC>FIi3~9o5@3KX;Dd z2s6<<(?AH10KLGN_mBzTCVuB*yYF*5?)UQNdj^g%Ak8Uo?x`~%eiL@~H%s^oi+Mbe zEWnJwzc@prdtdVNU(rr?9wnE=KwJ@9566`BYa}6v*D-s*3ue6QBwh_)n9arQ;nkzw z@RAel1&$dLu@S*Bx#j!)QTt-r)}bLJZD zrsX7~>D^}e;!}R(}&qJ47HU6474F8qJe^34}{31pJ(ETmeD`-C$gVjfkX-LlaHF zjufsmUfQqs9lm4&FK2{Qb@Q0^8d6(DA#n@l5iQ;{1fjJcY;rBhgjv8zjMeC=IIO2^ zjfttc#pHnb6KJ>yZbplcABO;Pff=;j)@(KX4#I>pCs;}}a0$p-jWJ!l@1D65<@EjTiud<)CR-pa zk+(N%pzP;ZpOY&@9l0+jW)x47KdV5qf3t!4(e~R>e*C}&X+K1 zHKfzU%YB;guj)aSYq|>vO>j?ceK4)X<0Rw6vQU`eeqArwr=f${hP$uSGc+ccOK@bQ zFrxp&rbB`=uS<6%hSTq5(t?0}C|IOSAwxY82hq=%Zuov2O{R&4EnsKex#oCN5%v&?Lgv?XUSfp!v}JaN-oQd~DBD zk|{lW*c>iAB3f_dRK_e8b3DwN?3&=Lwy3__>|q6?_4FC8{XEsnvQos!l9w|^fPBMv z8hQu@^tVf~$!^FXY`VfscTP5)GG0kA(>})>Qpl1u5B@HIn~&L#L@Z@PfPh;)U{c&k zC7lOyK5u^CPsbi1AyFo%`%H`Fy*AfBJ!98*_ALa4^XNU5ZAvvt?+2l!P7@_MK@!qm zhnZtTCxrGY;V*H6bWOn$TSS;dnH2?b!7&cm`bh5)UQ5+eCK5&Q={VuK<51?4auY=b zrW%4|Mx>T3*CnI)Rhlv|oI_#<>iEl@nGub-#kLo&kASV-nx{5=VzTv}=j)&jX3Vf^IoEy^458C}3}W*$cCpJ7H|t~-<=$tM>trF>INO5S6jjbzHJy#@Mdj@9mAA9pgi&}RGY zGRI00MiXSp$RY4G9#Tf7o zspZ0ye`!<7r`nckreswBVM#O73G*1cm)r4#a9Ur4F*$mgY_e zj8Z76V^J&mSs{_+mA>EaU=N+a+S)CkfxsyY4FXP1n&bg+en9HO#o!WjHt5)0( zjbwdHpA3HF00JUtfGg~m*-7%m%tQgCGMpp7HlOG8P;qZKTC%26n`ecHxgD|pI}HgY zJ4v%J?pZFDYE0;FY2SJuBbbQ$?4OI-?_`c7$(RIkFeir2)*$U`&2rYaM-`#QcHTeU z=l`PP8oFj7=I&Kpzpx9%^2IHxTN8NzLA5}0M~%04m@p`|K#0up8P0g#Km1SES}Ni| zVP=*XVeyJ&?8f&A2W*3$>i8iz^Xxv7(NghSEkg95JQt)_^hG?=Fbg%zluI5+%stFr z(42u1bdpdCier(&d1tyj&Y2KOoi!Rtq9G@+-kcnFNq%P*Jj1@a(*TV3*+~)1H8b7r z-2J8u<}F&7=^5@3?UoImw>!h$`(%Nauu&eGIt(%aT8=UIZ6*k^H!H}fvGc=e(6zr| zS8)!crc=HdjU@l|IyD4v@HMPqBv~0#okB39a+W>DZW8&d(8019#Si4`rEUV_!CsRb zc(Lou`)O-L2-L(tSayw;kT`^?iv{xO2OUb%OHxK}n>>8_b} zBwdW4H_jdqaePgO?xQioLZvPcVJ>upb5ROh8E&KDHgB3cz#RRvEzD0gv{6m#!9yyD z%bgtoO+j>r#I>}&Txj@Y$2>!|eyE15-9DBFDYo70D6mIbLJa6;q~}^+Frn6Yg04f& z+RZiA)#rmnNzJ*!Gupv)SU6K{$xE3XeLVt8Dbs12bd65S)fL(Wtzl+D(CKbWd`3c= zS+PrpBO06iI3`S2R8qJ{irnZ`=F?feG2nNtmcM_hvb$rvQf7Yggky?%b0c4$3|szr zB(3O#otSA(RoZodeNP=Ue=q4fAmKgR0oTPl&X8laoew|0-X-@@Ff#<34R|R&p|nv5 z266X~aNftg)C28|W@+^ZOUQgmfo_KivWqlPookV>lan(=*n`b9Az3j)%k&xXYmFN# zZ0iGXkCP&Vq1g)Yj_9}E)%J7ur8p=KF-4V`EhHf8Lu;H-U~;!GejUp*hr!iDp{8Vv zRl2BK*b7P68jHSK_EBJVz;G|_I@V7gOq&`uMz`QNS6XV7U-nC8?M^q4IZKBTnR%#= zI;T$EvLfTA zY0tJLdr~7=5YSo`K`sABb$o^Wi1@-wzE?w-S~ifZmZmjQV8z>}lDaEhA`#u!Un`#G zs4>L|IrI&g?lc*+EUH2eqn&hud`_7qZa3hZw!`wzz+^%Yz}$AT4#PY>_@|yod;0w? zHrGDw+r8}c;>)#K^P{G7$8rt+_H>;5y!msV6LIYb`#TMXO`$-U7>DZ8L7R?S zpXu*t5(-cBX9LHjS6DS!NscGpkZ6E*!lNp}twz7x9VKJxJ3qd(Nnje8Gi4bEsR&?N zTs2_Vb>bYJ4$CU8mJ)VI1_sju)}+^(IqF1{@Iv=Uw)Qy_cSk13fHwp9(7=PjQ}L?s z!)w@esDDzV=+OZSOw-MJF*~mdBT8PSl-cV78!5rd+&8J$R1=dTjPw2Fm8#55$?fh@ zUoxPUn>+2`)t1vHTCwdMNPVf62E^;4^=izg`FHh=i;t#LYr5&$DoYv_&zZ}{@^0H{ z#8o4~82_XRKWRDr4Y(MZP0yKAbaoOeyL_kLgb9gw<<)&3+_z5pWf%KlQ>SXuwkU~V zCYr^?X^7>b|VivnSKMLVA)hH*CLR%qgV`MHuL$)Y`S`7S)lM z&FGUfA-WjjOTQP@n0HiWEVNna2NqkZBVU!h?cw(@`#V=Fope*}aP>lucROC@?Mu8v?4`JWU z0fl_FG&4dp#TnSDP_g)x1oZn5r+}g^tL|_3^y4I>IZeylC1%xmy;gp#pCuxq0Jn=^Af{14#Q$ z;jvbXuA~!LEqQyW?IrU?IU#y{kxc*LoKh?O#{<-_m-bvAwsKJM)J`%!(lKt@lLevw z9QYpdynRf)pd`(T-rT+#hg@`$*NXp523QhQ4U#3m4c!Mkg!Bg;$ z*)z8eeD2WQ@15Nj+nB#6XgKCwQ^(|`h;!-d$fvtBpYDC_<(}IyP08^4=*@K%nj}Or z(?LKQ)?4P=b12km-6 zI%HZ}wTR5D7fpBC_sf!|yD%UeYdYC@XdJ9-L7}0+J$r`E`bIywhGtuA*i3!X0AW_(fqN~(4siiW?msJ&ej{z?(d>)oYR-KV*GKEH;9ZF-wgx1@~i0jF` z$meFq7m6UQLE_UPVdK)Mi zW8zFF2IsZNZVjvve=|3Y)l2oV>PL=G!#4!M*Gg!gYB^i@VgxiqeHmTD zduYwX5$R!&w%tT|Nx!QlCOmJ+%SH=<`+%dJn$jhj$c_|iyR0PuCrdFqF$of#Pp2#D zTveSZc;9vm9cxPu6X!~=b`NE00^}$6J!okNYXgtfDQ!H(D~d_CZg*O8sA{}Rp3D8H zGY5E{+e>46)$*|gA^#%COq6nLfY2_myJX$KS%`X;?fndyuzK0wv}lMtz~Z^Fwgbwi z$AT)FFkoL7z6(uED)S1vjg0$ekUv>4(&4T&LZ)r1>&NU)zPfKZ)6&k$jW3F%vvo1<2#`?_a88t+J4O@rfSxQ3J9yvsEkuJ zM^P3ak#wrAtEe`eZlf#wR5dY(jQF%dSC^CzxaduDOQRG9!Y9DgX_lM(t5#5-9dW`a zyiVSiFae@yy4+Ti1!(GJP?R+ur#2p!2s^H&$?M(q_w}V7ZZ0&dd)cli(Lik$%DqRv zrR07Z3@tDXh6;_ar{PFRV>jdcNO*RQCr%-aU0`9zV$vFnbX|R(A9$+AbC>}-+-Wc~ zv<&&E4zn(J;@FQRe+nszVJFU^-) z2;JL~lDbBYC(Yp&;7>ILB&z4b|judCNE^>F?#{Hf_w?&IXC!xQD`7 zPaYS-B%80=eA(b`EiJTUL(8-&w^#C7Q zw*sWQVya|z57rl`uq=~7O%uA40T?QicguOPf>x4MDZVE@Rj_t)Wl=*-cQfR^i8br}s|JfzQ>3WcN{Zhh}e=bEXJz5X@1o@x1BJoaIB-J!MVD~Pd#*$&%5 z{;M|GwFlGTzNI80D}jXpwfq(0A2)0XMxkrNlFWNf$%jJJsO0PllZ}px@VLh)eb% zS;<&OA);xE+I39C>^Qq@O>mu$_ocR+gcTIV`f|z%VoC+((h`^kV`_WLKt80=nG3XT z&{Clx;l7+nWo8eaB6M+niLekyUp1O6>V0Njd|g)a@?|#MOBW3pm(@fuyRM;1*hVPl z@4za7Ec3wX`5a|_ssdI@u^G#95v0+FSCAG)YMK?G9XqUG z4(;cixjjQ>cf@oH8p{hMdrp3KwJ#cAW?oO4Xod^TYTA?GcZy67z-l3f5hQX{tRkAT zPe>#_!Upv??D~VWqS@h}bwkDm5d!UzXCI%rzp>pQC9X?z-b(rxZtP*?xn{UDKnT+u z;&x|EW;Pl@i?MpHT1N-v?-+J^l3X*pa3J0Xl%tGNnTANiP$(oT&M}9UfVR);|2L6Hm~PPF%VSBn*Rt~bN+-`DF8t#c_1r;OJm zv~;N>#imBN!s2)85dMSUvj_z;qly!Q0Q&|>&wS2XNH}r2M!EuuDzve|I+q34`M{-s zABjHF(Gow2d&tc55mP=U#>3!^V8R`2QV2Oq9n@=tLY~ftCrThjO4ep*%&NXupVX;V zhAj()`kzvBvE0fN9>bCdIo=dhcUePE* zhX8^uYN_ld#*4PA&9g~l&2UZyBn^@`hLpAn4;xT2Rj4Yo?)?SeD0f2om72T8_DE`| zmzUp@aJ{4?_FH?jh=@T)5td(4tjH!AyifS!t#!E_Us^cVg83@^v|R#?nSWsCOUd-N zmU}g;?0orOu6rrdpqf>BCaX%;)omj)+l%USq4i;jVD3x-k@7t4a*$e*TQRzERZ=v7Az+XtiL3-q;{BS zvsV+w>!Rge^kPWK&$lpwFrV3XT9D}S?^q^U!F0FMkU1T(`S`h}<86hS#XOjT>XYXj z+73k0?q2ojxVZJ1h`KReS86HA&HhL(6NHR=uE%dq0UKU5%$GK$+fACsA!V$Ahj?7I zSpsLp;wmI{L1)H0B+;kI}Jmp<^4moOdIesi;E z(5)kuzKX-uymCd!NAOd!`wm?Ygu@1GZZI>F+2k4$8a6Xr-S>slu?}RY*or&Dv_xXW ziggW!T9c6gZq6texza?yR*k_*EYHg5$^20EQgTLB{fff5MFa8O1T4qsUPF`oS-~)) z3B(kq%1gYKD6w8_K}VU1KI{V+X6T{Vy`j~}yb;g=&UD$;yt=;iK(Z}t?vW;zXUPno zte@$}F;hVh3U@|DPa5`JwA?N>H|;atDE>Q`C7wRq7K`hHc@(Nf5Pz7b?hpxRaVFf& zs#YDM!HQMEzAw0FH>4=dJc<>9l|%i3?!=Gk*JZL_v^=S~_oZ0L@3`m{ZH-TXUc zk1ZSlb0?&rFyQDQuYlt)cPn@+z^Fxv1e6$plsh*=#8I$VkMqmL+r&eVNlJvE#13XiGH zv$%KYAfWlYb(JQjS%<(#fqe`uhdGQ`-$?M>-1NvtDwd&?>>LTvb6<3|B{BO!`hfe& za{&o1_R;z@m=AqeOU#ZJ1~V$67XSbt07*naRFsJZ>MWCai!CZHOTpTTdVNDzC?fTo zVuc2kN1U2dUDmo*f>b$$I(Lurz%#z?fNV1bO zM9?K2$9W9HJM@3ih{_9xuYn`fk~c2$mfA%@1-_{=t+ z2J@Y8<%Y7-kLLN^y=tjOg+E87#JWAX#J53f%m#MPSQU(4ksUEKWB!oBAG0F^ZH$^P zd`*;a&rB3685LjSVP?3KR@k^Qmp-fd03BAhkBQC&{1RGL(daNgM^nXE_cz zjQQa384^-LEKfbQN=JO2RTUGV*EQy5_Ls0U6X~hURHgn&Q@e|A+_a9R41ua4OL;az zMje|mzk?zb4Y;#sU#8m?v*iYorge#JXewIWK0ZRU2Cy*W-SFzyF8*sJ%dGK;D2ER|_>pq&#OwOsS)xwenQgm59;WdrN)MZk;Rqbd`$V&O z^voR_I@wj3O2ag|LQtbk**!;YOhGZl{MuwWFU)4O44#iYN)<~J5208o-r}M zhtQXMtf7Ula0)u3X^4o0Ez94gvjCA4A(NGT#h$KAuWj{O|!NIAtrS-s$H z)sx7}xKZ)zWTv?7D>m~YBa2$1UuWmfeJfA?mZyH3ZF+*2cKGw=06H>4{C=rl&2aPg zQ-N`qFJzd*Qj8E~KG0LOG&jUVYZoU3*@RwkypXP+U`65OTun&nra4OJyAk4MLPTT^ z7c#q4R1?zmFt@E~3Ru%QdPw{04H-$wc*7V?D_wISeOP(F;iguxu~Q_aGOC-)Rl6CY zQSDppcXOnCa%ij`O@)=+YYMV-V@zD@oSP=h%6-=~>!c=T24iQ~>9cS@5S=hTUK;12 z!}IwQh90I#RND>9bL?MM+|&j<&&9+2-~jrPD#x~1X3}mNt1AYl4|kMcNM<`1&4Q4c z+r8R zqy$Jb2+Cmu8PG~I;{ktLtVFwGer9+OA0Q@Z2_5gGw730S5+;I%2T8qShh_dpGC*GA zEi=q&*D<5bfo~-|$-6mLW}7Q}&Fvhw=iu0u;iyESc=k$GLjZ@So~<^ohgq~WK9gYT zn`Jz5JJz_Ze-3j9btQMUb;E}Lvh*m+a%tvu5qoY3VRFE{0BNunR8XbGN9(ZALzlJ1 zxp*z?BTalKDv1gtj)rm(X%m~$CJYrbR5cCIoM!TTo1H-%*upXE27xmqz4`*lpBwfsJ-S12HVHlH2=`JfHGs@ z9ckL1HFYtm62hm(wcZxc*ksAXJ1-1_-|I0UT9M4%k8AZWz}yI{>;cWuPVKX83au$r@ zSSolYnr*tB3xCat^xu{kTx_O!hL&(lh+_(q{W@xDSk!*|yrj93eTB_K2X3;Nft|?^ zgON0h5ZaL-)iC=!5o0D+e9w-Q14+ya<7L}V`5f?X_CmTrO+?Y+`FZg-AdTLmY|h!V z`N^r`9Z>=`j538y(05$tMqKYG%i&mfo-2s4(|lc2|F*bkwHhkJ@?~wR4|fAzc`^$atLPZ%Bx_QK3DiBezsTTq?Q>`)?z^Fd{Q(~Z5d&$Q)g&LvUf5x zPxJh`+R&1WAjrZTH9!=j8zk1*05X)FC|Id#!nq|(lg;5{+o?Hu6ugn$A4mc3XZ`^w z9g2`hgdB#O6e=(}!D+*!a_D>orwz^LFi*C4z%BjEc_WL;q)00H>=c0FDGkgU;18Q& zN`{*bJ8Vs`3_US_O=~00>mIyX_=_xbw1?WOPs%T=$R%48JHYIpFkU>H=`rA2M1EvCWnR8iV`rd#_R@c3e%%1rtv z*{KxJQV*-m$XfNX*(QR1hKBUX81J;4Q{EvOjUnct8r{|yR*!eVy_3=yF>c-c1v-8c zyhL4;yk!YE+g}UrPcgX(YO=FJ>g89B=9`A>mRonYR7jb-P6fmNghqN64*tE58TuV_ zKzs)y^L_n=!I}|^6w?#MxHewKl zYSyau&GO!Wk@!A}h69GdMI@^G@i7;#&3*#6K~ps?k~y$Nv;`L3k!eDrOy3~xWK{GI zWr#z|`bkoCH7zCdrhRMJOWx7#Mk}x+ww5Uc6=po?sF|&olLaH=s#?L^ZfHtE2Bzvx zzLoWdOv1JKgp>0Mt3{;*cE}vi#iZ$W_FQHpXt}KI!a)(5tgnpMy9H5?iCIUnf;6R4 zOx?EzIviBuTv6vIPwP5;2IDTwg8x=nSc8C}sY-H+lmbFEVl`iuYV9LEV_}QBV>Yz6 z&^)1aj#>H~&P7nf7J@2k8#&Um4x9TXh{WpR5V@l(Yp-FbL;&hxlU=(Z2=0e-m`yfx zS`7qZngr|`vj<`F6d5T>C5&3wltN|l+%hy zEDE|rE@-n=saz=m0-I*j$v^6FQclulX+ zIulAhp0ys0l)ch(>AOfk)qO+KrQ|(07xFNhBSYDYxwgcX+tf*CV`n$}-Bpi7W1{Gz z{2O*7S!FIbP~!WlAxWpA7*jtvH}-7?6A;1NUoY7?O?KPUc{@a~o}UHhq}j|C66ePe zka4|EVy{;8wO1>5PG+-bZAYdR4EkxfX=o8F8Lq=^v_daouGV$Zuf*x(+A4G31YyX` z5St;A0J75~@S;0h$kmPW7TL#aJtSlo)0DYb#mTYgs>OOGPCJIL-H0hrVY8&MK;~7* zc7EA;K3yJ|ohLI(udD&pwwrCb9A@w=l@4u+w5_6@QF9xHV{C6vbgS$*&7CW%yU+w(^07I$kz@)yV|0P3!zUetO2#aNuIV*FInfQ#-uZVh-k6ATXRqn5 zobq*e0ise;M<%M4nFo~9sD7^Dp3b}91I+S?Df2bi?%sg|z~vd8WRqey&b+zi8*r2? zO&Bv6x)mD^C|gXcSyFP>StmMoO`TiCprxXrB8!tStP?FN_MsLt)_2wr&XlN` z7!KR=I?B*QDPyLDmK)@i^E7Qgwi@|{-@OW74t$s(GB&t0#kV^R(9%h)B+n(V1)hm0ZMKtX-avuHq@6XvBzgOqpFsWWu)1lKRRnEDS*L@rH>$I7x^=3F6+J(qRp zFZV0(^STxG9ag9a%qM!urmBq|=8WG*(qZpGV9}0dkOV5^O_9l1NR9 zG0GnA6BL#4cOJx@hFggMHXXieOm1dA+1E7d+dW^STXqV(WEKYl?Mu%L$H}{AKWiE# zs=RB}O!NJ^5Jata0(asqGlOU&BgF93uBo3WmL5jGuYDi7-x9}Nd(}zMeD}O{|0+<` z&JAsVG%`JBADhMXc~0IAa>o14e{t<`44eN(vPJ^WvX?v%vD3bE{u(d5GG0PA1G5#X zuAPATArh8q);Gmg$!Tz^pjx*P(-f^9whlX~qr}Y}OhO$p%Sa}Pl_KE-E>K)8?lvq` zKz5l5Bg6-1lZHwZqhzFX4j7O0xn9W%J90-$HruAdCs=8Xng(-dfH9UBVlDo>| zdHZ&ibdJNyVs|r3dM7BJ86&odNRu};6E2u1?l%Yy9^G;>cQ_(qUp!myGehIlI^X}S z0l(kw3>(kaSqCdq2XlfU^)H+Vs-Pe&vg2h(H*8*1*n2iRW4!lCmSneTapg;GM9ywG znk~b8%2XPO;feRN6RO@1XPP7=oRp0r;5~P3txl8LQGBVBkFC(3j@xNGdAJT#hMW8l zq^5_=B($;+D)xi6C1_DCP6s#ZlQ#Ko8x#aX58q{Vl%WtZq-G`5UFnvCCk?H-4t4~8 zS4Ic|h-b|93EAY>5q-4P+Vj@8$L8O7W;m0INr&(H4(H*X`R@}voCU_8XS9bFEl40U z7KADvv9v7D;J_zC;1JxJ7N+e*x6Mpg-S+V^4(9qFvLLAtiIi6mrYkR{39ld834wBZ zijs7Kk+Q#AC}b4N9syk{y@TX!aJ14*O)MesZNw} z+iiEE{4jhA@l3YeKnmFE`5d;&3+kqz3&{%G40)kCiqBWd444yxb>O44pBUg2a52N9 zPj)IG)~9#VeY7BhIfr!VooV0G^K&9`e(q1V@0>bLV9CP<$h%$_$EC$aw75sNemy2g z$)Dq>>AnF|vFSKywu#z;np`Y-npRsy@1-eVqnpExr{A1amN;$#HMJC^BgPU87c@v_ z&&|(|_e%ZceUyX_QD`>zGU|l@A5vCGDWJX}Nv4nS!okGRdLz@{8kPiiH z=6Wv~@~s4|p0i-ID6WH-#uB<&%RXiNM9gr}*y)*1Ot@p>IXIs4ME%L$=LiL4Z99MO zo_ok!%*>$M`MJL`3b2D>NAZ~Jnide4l~DCiG@JE`hut#D%Zwt<#aUQ4KH8gMPEhE&E<=7X1X zwVitE2&w2el+h$Xw_`c5gHX^YI_{)(aYjgTAG(3bvSIVMwD}Nk+i5klaR*mJBj24u z*f7a<-Li*q=ZM(}*`4NnG9#Xj`ER|6xSk@+pw^OPi7-vbYOlPn3A5Gu5{Sv;BCFG~ zCcU&89hY*~$v>Az4z$OMFJrxG;$46Ee}<%#`J}Oi((n>F2goW4AY769M;Xl+b_9*< zNn>U@ot=^l_>G$M0TE60J;Ui=jrBBB=||UDyQ9vPsi0kZs-uBsj4NwclNzMiFFuje zq)K+rX5yXSfviwY1W{EQT@k)8##z^>o2R&t-!HxS%_+6^QOfbht!8mCS{N=EFljqc2ISf>9hfwyG|%2U!s_cL@X9pbETm76dB6@!h%=3=G6 z&I6uReI)eS^(nJNr}!_=UTm^F2+?VGNjmjw`konbhaPsRla*?Dnzkw}XfUE_{?cQFgM{oE!)>@{ zrduYyxmF(2Nx5&Ln{R!IV}P6{9F%SJ+!HY{+(1txo&HaueKc5Iy|`;8i zdqjt4X*#Ah=Yd?AD>L3auygLFYp0yhyl!`<{giD}Tp;f3x0M0Cy*Oq>K9%LTJz{vtI*m$T z&BLHB)kao%a-ibSumwVk7ob%qQIu!|lg)OXtv9dgEqWB~hm$e)&{o66c?gMkrMD5b z-&qnP4J${$J&GD6r>sL}rNXKBN-Xy|W<^VEyIpulhI+Ba=lNH7d8pnMV(EaT7cwm- zFH;W!+`|VFI7?$;49ory&nGj_t9jdz*@2D^hEuy?6rFra6XPlL$zhL7TzMQOZC3Jt zhf%#UMZPwtU)>mv!d?jan2CPP7|G0*nJg3EDv;Li`FB3PwhbHRrKynUHbXoh8(^NE zsS??WI8bz2Xfbwb=-f;!O%?W=m@Q`0dMO5IHe)8e=e~FoDxs}{Lj5EW6faBv`B=#9 zmBD9$Ii`F}-p*nLA%^E3s#?du6PU{~2Wm-fP}4?Y#FtL1hLpiBDggvkB!+XzI}AB2 zDmmXLPr+#|J4Fl%J0phHTj7l`N-beVQ%Q-VVfoR^Escc%@F!0&w4njS@~9fL{A$&dp##o1NLEcTwm$b;&(Dr z+e!ig5?&VYmeS~?lcH72>3mKt7R%Rk0`A;tW-#2k&t8E-c|E-6n149K<-Lo=bt*$y zB)uMnN$cc1i3-M9tC2&ufQ3t2_(cp-WTIXrWQSep+6$6G?Mh#*^~l;VLo>uc7A9Mv zV~HF%2oO#$5@}Y0!GqxMcFJcv>kfz4cU_@v-uGnVJu%yp&GtLC+}!^A`7Q*Krj(;4 z5h?yD!)fOHc z@Ad9|^TF|fu>zvX5wh@&~|s|NGjPhb5+iV z;hnWad=@d4gQ**4xNE+dad(i+QAHliBsjj?c5rF^{=|5nAZ2b}*d*xJ97fUclGq!q zT4eaLz`0*V5%pMtTGoDV8q?*)7_n?sv!$wDE!KpWl~bECVz>LZ9=X2V9AT0ek^3gq zHjOqWAu-mz{M>ij{!D%9AbBwf&m%cQbiAwA=)Y;VqJ&*j;B_S!HF)(IQGM!81S@gn zEoycw@ijZm+lr1m5c8^pg-gPAi%!IIU{k5x+AUM%9!&Q3E=p#{RN4=Bj~?!^YQFVe zcdhxHE|$;PG1X3*Kf%(~o_jn;zV#SXoNACR&cXR3RSQ7kJGrKFNNG&vjD&?5dF^jr zj+hzHj>|F8e0*J%sXtgtjuWPN1yz~H$7LS|g5#>~D&6};|2mPdYW>Cl5jih&&*3=j%Cn#suF7%)K()%1mnjf8Ex;%S5|7Zx5lO{xQfa_i<*- zbAi3Rz$+UNt@ckg(xTOdI#IxIvW&1z=K|C5_e<9Oely?BGptkMDO&$+BDn0{ z7kH5Dy(eL@>z5~ZxYLrqvt?%#woKspcRp?po7RHJA_M1%o!hk*KbjUr&p>D}lXh@C zG#PKLHQ$HtJ7}p4mp)*&Uw^zmZwCAID~|nf2?QOyMtIZJQc2+Furlf0HL$jgE}%%uW&|kZ`PMHb6XZXViN`(s^l&eVfz0V#_bG692A4vaL2B z|BSomwu7K%jh$h#IA&^@G5V$tN}1nt*GKES?zSu#o{m2eN@m2N;#&7F_i70O$~mgO zENo-DMcA4y#5lqvdSLV!Le9VQB<6085x&jJa4X}znCafk)?R-I1HRr2D2(_1-Y={R zuGfwC?_%1nu)q)n#(kpY_G;?a0io;fGAo0p{$96rB#Ze(ej-FqENbW%!-I)T{Ve%i z^d-*?n8xui=2pcE-L+C|N^J2OewPiz{Ir3Y)RE@-y*Ckk>7K*a+#0k=o;J7-VxK6O&mMN4D40vp8!t z$pUm+u5C3PnC^A`LG711Rh0SO(DbFSlOZNEAb%vWnA7)0v-+?MC^cCbZ^9Eyuh9s} z@w(lU1wIKrR}!=`-UFR(kkOfm4hM^g@2%7>#kE?NkTY2M45@DIV$(|EIW^_v@7h1N zab*xnk_L?O^x^2kc^>!uruh%kaw`&8HR5+#ZWjjFZ95pc8~jgD66-@;2B#+BMYBU^ zzP6jC?Ui(GF%M|mfK5SCRDz5ylmzGR;|KKIEG$&VuN7VR?tdX5rgZ_COyQzz`BEsXu z)MYz)i6d#i#%1jkwFG->xx1CIm2V!LU&Iis%s9}Ub4TnP2)hu*#58Ad=E3p2L@_gy ze9m>%o%qcRSvU8;L(5(D=ee)GD}k{a?6t3UP6|~_q1kYb*uJEM)gB-cAI}R>B0I;A zkLd3VT|0$Vqdr5*T=i|%YTcbA^0%oSCV}k@teiBRI8xr9UoY`ovSt%lDFHja$1h!~ zcwC~{sx5XgRaD2om>#p7ctN*K-HHTvY!IG{fQ?V+%`Ui~|^`N?I_Dzdi|Yy|wU zk-1q+&}<_`;XiNMAypTNBZ!L8H(D0YNLq!r>&z1>3`+BX6=6o5DXhCBRs+Zgz4S z(1zi#b<=RUefcl07pCGdX6-~V5mc6$vbEhhypIASd4Fg!GOhM2#oUz9nuG(O`F>p% zCis;!-=)qI(QF#XX4IY|(Qaf*6Larq@;lj-pR-t^G{&-HojLjC^u4J| zwe25N7!ax|VcPs;Q=6{n0_Z;WfcR?EOqnnRJY(B!(BK)w^>&?|Y#MaAe&~zOT4W~r zRU^sg&|?OO#{d8g)=5M`Q~EQ#8htjmDylTv~8i_cG3?>r8RD^a!S^AyZM6nSGKd$`iT zY#MQQ?dU%fbX{Hs)sh|7j{S@V)owb+DnrQ?M$Syf#;Y>hu`nE*M%C71+u3mOyHKvw zm2F`_mXWmhy5*SRWCF{`MSxlPHZfOP2SE4DvAWgi(l#+X&q4IYlFiVLX7F)Ys=DWA zCng)VKDT6mfZ+^AGDG#8UD70m))o&XzWfa`7$0Il= z>m-W6(vD^5$Te)g5NGDBC8mkiJtS6=Q^FUiNiu4SQpT)#Mq;&LSuK-uOx4+RkJG1- zvtI2O60tgi9v1Bhk5E~4arxv1NvCb#Wd5!}>y5ot$HG*6e9vfytPgJ5>$p2roKcNg zYFQa)RGso z?I_TwY_frvark&|&TJfvj^{BWUBY|}C0pwgqv>}rSc<{*%QfF*zj2C}d{@Q`#$+eT zFJ~~}c9#p0j9CN9U>_=hu`h$;)lSP^w7YX+4=eTeVRqYLpN&`Y zuyaUq+-^`Zm`Uxo9dt!IW^ZK>r%#5(!WPn7bJ5wAVSKa@PH7<@r4m*ju`=icA%|sh zfRWmAEJ~V7U67$uUGyw;Gug-88`%@{yhdm` z)@00N#$4Hv@&_CzNgLw(NRAn4r)gMm2w6N%yp*ykaFr@>x^%MIaHr5U=i4^HjpMo! zEmXO`$AztrBt6noc)}za!^}cAyD;8GXH_ixvQ&AO_IvK76G{n9M`gZ6#B<=xHSr?$hEgZKnPWDV%| zBCu;>IKt}KNXjL5P-GaH1I&|V1NnWg_lztE!_ug zQUw}Di!x#=+LP=yUL-Py0A%85a*U{UGyCnOS`YC!Wfo;dcZ~2cNnX}930|<%p*F?az9T^^V6Fzb zUdB29g`>>-ccB{|?=^LP_3(#Hx7(ilrw`kXc_*eyu=6vtB(tC3)JQS3BfO2Zz%!Yk z=3>9)^}&x147YWTc>9BxZg#F-5+1p(+TwFC9g3sMD<|oFZVRPMd#&qm2 z4#8Z*kuq)(JIG>c-M%}6sg}@;eQOVEr{pB*$oiM%gXheW_ChR9Kh2yYuk5*e={vq! zw&9lhq!=GGoI`0_T@^N3HL~K0Vq#gn8JtX`$r#${3_f~Q`?*z}(JxD>zu*6~TArBy zNjB<}Qv~*S9`iA7=ioX$zuanF#{jATrsrd%IwmH2dyuXGq%F<#N89+uUSI*WWYR zC&xu*xE7LKA?k2w1+AopTu6Vt$Dlz-fimZfudHF`nVXI0nMIytbLQ4W^9({M5vS|^pk<=Hj) z;X>gg8Q31?bhBD4QdwLAM7rJ`6pslv$v;lQv<II?U{gG4JQqP-&rMZHJ^Py&cn}(kO57$OV%d`|TsW4x6czE@_NAs81Z9~r+ zUA&sEL5<0G3^y~K(j6Sh-A$1qF*{j{e5mXC#a_#%**nc*WnA&#>f%u$b(VB>Jee>D zG~~8Ws(7}-$Sg2n=Y<(EL(QGCIyIfi4kQB7dlZohP_XdcP+EWMPJ4F-dfNA$6GdL8 z7M~4(XYSnm6Y~+6Eo{WK9eK;0v!8+#Sbj|j&Nc6$5#2#j!k%)51B5h9Mg|$>X9_qE zd}r6UX(mm*y6hg^HK3MBAZN1S&(pRI*hI$Y4qMr->H+0cvl+83LGOu)_}Hb|iM^Bf zEyu>1(5&9h%L3(KaLH6@X^PL7!vgEnpg_~z;aZGsF-_DZ*<0+U_pg@_3~+X%sE{LR z&(dc{RV^f?8;4GRV*8F6RWn$0w6QGq6W!!}LPsWq!N|-?haEprXG91dF3wE!O1%?< z>^OL3tf%wx@rmhRaApmNW2s){lbtv1PXm&fZrxVcm)n?)=H&EG_&x`z_f>@bq6HoS z$_%&JposwOH1ONC+_V)>QmAJf<&4SqLQz%qNOq0+!RZ3R9ic(Th@@n7pae`@?KpTA zeV@hJ@cY$5s|kD@NCiV~e_NyAMxZ0b{AFw^2P(ls>+NShC8 z0M8S-#jXVt@j{@7RhKRrN)iN6tCjNzO*c7KW~B!I*`y6eu|-Ep*nxcXT1D z7}2r9287CAp#eJbX~VH2yNA)aM1%!La)yX^ zc7l1fGcn$pnUC|E+f|YcA_RIR2TJnZwaX=N8O>pDPtV9+&ae|T_}z_BSo$;HUK?iH zeb-oApP=B&7{||XFXtT7Qw=$s!GpP$qCyMPp^w+GQijnfHL@-p#~W*`i^uQALGh~; zUmZw{0?sn2rv%Wu*mr#YH`O0HX0o!Y!VXD{|Fj{oV5aTtX479<`cbv0*mQp55d`i| z0uXj4c$(vP-!asbI7-EnOI@p(bQp`Ko9?lEq#eoAI9+3qR9kBgxYzs12m>CLiLjRj`7!#ujj6wEDR~u_|Fk4GRr|@pL=F*Y`+J^2LuZ-CtgX5@_ zl?w#zJ`Ndgj5Zey0#gZul)yKc!zIB_Ym0{rz+gnK0dLRmT3^Nm!}KnDrcATqhYOWs`Z6#$<;|#81dtuBpS1g}`A+>1`BCt+U!XX6H-QZk}QO z-Xk-~C1`GWvnc;Rw6eJ{BezrRGd015j7baImYRa+5bP%CU6GKkR#hB%@x71gwLlEC zZZ*q};QcqgS9_oyP|cNGt!^j!ISGa?4B_Vt-;sNJ+Lb*_xm|j!`I(>Kq+d6ZmyD9# z*3Bs~X2ya|1Sf{N>&;#p;c0X`Y>bKFLStQOyLEUpFVQsa$sF@Nq+Q({D7~jy3DE|1 z%FNj%5)v5DF6`ci11C8^2C__M#%Y#eqRtbmL+Q+;--THRW+7zeUYM>Io3)^fjtR1} z?faSOg4j_6s(3|c^7t4NTodwYTpP8i`N9b1^=7s&vEg*emefv_FoH6)EP5jWKeGXy q&bo%=7kYzbZ4C|@B + + + + + + +

This is cf web framework!

+ + diff --git a/static/js/jquery.min.js b/static/js/jquery.min.js new file mode 100755 index 00000000..644d35e2 --- /dev/null +++ b/static/js/jquery.min.js @@ -0,0 +1,4 @@ +/*! jQuery v3.2.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.2.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="
",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext;function B(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()}var C=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,D=/^.[^:#\[\.,]*$/;function E(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):D.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(E(this,a||[],!1))},not:function(a){return this.pushStack(E(this,a||[],!0))},is:function(a){return!!E(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var F,G=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,H=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||F,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:G.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),C.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};H.prototype=r.fn,F=r(d);var I=/^(?:parents|prev(?:Until|All))/,J={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function K(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return K(a,"nextSibling")},prev:function(a){return K(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return B(a,"iframe")?a.contentDocument:(B(a,"template")&&(a=a.content||a),r.merge([],a.childNodes))}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(J[a]||r.uniqueSort(e),I.test(a)&&e.reverse()),this.pushStack(e)}});var L=/[^\x20\t\r\n\f]+/g;function M(a){var b={};return r.each(a.match(L)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?M(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=e||a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function N(a){return a}function O(a){throw a}function P(a,b,c,d){var e;try{a&&r.isFunction(e=a.promise)?e.call(a).done(b).fail(c):a&&r.isFunction(e=a.then)?e.call(a,b,c):b.apply(void 0,[a].slice(d))}catch(a){c.apply(void 0,[a])}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==O&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:N,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:N)),c[2][3].add(g(0,a,r.isFunction(d)?d:O))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(P(a,g.done(h(c)).resolve,g.reject,!b),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)P(e[c],h(c),g.reject);return g.promise()}});var Q=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&Q.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var R=r.Deferred();r.fn.ready=function(a){return R.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||R.resolveWith(d,[r]))}}),r.ready.then=R.then;function S(){d.removeEventListener("DOMContentLoaded",S), +a.removeEventListener("load",S),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",S),a.addEventListener("load",S));var T=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)T(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){X.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=W.get(a,b),c&&(!d||Array.isArray(c)?d=W.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return W.get(a,c)||W.access(a,c,{empty:r.Callbacks("once memory").add(function(){W.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,la=/^$|\/(?:java|ecma)script/i,ma={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ma.optgroup=ma.option,ma.tbody=ma.tfoot=ma.colgroup=ma.caption=ma.thead,ma.th=ma.td;function na(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&B(a,b)?r.merge([a],c):c}function oa(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=na(l.appendChild(f),"script"),j&&oa(g),c){k=0;while(f=g[k++])la.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var ra=d.documentElement,sa=/^key/,ta=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ua=/^([^.]*)(?:\.(.+)|)/;function va(){return!0}function wa(){return!1}function xa(){try{return d.activeElement}catch(a){}}function ya(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ya(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=wa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(ra,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(L)||[""],j=b.length;while(j--)h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.hasData(a)&&W.get(a);if(q&&(i=q.events)){b=(b||"").match(L)||[""],j=b.length;while(j--)if(h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&W.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(W.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i\x20\t\r\n\f]*)[^>]*)\/>/gi,Aa=/\s*$/g;function Ea(a,b){return B(a,"table")&&B(11!==b.nodeType?b:b.firstChild,"tr")?r(">tbody",a)[0]||a:a}function Fa(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Ga(a){var b=Ca.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ha(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(W.hasData(a)&&(f=W.access(a),g=W.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c1&&"string"==typeof q&&!o.checkClone&&Ba.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ja(f,b,c,d)});if(m&&(e=qa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(na(e,"script"),Fa),i=h.length;l")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=na(h),f=na(a),d=0,e=f.length;d0&&oa(g,!i&&na(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(U(c)){if(b=c[W.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[W.expando]=void 0}c[X.expando]&&(c[X.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ka(this,a,!0)},remove:function(a){return Ka(this,a)},text:function(a){return T(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.appendChild(a)}})},prepend:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(na(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return T(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!Aa.test(a)&&!ma[(ka.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c1)}});function _a(a,b,c,d,e){return new _a.prototype.init(a,b,c,d,e)}r.Tween=_a,_a.prototype={constructor:_a,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=_a.propHooks[this.prop];return a&&a.get?a.get(this):_a.propHooks._default.get(this)},run:function(a){var b,c=_a.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):_a.propHooks._default.set(this),this}},_a.prototype.init.prototype=_a.prototype,_a.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},_a.propHooks.scrollTop=_a.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=_a.prototype.init,r.fx.step={};var ab,bb,cb=/^(?:toggle|show|hide)$/,db=/queueHooks$/;function eb(){bb&&(d.hidden===!1&&a.requestAnimationFrame?a.requestAnimationFrame(eb):a.setTimeout(eb,r.fx.interval),r.fx.tick())}function fb(){return a.setTimeout(function(){ab=void 0}),ab=r.now()}function gb(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ca[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function hb(a,b,c){for(var d,e=(kb.tweeners[b]||[]).concat(kb.tweeners["*"]),f=0,g=e.length;f1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?lb:void 0)),void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b), +null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&B(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(L);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),lb={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=mb[b]||r.find.attr;mb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=mb[g],mb[g]=e,e=null!=c(a,b,d)?g:null,mb[g]=f),e}});var nb=/^(?:input|select|textarea|button)$/i,ob=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return T(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):nb.test(a.nodeName)||ob.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function pb(a){var b=a.match(L)||[];return b.join(" ")}function qb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,qb(this)))});if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,qb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,qb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(L)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=qb(this),b&&W.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":W.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+pb(qb(c))+" ").indexOf(b)>-1)return!0;return!1}});var rb=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":Array.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(rb,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:pb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(Array.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var sb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!sb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,sb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(W.get(h,"events")||{})[b.type]&&W.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&U(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!U(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=W.access(d,b);e||d.addEventListener(a,c,!0),W.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=W.access(d,b)-1;e?W.access(d,b,e):(d.removeEventListener(a,c,!0),W.remove(d,b))}}});var tb=a.location,ub=r.now(),vb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var wb=/\[\]$/,xb=/\r?\n/g,yb=/^(?:submit|button|image|reset|file)$/i,zb=/^(?:input|select|textarea|keygen)/i;function Ab(a,b,c,d){var e;if(Array.isArray(b))r.each(b,function(b,e){c||wb.test(a)?d(a,e):Ab(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)Ab(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(Array.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)Ab(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&zb.test(this.nodeName)&&!yb.test(a)&&(this.checked||!ja.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:Array.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(xb,"\r\n")}}):{name:b.name,value:c.replace(xb,"\r\n")}}).get()}});var Bb=/%20/g,Cb=/#.*$/,Db=/([?&])_=[^&]*/,Eb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Fb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Gb=/^(?:GET|HEAD)$/,Hb=/^\/\//,Ib={},Jb={},Kb="*/".concat("*"),Lb=d.createElement("a");Lb.href=tb.href;function Mb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(L)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Nb(a,b,c,d){var e={},f=a===Jb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Ob(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Pb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Qb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:tb.href,type:"GET",isLocal:Fb.test(tb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Kb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Ob(Ob(a,r.ajaxSettings),b):Ob(r.ajaxSettings,a)},ajaxPrefilter:Mb(Ib),ajaxTransport:Mb(Jb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Eb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||tb.href)+"").replace(Hb,tb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(L)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Lb.protocol+"//"+Lb.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Nb(Ib,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Gb.test(o.type),f=o.url.replace(Cb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(Bb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(vb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Db,"$1"),n=(vb.test(f)?"&":"?")+"_="+ub++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Kb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Nb(Jb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Pb(o,y,d)),v=Qb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Rb={0:200,1223:204},Sb=r.ajaxSettings.xhr();o.cors=!!Sb&&"withCredentials"in Sb,o.ajax=Sb=!!Sb,r.ajaxTransport(function(b){var c,d;if(o.cors||Sb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Rb[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r(" + + + + +
+
+
+
+
欢迎管理员: + admin!当前时间:2019-05-10 00:00:03 +
+
+
+
+
+
最新一周新增用户
+
+
+ +
+
+
+
+
+
最新一周PV/UV量
+
+
+ +
+
+
+
+
+
用户来源
+
+
+ +
+
+
+
+
+
硬盘使用量
+
+
+ +
+
+
+
+
+ + + + + From b206f1eeaacd803f8a949d732cdc20c1aeab053b Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 15 May 2019 20:22:47 +0800 Subject: [PATCH 038/956] =?UTF-8?q?=E6=96=B0=E5=A2=9Ekeepalive=E7=9A=84tim?= =?UTF-8?q?eout=E5=A4=B4=E9=83=A8,=20=E5=9C=A8=E6=B5=8F=E8=A7=88=E5=99=A8?= =?UTF-8?q?=E8=AE=BF=E9=97=AE=E5=88=B0=E6=97=B6=E5=80=99=E9=80=82=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/init.lua | 2 +- lualib/protocol/http.lua | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua index 0e462539..5a16cc7b 100644 --- a/lualib/httpd/init.lua +++ b/lualib/httpd/init.lua @@ -101,7 +101,7 @@ end -- 连接保持时间 function httpd:timeout(timeout) - if type(timeout) == "number" then + if type(timeout) == "number" and timeout > 0 then self.__timeout = timeout end end diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index 8652f9eb..28f90be4 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -380,7 +380,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) local buffers = {} local ttl = http.ttl local server = http.__server - local timeout = http.__timeout + local timeout = http.__timeout or 30 local cookie = http.__cookie local cookie_secure = http.__cookie_secure local before_func = http._before_func @@ -388,7 +388,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) local max_header_size = http.__max_header_size local max_body_size = http.__max_body_size secCookie(cookie_secure) -- 如果需要 - local sock = tcp:new():set_fd(fd):timeout(timeout or 15) + local sock = tcp:new():set_fd(fd):timeout(timeout) while 1 do local buf = sock:recv(1024) if not buf then @@ -619,6 +619,9 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) end header[#header+1] = static end + if Connection == 'Connection: keep-alive' then + header[#header+1] = "Keep-Alive: timeout="..timeout + end sock:send(concat({concat(header, CRLF), CRLF2, body})) http:tolog(statucode, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, now() - start) if statucode ~= 200 or Connection ~= 'Connection: keep-alive' then From 4e62afbf8cd8ee7e5e2805527375f66fa7eba617 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 20 May 2019 17:38:23 +0800 Subject: [PATCH 039/956] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=B8=80?= =?UTF-8?q?=E4=BA=9B=E4=BB=A3=E7=A0=81,=20=E5=A2=9E=E5=8A=A0fast=5Fopen=20?= =?UTF-8?q?option=E6=B3=A8=E9=87=8A=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index b376dcc7..41cc62b1 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -7,16 +7,24 @@ static inline void SETSOCKETOPT(int sockfd){ + + int Enable = 1; + /* 设置非阻塞 */ non_blocking(sockfd); - int ENABLE = 1; + /* 地址/端口重用 */ + setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &Enable, sizeof(Enable)); + + /* 关闭小包延迟合并算法 */ + setsockopt(sockfd, SOL_SOCKET, TCP_NODELAY, &Enable, sizeof(Enable)); - /* 地址/端口重用 */ - setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &ENABLE, sizeof(ENABLE)); + /* TCP Fast Open*/ + /* + cfadmin Sever一般隐藏在代理层之后. . + setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN, &Enable, sizeof(Enable)); + */ - /* 关闭小包延迟合并算法 */ - setsockopt(sockfd, SOL_SOCKET, TCP_NODELAY, &ENABLE, sizeof(ENABLE)); } @@ -28,6 +36,7 @@ create_server_fd(int port, int backlog){ int sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); if (0 >= sockfd) return -1; + /* socket option set */ SETSOCKETOPT(sockfd); struct sockaddr_in6 SA; @@ -56,6 +65,7 @@ create_client_fd(const char *ipaddr, int port){ int sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); if (0 >= sockfd) return -1; + /* socket option set */ SETSOCKETOPT(sockfd); struct sockaddr_in6 SA; @@ -274,7 +284,7 @@ new_server_fd(lua_State *L){ int fd = create_server_fd(port, 0 >= backlog ? 128 : backlog); if (0 >= fd) return 0; - lua_pushinteger(L, fd); + lua_pushinteger(L, fd); return 1; } @@ -469,19 +479,18 @@ LUAMOD_API int luaopen_tcp(lua_State *L){ luaL_checkversion(L); /* 添加SSL支持 */ - SSL_library_init(); - SSL_load_error_strings(); - // CRYPTO_set_mem_functions(xmalloc, xrealloc, xfree); - // OpenSSL_add_ssl_algorithms(); + SSL_library_init(); + SSL_load_error_strings(); + // CRYPTO_set_mem_functions(xmalloc, xrealloc, xfree); + // OpenSSL_add_ssl_algorithms(); /* 添加SSL支持 */ luaL_newmetatable(L, "__TCP__"); lua_pushstring (L, "__index"); lua_pushvalue(L, -2); lua_rawset(L, -3); - lua_pushliteral(L, "__mode"); - lua_pushliteral(L, "kv"); - lua_rawset(L, -3); - + lua_pushliteral(L, "__mode"); + lua_pushliteral(L, "kv"); + lua_rawset(L, -3); luaL_Reg tcp_libs[] = { {"read", tcp_read}, {"write", tcp_write}, From f4c69e7c93f36915ce7b9b6d2bac5cb4c8ba2697 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 21 May 2019 03:08:10 +0800 Subject: [PATCH 040/956] =?UTF-8?q?=E4=B8=8A=E4=BC=A0admin=E5=BA=93?= =?UTF-8?q?=E4=B8=8E=E6=B5=8B=E8=AF=95=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/README.md | 9 + lualib/admin/config.lua | 12 + lualib/admin/cookie.lua | 34 + lualib/admin/db/README.md | 9 + lualib/admin/db/database.sql | 139 + lualib/admin/db/header.lua | 49 + lualib/admin/db/init.lua | 41 + lualib/admin/db/menu.lua | 68 + lualib/admin/db/permission.lua | 23 + lualib/admin/db/role.lua | 85 + lualib/admin/db/token.lua | 35 + lualib/admin/db/user.lua | 106 + lualib/admin/db/view.lua | 14 + lualib/admin/html/404.html | 29 + lualib/admin/html/dashboard/action.html | 3 + lualib/admin/html/dashboard/aside.html | 88 + lualib/admin/html/dashboard/base.html | 12 + lualib/admin/html/dashboard/content.html | 24 + lualib/admin/html/dashboard/head.html | 24 + lualib/admin/html/dashboard/header.html | 47 + lualib/admin/html/login/action.html | 25 + lualib/admin/html/login/base.html | 8 + lualib/admin/html/login/content.html | 12 + lualib/admin/html/login/head.html | 17 + lualib/admin/html/profile/base.html | 161 + lualib/admin/html/redirect.html | 5 + .../admin/html/system/header/header-add.html | 87 + .../admin/html/system/header/header-edit.html | 92 + lualib/admin/html/system/header/header.html | 82 + lualib/admin/html/system/menu/menu-add.html | 105 + lualib/admin/html/system/menu/menu-edit.html | 109 + lualib/admin/html/system/menu/menu.html | 158 + lualib/admin/html/system/role/role-add.html | 122 + lualib/admin/html/system/role/role-edit.html | 129 + lualib/admin/html/system/role/role.html | 84 + lualib/admin/html/system/user/user-add.html | 130 + lualib/admin/html/system/user/user-edit.html | 139 + lualib/admin/html/system/user/user.html | 85 + lualib/admin/html/view/base.html | 0 lualib/admin/http/dashboard.lua | 152 + lualib/admin/http/login.lua | 65 + lualib/admin/http/profile.lua | 148 + lualib/admin/http/system/header.lua | 182 ++ lualib/admin/http/system/init.lua | 32 + lualib/admin/http/system/menu.lua | 238 ++ lualib/admin/http/system/role.lua | 200 ++ lualib/admin/http/system/user.lua | 206 ++ lualib/admin/init.lua | 119 + lualib/admin/locales/EN-US.lua | 175 + lualib/admin/locales/ZH-CN.lua | 178 ++ lualib/admin/locales/init.lua | 4 + lualib/admin/token.lua | 36 + lualib/admin/utils/init.lua | 82 + lualib/admin/view.lua | 40 + script/test_cfadmin.lua | 81 + static/layui_ext/dist/dtree.js | 1 + static/layui_ext/dtree/dtree.css | 73 + static/layui_ext/dtree/dtree.js | 2808 +++++++++++++++++ static/layui_ext/dtree/font/dtreefont.css | 229 ++ static/layui_ext/dtree/font/dtreefont.eot | Bin 0 -> 19508 bytes static/layui_ext/dtree/font/dtreefont.svg | 80 + static/layui_ext/dtree/font/dtreefont.ttf | Bin 0 -> 19344 bytes static/layui_ext/dtree/font/dtreefont.woff | Bin 0 -> 19420 bytes static/layui_ext/dtree/font/icons.json | 283 ++ ...1\350\200\205\346\227\245\345\277\227.txt" | 141 + 65 files changed, 7954 insertions(+) create mode 100644 lualib/admin/README.md create mode 100644 lualib/admin/config.lua create mode 100644 lualib/admin/cookie.lua create mode 100644 lualib/admin/db/README.md create mode 100644 lualib/admin/db/database.sql create mode 100644 lualib/admin/db/header.lua create mode 100644 lualib/admin/db/init.lua create mode 100644 lualib/admin/db/menu.lua create mode 100644 lualib/admin/db/permission.lua create mode 100644 lualib/admin/db/role.lua create mode 100644 lualib/admin/db/token.lua create mode 100644 lualib/admin/db/user.lua create mode 100644 lualib/admin/db/view.lua create mode 100644 lualib/admin/html/404.html create mode 100644 lualib/admin/html/dashboard/action.html create mode 100644 lualib/admin/html/dashboard/aside.html create mode 100644 lualib/admin/html/dashboard/base.html create mode 100644 lualib/admin/html/dashboard/content.html create mode 100644 lualib/admin/html/dashboard/head.html create mode 100644 lualib/admin/html/dashboard/header.html create mode 100644 lualib/admin/html/login/action.html create mode 100644 lualib/admin/html/login/base.html create mode 100644 lualib/admin/html/login/content.html create mode 100644 lualib/admin/html/login/head.html create mode 100644 lualib/admin/html/profile/base.html create mode 100644 lualib/admin/html/redirect.html create mode 100644 lualib/admin/html/system/header/header-add.html create mode 100644 lualib/admin/html/system/header/header-edit.html create mode 100644 lualib/admin/html/system/header/header.html create mode 100644 lualib/admin/html/system/menu/menu-add.html create mode 100644 lualib/admin/html/system/menu/menu-edit.html create mode 100644 lualib/admin/html/system/menu/menu.html create mode 100644 lualib/admin/html/system/role/role-add.html create mode 100644 lualib/admin/html/system/role/role-edit.html create mode 100644 lualib/admin/html/system/role/role.html create mode 100644 lualib/admin/html/system/user/user-add.html create mode 100644 lualib/admin/html/system/user/user-edit.html create mode 100644 lualib/admin/html/system/user/user.html create mode 100644 lualib/admin/html/view/base.html create mode 100644 lualib/admin/http/dashboard.lua create mode 100644 lualib/admin/http/login.lua create mode 100644 lualib/admin/http/profile.lua create mode 100644 lualib/admin/http/system/header.lua create mode 100644 lualib/admin/http/system/init.lua create mode 100644 lualib/admin/http/system/menu.lua create mode 100644 lualib/admin/http/system/role.lua create mode 100644 lualib/admin/http/system/user.lua create mode 100644 lualib/admin/init.lua create mode 100644 lualib/admin/locales/EN-US.lua create mode 100644 lualib/admin/locales/ZH-CN.lua create mode 100644 lualib/admin/locales/init.lua create mode 100644 lualib/admin/token.lua create mode 100644 lualib/admin/utils/init.lua create mode 100644 lualib/admin/view.lua create mode 100644 script/test_cfadmin.lua create mode 100755 static/layui_ext/dist/dtree.js create mode 100755 static/layui_ext/dtree/dtree.css create mode 100755 static/layui_ext/dtree/dtree.js create mode 100755 static/layui_ext/dtree/font/dtreefont.css create mode 100755 static/layui_ext/dtree/font/dtreefont.eot create mode 100755 static/layui_ext/dtree/font/dtreefont.svg create mode 100755 static/layui_ext/dtree/font/dtreefont.ttf create mode 100755 static/layui_ext/dtree/font/dtreefont.woff create mode 100755 static/layui_ext/dtree/font/icons.json create mode 100755 "static/layui_ext/dtree/\345\274\200\345\217\221\350\200\205\346\227\245\345\277\227.txt" diff --git a/lualib/admin/README.md b/lualib/admin/README.md new file mode 100644 index 00000000..f1a776f1 --- /dev/null +++ b/lualib/admin/README.md @@ -0,0 +1,9 @@ +## admin库 + + admin是cf内置的web后台管理模板库, 目的是为使用者快速构建后台基于web的管理系统. + +#### 1. login 为登录页 + +#### 2. dashboard为后台首页 + +#### 3. view用户自定义页 diff --git a/lualib/admin/config.lua b/lualib/admin/config.lua new file mode 100644 index 00000000..2693313c --- /dev/null +++ b/lualib/admin/config.lua @@ -0,0 +1,12 @@ +local locales = require "admin.locales" +local config = { + cdn = 'http://localhost:8080/', -- 静态文件前缀地址 + -- github = 'https://github.com/candymi/core_framework', -- 跳转地址 + cache = false, -- 是否缓存模板 + locale = "ZH-CN", -- 当前语言 + locales = locales, -- 语言表 + secure = 'cfadmin', -- 生成token的secure + cookie_timeout = 86400 -- Cookie超时时间 +} + +return config diff --git a/lualib/admin/cookie.lua b/lualib/admin/cookie.lua new file mode 100644 index 00000000..02ff32c9 --- /dev/null +++ b/lualib/admin/cookie.lua @@ -0,0 +1,34 @@ +local config = require "admin.config" + +local Cookie = require "httpd.Cookie" +local getCookie = Cookie.getCookie +local setCookie = Cookie.setCookie +local delCookie = Cookie.delCookie + +local os_time = os.time + +local Cookie = {} + +-- 每次Login页需要生成全新的Cookie值. +function Cookie.init () + local session = getCookie('CFTOKEN') + if not session then + return + end + local session = getCookie('CFLANG') + if not session then + setCookie("CFLANG", config.locale) + end + return delCookie("CFTOKEN") +end + +-- 设置Cookie +function Cookie.setCookie (name, value) + return setCookie(name, value, config.cookie_timeout + os_time()) +end + +function Cookie.getCookie (name) + return getCookie(name) +end + +return Cookie diff --git a/lualib/admin/db/README.md b/lualib/admin/db/README.md new file mode 100644 index 00000000..9295f2b8 --- /dev/null +++ b/lualib/admin/db/README.md @@ -0,0 +1,9 @@ +# db + +## view + + dashboard视图的sql + +## user + + user的sql diff --git a/lualib/admin/db/database.sql b/lualib/admin/db/database.sql new file mode 100644 index 00000000..0b1c55d2 --- /dev/null +++ b/lualib/admin/db/database.sql @@ -0,0 +1,139 @@ +# ************************************************************ +# Sequel Pro SQL dump +# Version 4541 +# +# http://www.sequelpro.com/ +# https://github.com/sequelpro/sequelpro +# +# Host: 127.0.0.1 (MySQL 5.7.25) +# Database: cfadmin +# Generation Time: 2019-05-16 15:18:11 +0000 +# ************************************************************ + + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + + +# Dump of table cfadmin_headers +# ------------------------------------------------------------ + +CREATE TABLE `cfadmin_headers` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', + `name` varchar(255) NOT NULL COMMENT '头部名称', + `url` varchar(255) NOT NULL COMMENT '头部URL', + `create_at` int(11) unsigned NOT NULL COMMENT '创建时间', + `update_at` int(10) unsigned NOT NULL COMMENT '修改时间', + `active` tinyint(4) unsigned NOT NULL COMMENT '删除标志', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + + +# Dump of table cfadmin_logs +# ------------------------------------------------------------ + +CREATE TABLE `cfadmin_logs` ( + `id` bigint(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', + `uid` int(11) unsigned NOT NULL COMMENT '用户ID', + `content` text NOT NULL COMMENT '日志信息', + `create_at` int(11) unsigned NOT NULL COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `uid_index` (`uid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + + +# Dump of table cfadmin_menus +# ------------------------------------------------------------ + +CREATE TABLE `cfadmin_menus` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', + `parent` int(11) unsigned NOT NULL, + `name` varchar(255) NOT NULL COMMENT '菜单名称', + `url` varchar(255) DEFAULT NULL COMMENT '菜单链接', + `icon` char(255) DEFAULT NULL COMMENT '菜单图标', + `create_at` int(11) unsigned NOT NULL COMMENT '创建时间', + `update_at` int(11) unsigned NOT NULL COMMENT '更新时间', + `active` tinyint(4) unsigned NOT NULL COMMENT '删除标志', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + + +# Dump of table cfadmin_permissions +# ------------------------------------------------------------ + +CREATE TABLE `cfadmin_permissions` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', + `role_id` int(11) unsigned NOT NULL COMMENT '所属角色', + `menu_id` int(11) unsigned NOT NULL COMMENT '所属菜单', + `create_at` int(11) unsigned NOT NULL COMMENT '创建时间', + `update_at` int(11) unsigned NOT NULL COMMENT '修改时间', + `active` tinyint(4) unsigned NOT NULL COMMENT '是否启用', + PRIMARY KEY (`id`), + KEY `role_index` (`role_id`), + KEY `menu_index` (`menu_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + + +# Dump of table cfadmin_roles +# ------------------------------------------------------------ + +CREATE TABLE `cfadmin_roles` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', + `name` varchar(255) NOT NULL COMMENT '角色名称', + `is_admin` tinyint(4) unsigned NOT NULL COMMENT '管理员标志', + `create_at` int(11) unsigned NOT NULL COMMENT '创建时间', + `update_at` int(1) unsigned NOT NULL COMMENT '修改时间', + `active` tinyint(4) unsigned NOT NULL COMMENT '删除标志', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + + +# Dump of table cfadmin_tokens +# ------------------------------------------------------------ + +CREATE TABLE `cfadmin_tokens` ( + `uid` int(11) unsigned NOT NULL COMMENT '用户ID', + `name` varchar(255) NOT NULL COMMENT '用户名称', + `token` varchar(255) NOT NULL COMMENT '用户TOKEN', + `create_at` int(11) unsigned NOT NULL COMMENT '登录时间', + PRIMARY KEY (`uid`) +) ENGINE=MEMORY DEFAULT CHARSET=utf8; + + + +# Dump of table cfadmin_users +# ------------------------------------------------------------ + +CREATE TABLE `cfadmin_users` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', + `name` varchar(255) NOT NULL COMMENT '用户名', + `username` varchar(255) NOT NULL COMMENT '用户账户', + `password` varchar(255) NOT NULL COMMENT '用户密码', + `email` varchar(255) NOT NULL COMMENT '用户邮箱', + `phone` bigint(11) unsigned NOT NULL COMMENT '用户手机', + `role` int(11) unsigned NOT NULL COMMENT '用户角色', + `create_at` int(11) unsigned NOT NULL COMMENT '创建时间', + `update_at` int(11) unsigned NOT NULL COMMENT '修改时间', + `active` tinyint(4) unsigned NOT NULL COMMENT '删除标志', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + + + +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/lualib/admin/db/header.lua b/lualib/admin/db/header.lua new file mode 100644 index 00000000..131a25d4 --- /dev/null +++ b/lualib/admin/db/header.lua @@ -0,0 +1,49 @@ +local tonumber = tonumber +local os_time = os.time +local fmt = string.format + +local header = {} + +-- header 列表 +function header.header_list (db, opt) + local limit = tonumber(opt.limit) or 10 + local page = tonumber(opt.page) or 1 + return db:query(fmt([[SELECT id, name, url, create_at, update_at FROM cfadmin_headers WHERE active = '1' ORDER BY id LIMIT %s, %s]], limit * (page - 1) , limit)) +end + +-- 获取指定header +function header.get_header (db, id) + local ret, err = db:query(fmt([[ SELECT id, name, url FROM cfadmin_headers WHERE id = '%s' LIMIT 1]], id)) + if #ret == 0 then + return + end + return ret[1] +end + +-- header 总数 +function header.header_count (db) + return db:query([[SELECT count(id) as count FROM cfadmin_headers WHERE active = '1']])[1]['count'] +end + +-- 是否存在此header +function header.header_exists (db, id) + local ret, err = db:query(fmt([[SELECT id FROM cfadmin_headers WHERE id = '%s' AND active = '1' LIMIT 1]], id)) + return ret and #ret > 0 +end + +-- 删除header +function header.header_delete (db, id) + return db:query(fmt([[UPDATE cfadmin_headers SET active = '0', update_at = '%s' WHERE id = '%s' AND active = '1']], os_time(), id)) +end + +-- 增加header +function header.header_add(db, opt) + return db:query(fmt([[INSERT INTO cfadmin_headers(`name`, `url`, `create_at`, `update_at`, `active`) VALUES('%s', '%s', '%s', '%s', 1)]], opt.name, opt.url, os_time(), os_time())) +end + +-- 修改header +function header.header_update (db, opt) + return db:query(fmt([[UPDATE cfadmin_headers SET name = '%s', url = '%s', update_at = '%s' WHERE id = '%s']], opt.name, opt.url, os_time(), opt.id)) +end + +return header diff --git a/lualib/admin/db/init.lua b/lualib/admin/db/init.lua new file mode 100644 index 00000000..525b65cb --- /dev/null +++ b/lualib/admin/db/init.lua @@ -0,0 +1,41 @@ +local crypt = require "crypt" +local config = require "admin.config" + +local fmt = string.format +local os_time = os.time +-- 作为初始化DB工作, 这个函数(must)只能运行一次. +-- 一般情况下, 大家在设计完成后都会手动简历数据表并导入内容. +-- 此文件仅作为作者调试与使用者开发使用, 不对此文件做任何其它保证. +local log = require "logging" +local Log = log:new({path = 'admin-db'}) + +return function () + local ret, err + local db = config.db + local now = os_time() + -- 初始化角色 + ret, err = db:query(fmt([[ + INSERT INTO + cfadmin_roles + (`id`, `name`, `is_admin`, `create_at`, `update_at`, `active`) + VALUES + ('1', '管理员', '1', '%s', '%s', '1') + ]], now, now)) + if not ret then + Log:ERROR(err) + return nil, err + end + -- 初始化用户 + ret, err = db:query(fmt([[ + INSERT INTO + `cfadmin_users` + (`name`, `username`, `password`, `email`, `phone`, `role`, `create_at`, `update_at`, `active`) + VALUES + ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '1')]], + '管理员', 'admin', crypt.hexencode(crypt.sha1('admin')), '869646063@qq.com', '13000000000', '1', now, now)) + if not ret then + Log:ERROR(err) + return nil, err + end + return true, '初始化完成' +end diff --git a/lualib/admin/db/menu.lua b/lualib/admin/db/menu.lua new file mode 100644 index 00000000..7840f7e9 --- /dev/null +++ b/lualib/admin/db/menu.lua @@ -0,0 +1,68 @@ +local toint = math.tointeger +local fmt = string.format +local concat = table.concat +local os_time = os.time + +local menu = {} + +-- 菜单列表 +function menu.menu_list (db, opt) + local limit = toint(opt.limit) or 100 + local page = toint(opt.page) or 1 + return db:query(fmt([[SELECT id, parent, name, url, icon, create_at, update_at FROM cfadmin_menus WHERE active = '1' LIMIT %s, %s]], limit * (page - 1), limit)) +end + +-- 菜单信息 +function menu.menu_info (db, id) + return db:query(fmt([[SELECT id, name, url, icon FROM cfadmin_menus WHERE id = '%s' AND active = '1']], id))[1] +end + +-- 添加菜单菜单 +function menu.menu_add (db, opt) + local now = os_time() + if opt.id > 0 then -- 是否是增加二级菜单 + db:query(fmt([[UPDATE cfadmin_menus SET URL = 'NULL' WHERE id = '%s' AND active = '1']], id)) + end + return db:query(fmt([[INSERT INTO cfadmin_menus(`parent`, `name`, `url`, `icon`, `create_at`, `update_at`, `active`) VALUES('%s', '%s', '%s', '%s', '%s', '%s', '1')]], opt.id, opt.name, opt.url, opt.icon, now, now)) +end + +-- 更新菜单 +function menu.menu_update (db, opt) + local ret, err = db:query(fmt([[SELECT id FROM cfadmin_menus WHERE parent == '%s' AND active = '1']], opt.id)) + return db:query(fmt([[UPDATE cfadmin_menus SET name = '%s', url = '%s', icon = '%s' WHERE id = '%s' AND active = '1']], opt.name, ret and #ret > 0 and "NULL" or opt.url, opt.icon, opt.id)) +end + +-- dtree专用结构 +function menu.menu_tree (db) + local menus, err = db:query([[SELECT id, parent AS parentId, name AS title FROM cfadmin_menus WHERE active = '1']]) + for _, menu in ipairs(menus) do + menu.checkArr = "0" + end + return menus +end + +-- 删除菜单与下属子菜单 +function menu.menu_delete (db, id) + local id_list = {} + local menus, err = db:query(fmt([[SELECT id FROM cfadmin_menus WHERE parent = '%s' AND active = '1']], id)) + if menus and #menus > 0 then + for _, menu in ipairs(menus) do + id_list[#id_list+1] = menu.id + end + local subs, err = db:query(fmt([[SELECT id FROM cfadmin_menus WHERE parent IN (%s) AND active = '1']], concat(id_list, ', '))) + if subs and #subs > 0 then + for _, sub in ipairs(subs) do + id_list[#id_list+1] = sub.id + end + end + end + id_list[#id_list+1] = id + local now = os_time() + local list = concat(id_list, ', ') + -- 删除menu + db:query(fmt([[UPDATE cfadmin_menus SET active = '0', update_at = '%s' WHERE id IN (%s) AND active = '1']], now, list)) + -- 删除role关联permissions + db:query(fmt([[UPDATE cfadmin_permissions SET active = '0', update_at = '%s' WHERE menu_id IN (%s) AND active = '1']], now, list)) +end + +return menu diff --git a/lualib/admin/db/permission.lua b/lualib/admin/db/permission.lua new file mode 100644 index 00000000..389afe68 --- /dev/null +++ b/lualib/admin/db/permission.lua @@ -0,0 +1,23 @@ +local fmt = string.format + +local permission = {} + +-- 用户是否有此菜单的权限 +function permission.user_have_menu_permission (db, uid, url) + local ret, err = db:query(fmt([[ + SELECT + count(`cfadmin_menus`.`id`) AS count + FROM + cfadmin_users, cfadmin_menus, cfadmin_permissions + WHERE + (`cfadmin_users`.id = '%s' AND `cfadmin_users`.active = '1') AND + (`cfadmin_menus`.active = '1' AND `cfadmin_menus`.url = '%s') AND `cfadmin_permissions`.active = '1' AND + `cfadmin_users`.role = `cfadmin_permissions`.role_id AND `cfadmin_permissions`.menu_id = `cfadmin_menus`.id]], + uid, url)) + if ret and ret[1] and ret[1]['count'] == 1 then + return true + end + return false +end + +return permission diff --git a/lualib/admin/db/role.lua b/lualib/admin/db/role.lua new file mode 100644 index 00000000..d71de0ab --- /dev/null +++ b/lualib/admin/db/role.lua @@ -0,0 +1,85 @@ +local tonumber = tonumber +local tostring = tostring +local concat = table.concat +local os_time = os.time +local os_date = os.date +local fmt = string.format + +local role = {} + +-- 角色列表 +function role.role_list (db, opt) + local limit = tonumber(opt.limit) or 10 + local page = tonumber(opt.page) or 1 + local roles, err = db:query(fmt([[SELECT id, name, create_at, update_at FROM cfadmin_roles WHERE active = '1' ORDER BY id LIMIT %s, %s]], limit * (page - 1) , limit)) + if not roles then + return + end + for _, role in ipairs(roles) do + role.create_at = os_date("%Y-%m-%d %H:%M:%S", role.create_at) + role.update_at = os_date("%Y-%m-%d %H:%M:%S", role.update_at) + end + return roles +end + +-- 角色对应权限 +function role.role_permissions (db, id) + return db:query(fmt([[SELECT role_id, menu_id FROM cfadmin_permissions WHERE role_id = '%s' AND active = '1']], id)) +end + +-- 角色名已存在 +function role.role_name_exists (db, name) + local ret, err = db:query(fmt([[SELECT id, name FROM cfadmin_roles WHERE name = '%s' AND active = '1' LIMIT 1]], name)) + if ret and #ret > 0 then + return ret[1] + end + return false +end + +-- 角色id已存在 +function role.role_id_exists (db, id) + local ret, err = db:query(fmt([[SELECT id, name FROM cfadmin_roles WHERE id = '%s' AND active = '1' LIMIT 1]], id)) + if ret and #ret > 0 then + return ret[1] + end + return false +end + +-- 添加角色 +function role.role_add (db, opt) + local now = os_time() + db:query(fmt([[INSERT INTO cfadmin_roles(`name`, `is_admin`, `create_at`, `update_at`, `active`) VALUES('%s', '0', '%s', '%s', '1')]], opt.name, now, now)) + if opt.permissions then + local id = db:query(fmt([[SELECT id FROM cfadmin_roles WHERE name = '%s' AND active = '1' LIMIT 1]], opt.name))[1]['id'] + local tab = {} + local SQL = [[INSERT INTO cfadmin_permissions(`role_id`, `menu_id`, `create_at`, `update_at`, `active`) VALUES]] + for _, permission in ipairs(opt.permissions) do + tab[#tab+1] = '('..concat({id, permission.menu_id, now, now, 1}, ', ')..')' + end + db:query(SQL..concat(tab, ', ')) + end +end + +-- 删除角色关联数据 +function role.role_delete (db, id) + local now = os_time() + -- 删除角色 + db:query(fmt([[UPDATE cfadmin_roles SET active = '0', update_at = '%s' WHERE id = '%s' AND active = '1']], now, id)) + -- 删除角色对应的权限 + db:query(fmt([[UPDATE cfadmin_permissions SET active = '0', update_at = '%s' WHERE role_id = '%s' AND active = '1']], now, id)) +end + +-- 更新role相关数据 +function role.role_update(db, opt) + local now = os_time() + db:query(fmt([[UPDATE cfadmin_roles SET name = '%s', update_at = '%s' where id = '%s' AND active = '1']], opt.name, now, opt.id)) + db:query(fmt([[UPDATE cfadmin_permissions SET active = '0', update_at = '%s' WHERE role_id = '%s']], now, opt.id)) + local tab = {} + local SQL = [[INSERT INTO cfadmin_permissions(`role_id`, `menu_id`, `create_at`, `update_at`, `active`) VALUES]] + for _, permission in ipairs(opt.permissions) do + tab[#tab+1] = '('..concat({opt.id, permission.menu_id, now, now, 1}, ', ')..')' + end + db:query(SQL..concat(tab, ', ')) +end + +return role diff --git a/lualib/admin/db/token.lua b/lualib/admin/db/token.lua new file mode 100644 index 00000000..51b3264f --- /dev/null +++ b/lualib/admin/db/token.lua @@ -0,0 +1,35 @@ + +local fmt = string.format +local os_time = os.time + +local token = {} + +-- 写入Token +function token.token_add (db, uid, name, token) + return db:query(fmt( + [[ + INSERT INTO + `cfadmin_tokens`(`uid`, `name`, `token`, `create_at`) + VALUES + ('%s', '%s', '%s', '%s') + ON DUPLICATE KEY UPDATE `token` = '%s', `name` = '%s', `create_at` = '%s' + ]], + uid, name, token, os_time(), -- VALUES + token, name, os_time())) -- ON DUPLICATE VALUES +end + +-- 删除Token +function token.token_delete (db, id) + return db:query(fmt([[DELETE FROM cfadmin_tokens WHERE uid = '%s' LIMIT 1]], id)) +end + +-- Token 是否存在 +function token.token_exists (db, token) + local ret, err = db:query(fmt([[SELECT uid, name, token FROM cfadmin_tokens WHERE token = '%s']], token)) + if not ret or #ret == 0 then + return + end + return ret[1] +end + +return token diff --git a/lualib/admin/db/user.lua b/lualib/admin/db/user.lua new file mode 100644 index 00000000..9e4c92fa --- /dev/null +++ b/lualib/admin/db/user.lua @@ -0,0 +1,106 @@ +local toint = math.tointeger +local tostring = tostring +local os_time = os.time +local fmt = string.format + +local json = require "json" +local json_encode = json.encode +local json_decode = json.decode + +local user = {} + +-- 用户列表 +function user.user_list (db, opt) + local limit = toint(opt.limit) or 10 + local page = toint(opt.page) or 1 + return db:query(fmt([[ + SELECT + `cfadmin_users`.id, + `cfadmin_users`.name, + `cfadmin_users`.username, + `cfadmin_roles`.name as role_name, + `cfadmin_users`.email, + `cfadmin_users`.phone, + `cfadmin_users`.create_at, + `cfadmin_users`.update_at, + `cfadmin_users`.active + FROM cfadmin_users, cfadmin_roles + WHERE + `cfadmin_roles`.id = `cfadmin_users`.role AND `cfadmin_users`.active = 1 + LIMIT %s, %s + ]], limit * (page - 1), limit)) +end + +-- 用户总数 +function user.user_count (db) + return db:query([[SELECT count(id) as count FROM cfadmin_users WHERE active = '1']])[1]['count'] +end + +-- 用户是否存在 +function user.user_exists (db, username, uid) + return db:query(fmt([[ + SELECT + `cfadmin_users`.id, + `cfadmin_users`.name, + `cfadmin_users`.username, + `cfadmin_users`.password + FROM cfadmin_users + WHERE + `cfadmin_users`.active = '1' AND `cfadmin_users`.username = '%s' OR `cfadmin_users`.id = '%s' + LIMIT 1]], + tostring(username), toint(uid)))[1] +end + +-- 用户信息 +function user.user_info (db, uid) + local ret, err = db:query(fmt([[ + SELECT + `cfadmin_users`.id, + `cfadmin_users`.name, + `cfadmin_users`.username, + `cfadmin_users`.password, + `cfadmin_roles`.name as role_name, + `cfadmin_roles`.is_admin, + `cfadmin_users`.role, + `cfadmin_users`.phone, + `cfadmin_users`.email + FROM + cfadmin_users, cfadmin_roles + WHERE + `cfadmin_users`.role = `cfadmin_roles`.id AND `cfadmin_users`.id = '%s' + LIMIT 1]], uid)) + return ret[1] +end + +-- 添加用户 +function user.user_add (db, opt) + local now = os_time() + return db:query(fmt([[ + INSERT INTO cfadmin_users(`name`, `username`, `password`, `role`, `email`, `phone`, `create_at`, `update_at`, `active`) + VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '1') + ]], opt.name, opt.username, opt.password, opt.role, opt.email, opt.phone, now, now)) +end + +-- 删除用户 +function user.user_delete (db, uid) + return db:query(fmt([[UPDATE cfadmin_users SET active = '0', update_at = '%s' WHERE id = '%s']], os_time(), uid)) +end + +function user.user_update (db, opt) + return db:query(fmt([[ + UPDATE cfadmin_users + SET name = '%s', username = '%s', password = '%s', role = '%s', email = '%s', phone = '%s', update_at = '%s' WHERE id = '%s' + ]], opt.name, opt.username, opt.password, opt.role, opt.email, opt.phone, os_time(), opt.id)) +end + +-- 更新用户密码 +function user.user_update_password (db, opt) + return db:query(fmt([[UPDATE cfadmin_users SET password = '%s' WHERE id = '%s' AND active = '1']], opt.password, opt.id)) +end + +-- 更新用户信息 +function user.user_update_info (db, opt) + return db:query(fmt([[UPDATE cfadmin_users SET name = '%s', phone = '%s', email = '%s' WHERE id = '%s' AND active = '1']], opt.name, opt.phone, opt.email, opt.id)) +end + +return user diff --git a/lualib/admin/db/view.lua b/lualib/admin/db/view.lua new file mode 100644 index 00000000..5568f219 --- /dev/null +++ b/lualib/admin/db/view.lua @@ -0,0 +1,14 @@ +local fmt = string.format +local view = {} + +-- 获取顶部栏 +function view.get_headers (db) + return db:query([[ SELECT id, name, url FROM cfadmin_headers WHERE active = '1' ORDER BY id LIMIT 8 ]]) +end + +-- 获取菜单栏 +function view.get_menus (db, permission) + return db:query([[SELECT id, parent, name, url, icon, create_at, update_at FROM cfadmin_menus WHERE active = '1']]) +end + +return view diff --git a/lualib/admin/html/404.html b/lualib/admin/html/404.html new file mode 100644 index 00000000..60bca51f --- /dev/null +++ b/lualib/admin/html/404.html @@ -0,0 +1,29 @@ + + + + + {* locale['error.404.title'] *} + + + + + + + + +
+
+
+

+

{* locale['error.404.message'] *}

+

3秒后将自动跳转

+
+
+
+ + + diff --git a/lualib/admin/html/dashboard/action.html b/lualib/admin/html/dashboard/action.html new file mode 100644 index 00000000..d5614d30 --- /dev/null +++ b/lualib/admin/html/dashboard/action.html @@ -0,0 +1,3 @@ + diff --git a/lualib/admin/html/dashboard/aside.html b/lualib/admin/html/dashboard/aside.html new file mode 100644 index 00000000..95127b95 --- /dev/null +++ b/lualib/admin/html/dashboard/aside.html @@ -0,0 +1,88 @@ + +
+
+ +
+
diff --git a/lualib/admin/html/dashboard/base.html b/lualib/admin/html/dashboard/base.html new file mode 100644 index 00000000..3add00c9 --- /dev/null +++ b/lualib/admin/html/dashboard/base.html @@ -0,0 +1,12 @@ + + + + {(lualib/admin/html/dashboard/head.html)} + + + {(lualib/admin/html/dashboard/header.html)} + {(lualib/admin/html/dashboard/aside.html)} + {(lualib/admin/html/dashboard/content.html)} + {(lualib/admin/html/dashboard/action.html)} + + diff --git a/lualib/admin/html/dashboard/content.html b/lualib/admin/html/dashboard/content.html new file mode 100644 index 00000000..c97a937a --- /dev/null +++ b/lualib/admin/html/dashboard/content.html @@ -0,0 +1,24 @@ +
+
+
    +
  • + {{ locale['dashboard.crumbs.home'] }} +
  • +
+
+
+
{{ locale['dashboard.crumbs.close_this'] }}
+
{{ locale['dashboard.crumbs.close_other'] }}
+
{{ locale['dashboard.crumbs.close_all'] }}
+
+
+
+
+ +
+
+
+
+
+
+ diff --git a/lualib/admin/html/dashboard/head.html b/lualib/admin/html/dashboard/head.html new file mode 100644 index 00000000..22ece642 --- /dev/null +++ b/lualib/admin/html/dashboard/head.html @@ -0,0 +1,24 @@ + +{* locale['dashboard.header.title' ] *} + + + + + + + + + + + + + + + + + diff --git a/lualib/admin/html/dashboard/header.html b/lualib/admin/html/dashboard/header.html new file mode 100644 index 00000000..a32bf19c --- /dev/null +++ b/lualib/admin/html/dashboard/header.html @@ -0,0 +1,47 @@ + + + diff --git a/lualib/admin/html/login/action.html b/lualib/admin/html/login/action.html new file mode 100644 index 00000000..5aa0f290 --- /dev/null +++ b/lualib/admin/html/login/action.html @@ -0,0 +1,25 @@ + diff --git a/lualib/admin/html/login/base.html b/lualib/admin/html/login/base.html new file mode 100644 index 00000000..7d827dae --- /dev/null +++ b/lualib/admin/html/login/base.html @@ -0,0 +1,8 @@ + + + {(lualib/admin/html/login/head.html)} + + {(lualib/admin/html/login/content.html)} + {(lualib/admin/html/login//action.html)} + + diff --git a/lualib/admin/html/login/content.html b/lualib/admin/html/login/content.html new file mode 100644 index 00000000..22c418bd --- /dev/null +++ b/lualib/admin/html/login/content.html @@ -0,0 +1,12 @@ + diff --git a/lualib/admin/html/login/head.html b/lualib/admin/html/login/head.html new file mode 100644 index 00000000..9bc7ce66 --- /dev/null +++ b/lualib/admin/html/login/head.html @@ -0,0 +1,17 @@ + + + {{ locale['login.form.title'] }} + + + + + + + + + + + diff --git a/lualib/admin/html/profile/base.html b/lualib/admin/html/profile/base.html new file mode 100644 index 00000000..6e778f44 --- /dev/null +++ b/lualib/admin/html/profile/base.html @@ -0,0 +1,161 @@ + + + + + + profile + + + + + + + + + + + +
+
+
{*locale['dashboard.header.profile.update_userinfo']*}
+
+
+
+ +
+ +
+
+ *{*locale['dashboard.header.profile.form.name.notice']*} +
+
+
+ +
+ +
+
+ *{*locale['dashboard.header.profile.form.username.notice']*} +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+ + +
+
+
+
+
+
{*locale['dashboard.header.profile.update_password']*}
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+ + +
+
+
+
+ + + + diff --git a/lualib/admin/html/redirect.html b/lualib/admin/html/redirect.html new file mode 100644 index 00000000..c38b38a3 --- /dev/null +++ b/lualib/admin/html/redirect.html @@ -0,0 +1,5 @@ + + + + + diff --git a/lualib/admin/html/system/header/header-add.html b/lualib/admin/html/system/header/header-add.html new file mode 100644 index 00000000..ca1a3ec9 --- /dev/null +++ b/lualib/admin/html/system/header/header-add.html @@ -0,0 +1,87 @@ + + + + + + header-add + + + + + + + + + + + +
+
+
+
+ +
+ +
+
+ *{*locale['dashboard.menu.header_manage.form.header_add.name.notice']*} +
+
+
+ +
+ +
+
+ *{*locale['dashboard.menu.header_manage.form.header_add.url.notice']*} +
+
+
+
+ +
+
+
+
+ +
+
+
+ + +
+
+
+
+ + + + diff --git a/lualib/admin/html/system/header/header-edit.html b/lualib/admin/html/system/header/header-edit.html new file mode 100644 index 00000000..8ac229a7 --- /dev/null +++ b/lualib/admin/html/system/header/header-edit.html @@ -0,0 +1,92 @@ + + + + + + header-edit + + + + + + + + + + + +
+
+
+
+ +
+ +
+
+ *{*locale['dashboard.menu.header_manage.form.header_edit.name.notice']*} +
+
+
+ +
+ +
+
+ *{*locale['dashboard.menu.header_manage.form.header_edit.url.notice']*} +
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+ + +
+
+
+
+ + + + diff --git a/lualib/admin/html/system/header/header.html b/lualib/admin/html/system/header/header.html new file mode 100644 index 00000000..e748231c --- /dev/null +++ b/lualib/admin/html/system/header/header.html @@ -0,0 +1,82 @@ + + + + + {* locale['dashboard.header_manage.title'] *} + + + + + + + + + + + + + + + + + + + + + + +
#{* locale['dashboard.menu.header_manage.table.name'] *}{* locale['dashboard.menu.header_manage.table.url'] *}{* locale['dashboard.menu.header_manage.table.create_time'] *}{* locale['dashboard.menu.header_manage.table.update_time'] *}{* locale['dashboard.menu.header_manage.table.options'] *}
+ + + + + diff --git a/lualib/admin/html/system/menu/menu-add.html b/lualib/admin/html/system/menu/menu-add.html new file mode 100644 index 00000000..4e08fa52 --- /dev/null +++ b/lualib/admin/html/system/menu/menu-add.html @@ -0,0 +1,105 @@ + + + + + + user-add + + + + + + + + + + + +
+
+
+
+ +
+ +
+
+ *{*locale['dashboard.menu.menu_manage.menu_add.form.name.notice']*} +
+
+
+ +
+ +
+
+ *{*locale['dashboard.menu.menu_manage.menu_add.form.url.notice']*} +
+
+
+ +
+ +
+
+ *{*locale['dashboard.menu.menu_manage.menu_add.form.icon.notice']*} +
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+ + +
+
+
+
+ + + + diff --git a/lualib/admin/html/system/menu/menu-edit.html b/lualib/admin/html/system/menu/menu-edit.html new file mode 100644 index 00000000..df50e536 --- /dev/null +++ b/lualib/admin/html/system/menu/menu-edit.html @@ -0,0 +1,109 @@ + + + + + + user-add + + + + + + + + + + + +
+
+
+
+ +
+ +
+
+ *{*locale['dashboard.menu.menu_manage.menu_edit.form.name.notice']*} +
+
+
+ +
+ +
+
+ *{*locale['dashboard.menu.menu_manage.menu_edit.form.url.notice']*} +
+
+
+ +
+ +
+
+ *{*locale['dashboard.menu.menu_manage.menu_edit.form.icon.notice']*} +
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+ + +
+
+
+
+ + + + diff --git a/lualib/admin/html/system/menu/menu.html b/lualib/admin/html/system/menu/menu.html new file mode 100644 index 00000000..228751af --- /dev/null +++ b/lualib/admin/html/system/menu/menu.html @@ -0,0 +1,158 @@ + + + + + + menu + + + + + + + + + + + + + +
+
+ + +
+
+ + + + + + + + + + + {% if menus and type(menus) == 'table' then%} + {% for index, menu in ipairs(menus) do %} + + + + + + + + {% if type(menu.list) == 'table' then%} + {% for index, sub in ipairs(menu.list) do%} + + + + + + + + {% if type(sub.list) == 'table' then %} + {% for index, it in ipairs(sub.list) do %} + + + + + + + + {% end %} + {% end%} + {% end %} + {% end %} + {% end %} + {% end %} + +
{*locale['dashboard.menu.menu_manage.table.id']*}{*locale['dashboard.menu.menu_manage.table.icon']*}{*locale['dashboard.menu.menu_manage.table.name']*}{*locale['dashboard.menu.menu_manage.table.url']*}{*locale['dashboard.menu.menu_manage.table.options']*}
{*menu.id*}{*menu.icon*} + {% if type(menu.list) == 'table' then %} + + {% end %}{{locale[menu.name] or menu.name}} + {{ menu.url or '-' }} + + + +
{*sub.id*}{*sub.icon*} + |-------> + {% if type(sub.list) == 'table' then %} + + {% end %} + {{locale[sub.name] or sub.name}} + {{ sub.url or '-' }} + + + +
{*it.id*}{*it.icon*} +         +         + |-------> {{locale[it.name] or it.name}} + {*it.url*} + + +
+
+
+
+ + + diff --git a/lualib/admin/html/system/role/role-add.html b/lualib/admin/html/system/role/role-add.html new file mode 100644 index 00000000..35367da0 --- /dev/null +++ b/lualib/admin/html/system/role/role-add.html @@ -0,0 +1,122 @@ + + + + + + role-add + + + + + + + + + + + + + +
+
+
+
+ +
+ +
+
+ *用户的角色名 +
+
+
+ +
+
    +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    + + +
    +
    +
    +
    + + + + diff --git a/lualib/admin/html/system/role/role-edit.html b/lualib/admin/html/system/role/role-edit.html new file mode 100644 index 00000000..bd6cef91 --- /dev/null +++ b/lualib/admin/html/system/role/role-edit.html @@ -0,0 +1,129 @@ + + + + + + role-edit + + + + + + + + + + + + + +
    +
    +
    +
    + +
    + +
    +
    + *用户的角色名 +
    +
    +
    + +
    +
      +
      +
      +
      +
      + +
      +
      +
      +
      + +
      +
      +
      + + +
      +
      +
      +
      + + + + diff --git a/lualib/admin/html/system/role/role.html b/lualib/admin/html/system/role/role.html new file mode 100644 index 00000000..45c9d066 --- /dev/null +++ b/lualib/admin/html/system/role/role.html @@ -0,0 +1,84 @@ + + + + + role + + + + + + + + + + + + + + + + + + + + + +
      #{* locale['dashboard.menu.role_manage.table.name'] *}{* locale['dashboard.menu.role_manage.table.create_time'] *}{* locale['dashboard.menu.role_manage.table.update_time'] *}{* locale['dashboard.menu.role_manage.table.options'] *}
      + + + + + diff --git a/lualib/admin/html/system/user/user-add.html b/lualib/admin/html/system/user/user-add.html new file mode 100644 index 00000000..a7bda97e --- /dev/null +++ b/lualib/admin/html/system/user/user-add.html @@ -0,0 +1,130 @@ + + + + + + user-add + + + + + + + + + + + +
      +
      +
      +
      + +
      + +
      +
      + *{*locale['dashboard.menu.user_manage.form.user_add.name.notice']*} +
      +
      +
      + +
      + +
      +
      + *{*locale['dashboard.menu.user_manage.form.user_add.username.notice']*} +
      +
      +
      + +
      + +
      +
      + *{*locale['dashboard.menu.user_manage.form.user_add.password.notice']*} +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +
      + +
      +
      +
      +
      + +
      +
      +
      + + +
      +
      +
      +
      + + + + diff --git a/lualib/admin/html/system/user/user-edit.html b/lualib/admin/html/system/user/user-edit.html new file mode 100644 index 00000000..e424e772 --- /dev/null +++ b/lualib/admin/html/system/user/user-edit.html @@ -0,0 +1,139 @@ + + + + + + user-add + + + + + + + + + + + +
      +
      +
      +
      + +
      + +
      +
      + *{*locale['dashboard.menu.user_manage.form.user_edit.name.notice']*} +
      +
      +
      + +
      + +
      +
      + *{*locale['dashboard.menu.user_manage.form.user_edit.username.notice']*} +
      +
      +
      + +
      + +
      +
      + *{*locale['dashboard.menu.user_manage.form.user_edit.password.notice']*} +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +
      + +
      +
      +
      +
      + +
      +
      +
      +
      + +
      +
      +
      + + +
      +
      +
      +
      + + + + diff --git a/lualib/admin/html/system/user/user.html b/lualib/admin/html/system/user/user.html new file mode 100644 index 00000000..a59d631d --- /dev/null +++ b/lualib/admin/html/system/user/user.html @@ -0,0 +1,85 @@ + + + + + {* locale['dashboard.menu.user_manage.title'] *} + + + + + + + + + + + + + + + + + + + + + + + + + +
      #{* locale['dashboard.menu.user_manage.table.name'] *}{* locale['dashboard.menu.user_manage.table.username'] *}{* locale['dashboard.menu.user_manage.table.permission_name'] *}{* locale['dashboard.menu.user_manage.table.email'] *}{* locale['dashboard.menu.user_manage.table.phone'] *}{* locale['dashboard.menu.user_manage.table.create_time'] *}{* locale['dashboard.menu.user_manage.table.update_time'] *}{* locale['dashboard.menu.user_manage.table.options'] *}
      + + + + + diff --git a/lualib/admin/html/view/base.html b/lualib/admin/html/view/base.html new file mode 100644 index 00000000..e69de29b diff --git a/lualib/admin/http/dashboard.lua b/lualib/admin/http/dashboard.lua new file mode 100644 index 00000000..20063255 --- /dev/null +++ b/lualib/admin/http/dashboard.lua @@ -0,0 +1,152 @@ +local template = require "template" +local utils = require "admin.utils" +local Cookie = require "admin.cookie" +local config = require "admin.config" +local locales = require "admin.locales" +local role = require "admin.db.role" +local user = require "admin.db.user" +local user_token = require "admin.db.token" +local view = require "admin.db.view" + +local type = type +local ipairs = ipairs + +local get_locale = utils.get_locale +local user_have_permission = utils.user_have_permission + +local template_path = 'lualib/admin/html/dashboard/base.html' + +-- 管理页面验权 +local function verify_permission (content, db) + local args = content.args + -- 切换语言 + if type(args) == 'table' and args.lang then + local lang = args.lang + local locale = config.locales[lang] + if locale then + Cookie.setCookie('CFLANG', lang) + else + Cookie.setCookie('CFLANG', config.locale) + end + return false, config.dashboard + end + -- 登录注入Cookie + if type(args) == 'table' and args.token then + local token = args.token + if not token then -- 对一些错误传参直接重定向到登录页 + return false, config.login_render + end + -- 开启验证Token + local exists = user_token.token_exists(db, token) + if not exists then -- Token不存在需要重新登录 + return false, config.login_render + end + -- 如果是第一次带上Token访问后, admin会给予一个重写URL后的url. + -- 同时admin会在这里将Token写入到Cookie中去, 用户无需感知. + Cookie.setCookie("CFTOKEN", token) + return false, utils.get_path(content) + end + local token = Cookie.getCookie('CFTOKEN') + if not token then -- 未授权的访问 + return false, config.login_render + end + local exists = user_token.token_exists(db, token) + if not exists then -- Token不存在需要重新登录 + return false, config.login_render + end + local info = user.user_info(db, exists.uid) + if not info then + return false, config.login_render + end + info.token = exists.token + info.is_admin = info.is_admin == 1 + info.roles = role.role_permissions(db, info.role) + return true, info +end + +-- 构建根节点 +local function root_tree (list, role) + local root = {} + for _, item in ipairs(list) do + -- 判断是否超级管理员或用户所在权限组有当前页面权限 + if (role.is_admin or user_have_permission(role.roles, item.id)) and item.parent == 0 then + root[#root+1] = {id = item.id, item.name, item.url ~= null and item.url or nil, item.icon ~= null and item.icon or nil} + end + end + return root -- 返回树的根结构 +end + +-- 构建叶(子)节点 +local function sub_tree (root, list, role) + local tab = {} + for index, item in ipairs(list) do + -- 判断是否超级管理员或用户所在权限组有当前页面权限 + if (role.is_admin or user_have_permission(role.roles, item.id)) and root.id == item.parent then + tab[#tab+1] = {id = item.id, item.name, item.url ~= null and item.url or nil, item.icon ~= null and item.icon or nil} + end + end + return #tab > 0 and tab or nil -- 返回树的叶(子)结构 +end + +-- menu 生成 +local function get_menus (db, role) + local list = view.get_menus(db) + if not list then + return {} + end + local roots = root_tree(list, role) + if #roots == 0 then + return roots + end + for _, root in ipairs(roots) do + local subs = sub_tree(root, list, role) + if subs then + for _, sub in ipairs(subs) do + sub[2] = sub_tree(sub, list, role) or sub[2] + end + end + root[2] = subs or root[2] + end + return roots +end + +-- header 生成 +local function get_headers (db) + local tab = {} + for _, header in ipairs(view.get_headers(db)) do + tab[#tab+1] = {header.name, header.url} + end + return tab +end + +local dashboard = {} + +-- 渲染登录页模板 +function dashboard.render(content) + local db = config.db + local ok, user = verify_permission(content, db) + if not ok then + return utils.redirect(user) + end + if not config.cache then + template.cache = {} + end + return template.compile(template_path){ + cdn = config.cdn, + home = config.home, + logo = config.dashboard, + menus = get_menus(db, {is_admin = user.is_admin, roles = user.roles}), + headers = get_headers(db), + username = user.name, + logout = config.login_render, + user = config.system_user_render, + menu = config.system_menu_render, + header = config.system_header_render, + role = config.system_role_render, + profile = config.profile_render, + is_admin = user.is_admin, + locale = get_locale(Cookie.getCookie("CFLANG")) + } +end + +return dashboard diff --git a/lualib/admin/http/login.lua b/lualib/admin/http/login.lua new file mode 100644 index 00000000..911eeea5 --- /dev/null +++ b/lualib/admin/http/login.lua @@ -0,0 +1,65 @@ +local template = require "template" +local utils = require "admin.utils" +local config = require "admin.config" +local Cookie = require "admin.cookie" +local user = require "admin.db.user" +local user_token = require "admin.db.token" +local token = require "admin.token" +local crypt = require "crypt" + +local json = require "json" +local json_decode = json.decode +local json_encode = json.encode + +local get_locale = utils.get_locale + +template_path = 'lualib/admin/html/login/base.html' + +local login = {} + +-- 登录页面逻辑 +function login.render(content) + if not config.cache then + template.cache = {} + end + Cookie.init() -- 初始化所有Cookie + return template.compile(template_path){ + cdn = config.cdn, + login_api = config.login_api, + locale = get_locale(Cookie.getCookie("CFLANG")) + } +end + +-- 登录接口逻辑 +function login.response (content) + local db = config.db + local args = content['args'] + if type(args) ~= 'table' then + return json_encode({code = 401, msg = "1. 非法的参数"}) + end + -- 验证参数 + local username, password = args.username, args.password + if not username or not password then + return json_encode({code = 401, msg = "2. 错误的参数"}) + end + -- 获取登录信息 + local user_info = user.user_exists(db, username) + if not user_info or crypt.hexencode(crypt.sha1(password)) ~= user_info.password then + return json_encode({code = 403, msg = "3. 用户不存在或者密码错误"}) + end + local uid, name = user_info.id, user_info.name + -- 生成token + local TOKEN = token.generate(uid) + -- 将Token写入到数据库内 + local ok, err = user_token.token_add(db, uid, name, TOKEN) + if not ok then + return json_encode({code = 500, msg = err}) + end + return json_encode({ + code = 200, + token = TOKEN, + url = config.dashboard, + }) +end + +return login diff --git a/lualib/admin/http/profile.lua b/lualib/admin/http/profile.lua new file mode 100644 index 00000000..ec6d92a8 --- /dev/null +++ b/lualib/admin/http/profile.lua @@ -0,0 +1,148 @@ +local template = require "template" +local utils = require "admin.utils" +local Cookie = require "admin.cookie" +local config = require "admin.config" +local locales = require "admin.locales" +local role = require "admin.db.role" +local user = require "admin.db.user" +local user_token = require "admin.db.token" +local view = require "admin.db.view" +local crypt = require "crypt" + +local type = type +local ipairs = ipairs +local toint = math.tointeger + +local json = require "json" +local json_decode = json.decode +local json_encode = json.encode + +local url = require "url" +local url_encode = url.encode +local url_decode = url.decode + +local get_locale = utils.get_locale + +local template_path = 'lualib/admin/html/profile/base.html' + +-- 管理页面验权 +local function verify_permission (content, db) + local args = content.args + -- 切换语言 + if type(args) == 'table' and args.lang then + local lang = args.lang + local locale = config.locales[lang] + if locale then + Cookie.setCookie('CFLANG', lang) + else + Cookie.setCookie('CFLANG', config.locale) + end + return false, config.dashboard + end + -- 登录注入Cookie + if type(args) == 'table' and args.token then + local token = args.token + if not token then -- 对一些错误传参直接重定向到登录页 + return false, config.login_render + end + -- 开启验证Token + local exists = user_token.token_exists(db, token) + if not exists then -- Token不存在需要重新登录 + return false, config.login_render + end + -- 如果是第一次带上Token访问后, admin会给予一个重写URL后的url. + -- 同时admin会在这里将Token写入到Cookie中去, 用户无需感知. + Cookie.setCookie("CFTOKEN", token) + return false, utils.get_path(content) + end + local token = Cookie.getCookie('CFTOKEN') + if not token then -- 未授权的访问 + return false, config.login_render + end + local exists = user_token.token_exists(db, token) + if not exists then -- Token不存在需要重新登录 + return false, config.login_render + end + local info = user.user_info(db, exists.uid) + if not info then + return false, config.login_render + end + info.token = exists.token + info.roles = role.role_permissions(db, info.role) + return true, info +end + + +local profile = {} + +-- 渲染登录页模板 +function profile.render(content) + local db = config.db + local ok, user = verify_permission(content, db) + if not ok then + return utils.redirect(user) + end + if not config.cache then + template.cache = {} + end + return template.compile(template_path){ + cdn = config.cdn, + user = user, + api_url = config.profile_api, + locale = get_locale(Cookie.getCookie("CFLANG")) + } +end + +function profile.response (content) + local db = config.db + if type(content.args) ~= 'table' then + return json_encode({code = 500, msg = "1. 错误的参数" }) + end + local args = content.args + local token = args.token + if not token then + return json_encode({code = 400, data = null, msg = '2. 错误的参数'}) + end + -- 验证Token + local exists = user_token.token_exists(db, token) + if not exists then + return json_encode({code = 400, data = null, msg = '3. token不存在'}) + end + args.id = toint(exists.uid) + local user_info = user.user_info(db, args.id) + if not user_info then + return json_encode({code = 400, data = null, msg = '4. 用户不存在'}) + end + if args.action == 'update_password' then + if not args.password or not args.cupassword then + return json_encode({code = 401, msg = "1. 错误的password参数" }) + end + if #args.password < 6 or #args.password > 20 then + return json_encode({code = 403, msg = "2. password长度必须在6~20之间" }) + end + if args.password == args.cupassword then + return json_encode({code = 403, msg = "3. 新老密码不能一样" }) + end + args.password = crypt.hexencode(crypt.sha1(url_decode(args.password))) + args.cupassword = crypt.hexencode(crypt.sha1(url_decode(args.cupassword))) + if user_info.password == args.password then + return json_encode({code = 403, msg = "4. 当前密码不正确或新老密码一致" }) + end + user.user_update_password(db, args) + user_token.token_delete(db, args.id) + return json_encode({code = 0, msg = 'Success: 密码修改成功'}) + end + if args.action == 'update_userinfo' then + if not args.name or not args.email or not args.phone then + return json_encode({code = 500, msg = "2. 错误的info参数" }) + end + args.name = url_decode(args.name) + args.email = url_decode(args.email) + user.user_update_info(db, args) + user_token.token_delete(db, args.id) + return json_encode({code = 0, msg = 'Success: 用户信息更新成功'}) + end + return json_encode({code = 500, msg = "恭喜您完美的错过了所有正确参数" }) +end + +return profile diff --git a/lualib/admin/http/system/header.lua b/lualib/admin/http/system/header.lua new file mode 100644 index 00000000..1443a623 --- /dev/null +++ b/lualib/admin/http/system/header.lua @@ -0,0 +1,182 @@ +local config = require 'admin.config' +local template = require "template" +local utils = require "admin.utils" +local user = require "admin.db.user" +local user_token = require "admin.db.token" +local role = require "admin.db.role" +local header = require "admin.db.header" +local Cookie = require "admin.Cookie" + +local json = require "json" +local json_encode = json.encode +local json_decode = json.decode + +local url = require "url" +local url_encode = url.encode +local url_decode = url.decode + +local type = type +local ipairs = ipairs +local tostring = tostring +local os_date = os.date +local toint = math.tointeger + +local get_locale = utils.get_locale + +local template_path = 'lualib/admin/html/system/header/' + +local function verify_permission (content, db) + local token = Cookie.getCookie("CFTOKEN") + if not token then + return false, config.login_render + end + local exists = user_token.token_exists(db, token) + if not exists then -- Token不存在需要重新登录 + return false, config.login_render + end + local info = user.user_info(db, exists.uid) + if not info or info.is_admin ~= 1 then + return false, config.login_render + end + info.token = exists.token + info.roles = role.role_permissions(db, info.role) + return true, info +end + +local system = {} + +-- 用户管理render +function system.header_render (content) + local db = config.db + local ok, user = verify_permission(content, db) + if not ok then + return utils.redirect(user) + end + if not config.cache then + template.cache = {} + end + return template.compile(template_path..'header.html'){ + cdn = config.cdn, + token = user.token, + api_url = config.system_header_api, + header_add_url = config.system_header_add_render, + header_edit_url = config.system_header_edit_render, + locale = get_locale(Cookie.getCookie('CFLANG')) + } +end + +-- 添加导航 +function system.header_add_render (content) + local db = config.db + local ok, user = verify_permission(content, db) + if not ok then + return utils.redirect(user) + end + if not config.cache then + template.cache = {} + end + return template.compile(template_path..'header-add.html'){ + cdn = config.cdn, + api_url = config.system_header_api, + token = Cookie.getCookie("CFTOKEN"), + locale = get_locale(Cookie.getCookie('CFLANG')) + } +end + +-- 编辑导航 +function system.header_edit_render (content) + local db = config.db + local ok, user = verify_permission(content, db) + if not ok then + return utils.redirect(user) + end + local args = content.args + if not args or not args.id then + return utils.error_404() + end + local h = header.get_header(db, args.id) + if not h then + return utils.error_404() + end + if not config.cache then + template.cache = {} + end + return template.compile(template_path..'header-edit.html'){ + cdn = config.cdn, + id = h.id, + url = h.url, + name = h.name, + api_url = config.system_header_api, + token = Cookie.getCookie("CFTOKEN"), + locale = get_locale(Cookie.getCookie('CFLANG')) + } +end + +-- 用户管理API接口 +function system.header_response (content) + local db = config.db + local args = content.args + if type(args) ~= 'table' then + return json_encode({code = 400, data = null, msg = '1. 错误的参数'}) + end + local token = args.token + if not token then + return json_encode({code = 400, data = null, msg = '2. 错误的参数'}) + end + -- 验证Token + local exists = user_token.token_exists(db, token) + if not exists then + return json_encode({code = 400, data = null, msg = '3. token不存在或权限不足'}) + end + local user_info = user.user_info(db, exists.uid) + if not user_info or user_info.is_admin == 0 then + return json_encode({code = 400, data = null, msg = '4. 用户权限不足'}) + end + if args.action == 'delete' then + local headerid = toint(args.headerid) + if not headerid then + return json_encode({ code = 400, data = null, msg = '1. 未知的header' }) + end + local exists = header.header_exists(db, headerid) + if not exists then + return json_encode({ code = 400, data = null, msg = '2. 试图删除一个不存在的的header' }) + end + header.header_delete(db, headerid) + return json_encode({ code = 0, data = null, msg = '删除成功'}) + end + if args.action == 'list' then + local headers = header.header_list(db, args) + for _, header in ipairs(headers) do + header.create_at = os_date("%Y-%m-%d %H:%M:%S", header.create_at) + header.update_at = os_date("%Y-%m-%d %H:%M:%S", header.update_at) + end + return json_encode({code = 0, data = headers, count = header.header_count(db)}) + end + if args.action == 'add' then + if not args.url or not args.name then + return json_encode({ code = 400, data = null, msg = "添加导航栏参数错误"}) + end + args.url = url_decode(args.url) + args.name = url_decode(args.name) + header.header_add(db, args) + return json_encode({code = 0, msg = "添加成功"}) + end + if args.action == 'edit' then + if not args.id then + json_encode({ code = 400, data = null, msg = "1. 找不到此header"}) + end + local url = tostring(args.url) + local name = tostring(args.name) + if not url or not name then + json_encode({ code = 400, data = null, msg = "2. 非法的参数"}) + end + args.url = url_decode(args.url) + args.name = url_decode(args.name) + header.header_update(db, args) + return json_encode({ code = 0, msg = "修改成功"}) + end + return json_encode({code = 500, data = null, msg = '恭喜您完美的错过了所有正确参数'}) +end + + +return system diff --git a/lualib/admin/http/system/init.lua b/lualib/admin/http/system/init.lua new file mode 100644 index 00000000..a3b2d1f5 --- /dev/null +++ b/lualib/admin/http/system/init.lua @@ -0,0 +1,32 @@ +local user = require "admin.http.system.user" +local menu = require "admin.http.system.menu" +local header = require "admin.http.system.header" +local role = require "admin.http.system.role" + +return { + + -- 用户管理 Controller + user_response = user.user_response, + user_render = user.user_render, + user_add_render = user.user_add_render, + user_edit_render = user.user_edit_render, + + -- 角色管理 Controller + role_response = role.role_response, + role_render = role.role_render, + role_add_render = role.role_add_render, + role_edit_render = role.role_edit_render, + + -- 侧边栏 Controller + menu_response = menu.menu_response, + menu_render = menu.menu_render, + menu_add_render = menu.menu_add_render, + menu_edit_render = menu.menu_edit_render, + + -- 导航栏 Controller + header_response = header.header_response, + header_render = header.header_render, + header_add_render = header.header_add_render, + header_edit_render = header.header_edit_render, + +} diff --git a/lualib/admin/http/system/menu.lua b/lualib/admin/http/system/menu.lua new file mode 100644 index 00000000..890a9f3d --- /dev/null +++ b/lualib/admin/http/system/menu.lua @@ -0,0 +1,238 @@ +local config = require 'admin.config' +local template = require "template" +local utils = require "admin.utils" +local user = require "admin.db.user" +local user_token = require "admin.db.token" +local menu = require "admin.db.menu" +local role = require "admin.db.role" +local view = require "admin.db.view" +local Cookie = require "admin.Cookie" + +local json = require "json" +local json_encode = json.encode +local json_decode = json.decode + +local url = require "url" +local url_encode = url.encode +local url_decode = url.decode + +local type = type +local ipairs = ipairs +local os_date = os.date +local toint = math.tointeger + +local get_locale = utils.get_locale + +local template_path = 'lualib/admin/html/system/menu/' + +local function verify_permission (content, db) + local token = Cookie.getCookie("CFTOKEN") + if not token then + return false, config.login_render + end + local exists = user_token.token_exists(db, token) + if not exists then -- Token不存在需要重新登录 + return false, config.login_render + end + local info = user.user_info(db, exists.uid) + if not info or info.is_admin ~= 1 then + return false, config.login_render + end + info.token = exists.token + info.roles = role.role_permissions(db, info.role) + return true, info +end + +-- 构建根节点 +local function root_tree (list, role) + local root = {} + for _, item in ipairs(list) do + -- 判断是否超级管理员或用户所在权限组有当前页面权限 + if (role.is_admin or user_have_permission(role.roles, item.id)) and item.parent == 0 then + root[#root+1] = {id = item.id, name = item.name, url = item.url ~= null and item.url or nil, icon = item.icon ~= null and item.icon or nil} + end + end + return root -- 返回树的根结构 +end + +-- 构建叶(子)节点 +local function sub_tree (root, list, role) + local tab = {} + for index, item in ipairs(list) do + -- 判断是否超级管理员或用户所在权限组有当前页面权限 + if (role.is_admin or user_have_permission(role.roles, item.id)) and root.id == item.parent then + tab[#tab+1] = {id = item.id, name = item.name, url = item.url ~= null and item.url or nil, icon = item.icon ~= null and item.icon or nil} + end + end + return #tab > 0 and tab or nil -- 返回树的叶(子)结构 +end + +-- menu 生成 +local function get_menus (db, role) + local list = view.get_menus(db) + if not list then + return {} + end + local roots = root_tree(list, role) + if #roots == 0 then + return roots + end + for _, root in ipairs(roots) do + local subs = sub_tree(root, list, role) + if subs then + for _, sub in ipairs(subs) do + sub['list'] = sub_tree(sub, list, role) + end + end + root['list'] = subs + end + return roots +end + + +local system = {} + +-- 菜单管理render +function system.menu_render (content) + local db = config.db + local ok, user = verify_permission(content, db) + if not ok then + return utils.redirect(user) + end + if not config.cache then + template.cache = {} + end + return template.compile(template_path..'menu.html'){ + cdn = config.cdn, + token = user.token, + api_url = config.system_menu_api, + menu_add_url = config.system_menu_add_render, + menu_edit_url = config.system_menu_edit_render, + menus = get_menus(db, {is_admin = user.is_admin, roles = user.roles}), + locale = get_locale(Cookie.getCookie('CFLANG')) + } +end + +function system.menu_add_render (content) + local db = config.db + local ok, user = verify_permission(content, db) + if not ok then + return utils.redirect(user) + end + if not config.cache then + template.cache = {} + end + local args = content.args + if type(args) ~= 'table' then + args = { id = nil } + else + args.id = toint(args.id) or nil + end + return template.compile(template_path..'menu-add.html'){ + cdn = config.cdn, + id = args.id, + token = user.token, + api_url = config.system_menu_api, + locale = get_locale(Cookie.getCookie('CFLANG')) + } +end + +function system.menu_edit_render (content) + local db = config.db + local ok, user = verify_permission(content, db) + if not ok then + return utils.redirect(user) + end + if not config.cache then + template.cache = {} + end + local args = content.args + if type(args) ~= 'table' then + return utils.error_404(config.login_render) + end + local id = toint(args.id) + if not id then + return utils.error_404(config.login_render) + end + local menu = menu.menu_info(db, id) + if not menu then + return utils.error_404(config.login_render) + end + return template.compile(template_path..'menu-edit.html'){ + cdn = config.cdn, + token = user.token, + id = id, + menu = menu, + api_url = config.system_menu_api, + locale = get_locale(Cookie.getCookie('CFLANG')) + } +end + +-- 菜单管理API接口 +function system.menu_response (content) + local db = config.db + local args = content.args + if type(args) ~= 'table' then + return json_encode({code = 400, data = null, msg = '1. 错误的参数'}) + end + local token = args.token + if not token then + return json_encode({code = 400, data = null, msg = '2. 错误的参数'}) + end + -- 验证Token + local exists = user_token.token_exists(db, token) + if not exists then + return json_encode({code = 400, data = null, msg = '3. token不存在或权限不足'}) + end + local user_info = user.user_info(db, exists.uid) + if not user_info or user_info.is_admin == 0 then + return json_encode({code = 400, data = null, msg = '4. 用户权限不足'}) + end + -- 添加菜单 + local action = args.action + if action == 'add' then + if not args.name then + return json_encode({ code = 401, msg = '1. add参数不足'}) + end + args.id = toint(args.id) or 0 -- id > 0 则为添加子菜单 + args.name = url_decode(args.name) + args.url = url_decode(args.url) or "NULL" + args.icon = url_decode(args.icon) + menu.menu_add(db, args) + return json_encode({code = 0, msg = 'Success'}) + end + -- 删除菜单 + if action == 'delete' then + local id = toint(args.id) + if not id then + return json_encode({ code = 500, msg = "1.1 错误的删除菜单参数"}) + end + menu.menu_delete(db, id) + return json_encode({code = 0, msg = "删除成功"}) + end + if action == 'edit' then + args.id = toint(args.id) + if not args.id then + return json_encode({ code = 500, msg = "1.1 错误的删除菜单参数"}) + end + args.name = url_decode(args.name) + args.url = url_decode(args.url) or "NULL" + args.icon = url_decode(args.icon) + menu.menu_update(db, args) + return json_encode({code = 0, msg = "修改成功"}) + end + -- 获取菜单列表 + if action == 'list' then + local menus = menu.menu_list(db, args) + for _, menu in ipairs(menus) do + menu.create_at = os_date("%Y-%m-%d %H:%M:%S", menu.create_at) + menu.update_at = os_date("%Y-%m-%d %H:%M:%S", menu.delete_at) + end + return json_encode({ code = 0, data = menus, count = menu.menu_count(db) }) + end + -- 如果没有action字段则返回500 + return json_encode({code = 500, data = null, msg = '恭喜您完美的给予了错误的参数'}) +end + + +return system diff --git a/lualib/admin/http/system/role.lua b/lualib/admin/http/system/role.lua new file mode 100644 index 00000000..fb9ace2b --- /dev/null +++ b/lualib/admin/http/system/role.lua @@ -0,0 +1,200 @@ +local config = require 'admin.config' +local template = require "template" +local utils = require "admin.utils" +local user = require "admin.db.user" +local user_token = require "admin.db.token" +local menu = require "admin.db.menu" +local role = require "admin.db.role" +local Cookie = require "admin.Cookie" + +local json = require "json" +local json_encode = json.encode +local json_decode = json.decode + +local url = require "url" +local url_encode = url.encode +local url_decode = url.decode + +local type = type +local ipairs = ipairs +local os_date = os.date +local toint = math.tointeger + +local get_locale = utils.get_locale +local role_already_selected = utils.role_already_selected + +local template_path = 'lualib/admin/html/system/role/' + +local function verify_permission (content, db) + local token = Cookie.getCookie("CFTOKEN") + if not token then + return false, config.login_render + end + local exists = user_token.token_exists(db, token) + if not exists then -- Token不存在需要重新登录 + return false, config.login_render + end + local info = user.user_info(db, exists.uid) + if not info or info.is_admin ~= 1 then + return false, config.login_render + end + info.token = exists.token + info.roles = role.role_permissions(db, info.role) + return true, info +end + +local system = {} + +-- 角色管理 +function system.role_render (content) + local db = config.db + local ok, user = verify_permission(content, db) + if not ok then + return utils.redirect(opt) + end + if not config.cache then + template.cache = {} + end + return template.compile(template_path..'role.html'){ + cdn = config.cdn, + token = user.token, + api_url = config.system_role_api, + role_add_url = config.system_role_add_render, + role_edit_url = config.system_role_edit_render, + locale = get_locale(Cookie.getCookie('CFLANG')) + } +end + +-- 添加角色 +function system.role_add_render (content) + local db = config.db + local ok, user = verify_permission(content, db) + if not ok then + return utils.redirect(opt) + end + if not config.cache then + template.cache = {} + end + return template.compile(template_path..'role-add.html'){ + cdn = config.cdn, + token = user.token, + api_url = config.system_role_api, + locale = get_locale(Cookie.getCookie('CFLANG')) + } +end + +-- 编辑角色 +function system.role_edit_render (content) + local db = config.db + local ok, user = verify_permission(content, db) + if not ok then + return utils.redirect(opt) + end + if not config.cache then + template.cache = {} + end + local args = content.args + if type(args) ~= 'table' then + return utils.error_404(config.login_render) + end + local id = toint(args.id) + if not id then + return utils.error_404(config.login_render) + end + local exists = role.role_id_exists(db, id) + if not exists then + return utils.error_404(config.login_render) + end + return template.compile(template_path..'role-edit.html'){ + cdn = config.cdn, + id = exists.id, + name = exists.name, + token = user.token, + api_url = config.system_role_api, + locale = get_locale(Cookie.getCookie('CFLANG')) + } +end + +-- 用户管理API接口 +function system.role_response (content) + local db = config.db + local args = content.args + if type(args) ~= 'table' then + if not content.json then -- 可以用这个字段判断是否json请求 + return json_encode({code = 400, data = null, msg = '1. 错误的参数'}) + end + args = json_decode(content.body) + end + local token = args.token + if not token then + return json_encode({code = 400, data = null, msg = '2. 错误的参数'}) + end + -- 验证Token + local exists = user_token.token_exists(db, token) + if not exists then + return json_encode({code = 400, data = null, msg = '3. token不存在或权限不足'}) + end + local user_info = user.user_info(db, exists.uid) + if not user_info or user_info.is_admin == 0 then + return json_encode({code = 400, data = null, msg = '4. 用户权限不足'}) + end + if args.action == 'list' then + return json_encode({code = 0, data = role.role_list(db, args)}) + end + if args.action == 'add' then + if not args.name or not args.permissions then + return json_encode({code = 400, data = null, msg = '错误的role创建参数'}) + end + args.name = url_decode(args.name) + if role.role_name_exists(db, args.name) then + return json_encode({ code = 401, msg = "角色名已存在"}) + end + local name = role.role_add(db, args) + return json_encode({code = 0, msg = '添加成功'}) + end + if args.action == 'get_tree_list' then + return json_encode({code = 0, data = menu.menu_tree(db, {page=1, limit=1000})}) + end + if args.action == 'get_veri_tree' then + local id = toint(args.id) + if not id then + return json_encode({code = 400, msg = "错误的参数"}) + end + local menus = menu.menu_tree(db, {page=1, limit=1000}) + if #menus <= 0 then + return json_encode({code = 0, data = json.empty_array}) + end + local permissions = role.role_permissions(db, id) + for _, menu in ipairs(menus) do + if role_already_selected(permissions, menu.id) then + menu.checkArr = {isChecked = 1} + end + end + return json_encode({code = 0, data = menus}) + end + if args.action == 'edit' then + args.id = toint(args.id) + if not args.id or not args.name or type(args.permissions) ~= 'table' then + return json_encode({code = 400, data = null, msg = '错误的role修改参数'}) + end + args.name = url_decode(args.name) + role.role_update(db, args) + return json_encode({code = 0, msg = '更新成功'}) + end + if args.action == 'delete' then + local id = toint(args.id) + if not id then + return json_encode({code = 400, msg = "1. 删除失败"}) + end + local exists = role.role_id_exists(db, id) + if not exists then + return json_encode({code = 400, msg = "2. 试图删除一个不存在的role"}) + end + role.role_delete(db, id) + return json_encode({code = 0, msg = "role删除成功"}) + end + return json_encode({code = 500, msg = '恭喜您完美的错过了所有正确参数'}) +end + + +return system diff --git a/lualib/admin/http/system/user.lua b/lualib/admin/http/system/user.lua new file mode 100644 index 00000000..c536d802 --- /dev/null +++ b/lualib/admin/http/system/user.lua @@ -0,0 +1,206 @@ +local config = require 'admin.config' +local template = require "template" +local utils = require "admin.utils" +local user = require "admin.db.user" +local user_token = require "admin.db.token" +local role = require "admin.db.role" +local Cookie = require "admin.Cookie" +local crypt = require "crypt" + +local json = require "json" +local json_encode = json.encode +local json_decode = json.decode + +local url = require "url" +local url_encode = url.encode +local url_decode = url.decode + +local type = type +local ipairs = ipairs +local os_date = os.date +local toint = math.tointeger + +local get_locale = utils.get_locale + +local template_path = 'lualib/admin/html/system/user/' + +local function verify_permission (content, db) + local token = Cookie.getCookie("CFTOKEN") + if not token then + return false, config.login_render + end + local exists = user_token.token_exists(db, token) + if not exists then -- Token不存在需要重新登录 + return false, config.login_render + end + local info = user.user_info(db, exists.uid) + if not info or info.is_admin ~= 1 then + return false, config.login_render + end + info.token = exists.token + info.roles = role.role_permissions(db, info.role) + return true, info +end + +local system = {} + +-- 用户管理render +function system.user_render (content) + local db = config.db + local ok, user = verify_permission(content, db) + if not ok then + return utils.redirect(user) + end + if not config.cache then + template.cache = {} + end + return template.compile(template_path..'user.html'){ + cdn = config.cdn, + token = user.token, + api_url = config.system_user_api, + user_add_url = config.system_user_add_render, + user_edit_url = config.system_user_edit_render, + locale = get_locale(Cookie.getCookie('CFLANG')) + } +end + +-- 添加用户页面 +function system.user_add_render (content) + local db = config.db + local ok, user = verify_permission(content, db) + if not ok then + return utils.redirect(user) + end + if not config.cache then + template.cache = {} + end + return template.compile(template_path..'user-add.html'){ + cdn = config.cdn, + token = user.token, + api_url = config.system_user_api, + roles = role.role_list(db, {limit = 100}), + locale = get_locale(Cookie.getCookie('CFLANG')) + } +end + +-- 编辑用户 +function system.user_edit_render (content) + local db = config.db + local ok, opt = verify_permission(content, db) + if not ok then + return utils.redirect(opt) + end + local args = content.args + if not type(args) == 'table' and not args.id then + return utils.error_404(config.login_render) + end + local user = user.user_info(db, args.id or 0) + if not user then + return utils.error_404(config.login_render) + end + if not config.cache then + template.cache = {} + end + return template.compile(template_path..'user-edit.html'){ + cdn = config.cdn, + token = opt.token, + api_url = config.system_user_api, + user = user, + roles = role.role_list(db, {limit = 100}), + locale = get_locale(Cookie.getCookie('CFLANG')) + } +end + +-- 用户管理API接口 +function system.user_response (content) + local db = config.db + local args = content.args + if type(args) ~= 'table' then + return json_encode({code = 400, data = null, msg = '1. 错误的参数'}) + end + local token = args.token + if not token then + return json_encode({code = 400, data = null, msg = '2. 错误的参数'}) + end + -- 验证Token + local exists = user_token.token_exists(db, token) + if not exists then + return json_encode({code = 400, data = null, msg = '3. token不存在或权限不足'}) + end + local user_info = user.user_info(db, exists.uid) + if not user_info or user_info.is_admin == 0 then + return json_encode({code = 400, data = null, msg = '4. 用户权限不足'}) + end + -- 添加用户 + local action = args.action + if action == 'add' then + if not args.name or not args.username or not args.password then + return json_encode({code = 400, data = null, msg = '1. 用户信息不完善'}) + end + args.phone = toint(args.phone) + args.role = toint(args.role) + if not args.role or not args.email or not args.phone then + return json_encode({code = 400, data = null, msg = '2. 用户信息不完善'}) + end + if #args.username < 6 or #args.username > 20 or #args.password < 6 or #args.password > 20 then + return json_encode({code = 400, data = null, msg = '3. 用户名与密码需要在6~20字符之间'}) + end + args.name = url.decode(args.name) + args.email = url.decode(args.email) + local exists = user.user_exists(db, args.username) + if exists then + return json_encode({code = 400, data = null, msg = '4. 用户已存在'}) + end + args.password = crypt.hexencode(crypt.sha1(args.password)) + user.user_add(db, args) + return json_encode({code = 0, msg = "添加成功"}) + end + -- 删除用户 + if action == 'delete' then + local uid = toint(args.uid) + if not uid then + return json_encode({code = 400, data = null, msg = '1. 未知的uid'}) + end + local exists = user.user_exists(db, nil, uid) + if not exists then + return json_encode({code = 400, data = null, msg = '2. 试图删除不存在的用户'}) + end + user.user_delete(db, uid) + user_token.token_delete(db, uid) -- 清除Token + return json_encode({code = 0, msg = "删除用户成功"}) + end + -- 获取用户列表 + if action == 'list' then + local users = user.user_list(db, args) + for _, user in ipairs(users) do + user.create_at = os_date("%Y-%m-%d %H:%M:%S", user.create_at) + user.update_at = os_date("%Y-%m-%d %H:%M:%S", user.update_at) + end + return json_encode({code = 0, msg = "SUCCESS", count = user.user_count(db), data = users}) + end + if action == 'edit' then + if not args.name then + return json_encode({code = 400, data = null, msg = "1. 未知的用户名"}) + end + args.name = url_decode(args.name) + if not args.id or not args.role then + return json_encode({code = 400, data = null, msg = "2. 未知的用户与权限"}) + end + if not args.username or not args.password then + return json_encode({code = 400, data = null, msg = "3. 未知的账户与密码"}) + end + if not args.phone or not args.email then + return json_encode({code = 400, data = null, msg = "4. 邮箱与手机号"}) + end + args.email = url_decode(args.email) + args.password = crypt.hexencode(crypt.sha1(url_decode(args.password))) + user.user_update(db, args) + user_token.token_delete(db, args.id) -- 清除Token + return json_encode({code = 0, msg = "SUCCESS"}) + end + -- 如果没有action字段则返回500 + return json_encode({code = 500, data = null, msg = '恭喜您完美的给予了错误的参数'}) +end + + +return system diff --git a/lualib/admin/init.lua b/lualib/admin/init.lua new file mode 100644 index 00000000..6fab288d --- /dev/null +++ b/lualib/admin/init.lua @@ -0,0 +1,119 @@ +local template = require "template" +local config = require "admin.config" +local locales = require "admin.locales" +local admin_db = require "admin.db" +local login = require "admin.http.login" +local system = require "admin.http.system" +local profile = require "admin.http.profile" +local dashboard = require "admin.http.dashboard" + +local admin = {} + +-- 开启全局模板缓存 +function admin.cached() + config.cache = true +end + +-- 设置静态文件的域名与前缀 +function admin.static(domain) + config.cdn = domain +end + +-- Cookie时间 +function admin.cookie_timeout (timeout) + config.cookie_timeout = tonumber(timeout) or 86400 +end + +-- 添加某语言的字段 +function admin.add_locale_item(lang, locale_item) + local locale = locales[lang] + if locale and type(locale) == 'table' then + for _, item in ipairs(locale_item) do + locale[item[1]] = item[2] + end + end +end + +-- 修改全局默认语言 +function admin.set_locale (locale) + if type(locale) == 'string' then + config.locale = locale + end +end + +-- 设置仪表盘显示页 +function admin.init_home (url) + config.home = url or config.home +end + +function admin.init_page (app, db) + config.app = assert(app, '初始化必须传入有效的http对象') + config.db = assert(db, '初始化必须传入有效的db对象') + + config.home = config.home or '/welcome.html' + + -- 注册登录页相关路由 + config.login_api = '/api/login' + config.login_render = '/admin' + app:api(config.login_api, login.response) + app:use(config.login_render, login.render) + + -- 后台首页路由 + config.dashboard = '/admin/dashboard' + app:use(config.dashboard, dashboard.render) + + -- 用户管理路由 + config.system_user_api = '/api/system/user' + config.system_user_render = '/admin/system/user' + config.system_user_add_render = '/admin/system/user/add' + config.system_user_edit_render = '/admin/system/user/edit' + app:api(config.system_user_api, system.user_response) + app:use(config.system_user_render, system.user_render) + app:use(config.system_user_add_render, system.user_add_render) + app:use(config.system_user_edit_render, system.user_edit_render) + + -- 菜单管理 + config.system_menu_api = '/api/system/menu' + config.system_menu_render = '/admin/system/menu' + config.system_menu_add_render = '/admin/system/menu/add' + config.system_menu_edit_render = '/admin/system/menu/edit' + app:api(config.system_menu_api, system.menu_response) + app:use(config.system_menu_render, system.menu_render) + app:use(config.system_menu_add_render, system.menu_add_render) + app:use(config.system_menu_edit_render, system.menu_edit_render) + + -- 导航管理 + config.system_header_api = '/api/system/header' + config.system_header_render = '/admin/system/header' + config.system_header_add_render = '/admin/system/header/add' + config.system_header_edit_render = '/admin/system/header/edit' + app:api(config.system_header_api, system.header_response) + app:use(config.system_header_render, system.header_render) + app:use(config.system_header_add_render, system.header_add_render) + app:use(config.system_header_edit_render, system.header_edit_render) + + -- 权限管理 + config.system_role_api = '/api/system/role' + config.system_role_render = '/admin/system/role' + config.system_role_add_render = '/admin/system/role/add' + config.system_role_edit_render = '/admin/system/role/edit' + app:api(config.system_role_api, system.role_response) + app:use(config.system_role_render, system.role_render) + app:use(config.system_role_add_render, system.role_add_render) + app:use(config.system_role_edit_render, system.role_edit_render) + + + -- profile路由 + config.profile_api = '/api/profile' + config.profile_render = '/admin/profile' + app:api(config.profile_api, profile.response) + app:use(config.profile_render, profile.render) + +end + +-- 初始化数据 +function admin.init_db (...) + return admin_db(...) +end + +return admin diff --git a/lualib/admin/locales/EN-US.lua b/lualib/admin/locales/EN-US.lua new file mode 100644 index 00000000..9ae8282f --- /dev/null +++ b/lualib/admin/locales/EN-US.lua @@ -0,0 +1,175 @@ +return { + -- 错误页 + ['error.404.title'] = 'CFAdmin Error Page', + ['error.404.message'] = "can't find page or data, Please contact Administrator.", + + -- 登录页 + ['login.form.title'] = 'CFAdmin System', + ['login.form.username'] = 'Username', + ['login.form.password'] = 'Password', + ['login.form.submit'] = 'Click Login', + + -- 主页 + ['dashboard.header.title'] = 'CFAdmin System', + ['dashboard.header.logo'] = 'CFAdmin System', + ['dashboard.header.user.profile'] = 'Profile', + ['dashboard.header.user.quit'] = 'Logout', + + ['dashboard.header.langs'] = 'Select Lang', + ['dashboard.header.langs.us-english'] = 'English-America', + ['dashboard.header.langs.simplified-chinese'] = 'Simplified-Chinese', + + ['dashboard.header.profile.update_userinfo'] = 'Update Profile', + ['dashboard.header.profile.form.name'] = 'Name', + ['dashboard.header.profile.form.name.notice'] = 'User Display Name', + ['dashboard.header.profile.form.username'] = 'User Name', + ['dashboard.header.profile.form.username.notice'] = 'User Account', + ['dashboard.header.profile.form.email'] = 'Email', + ['dashboard.header.profile.form.phone'] = 'Phone', + + + ['dashboard.header.profile.update_password'] = 'Update Password', + ['dashboard.header.profile.form.cupassword'] = 'Old-PW', + ['dashboard.header.profile.form.password'] = 'New-PW', + -- 面包屑 + ['dashboard.crumbs.home'] = 'Dashboard', + ['dashboard.crumbs.close_all'] = 'Close All Page', + ['dashboard.crumbs.close_other'] = 'Close Other Page', + ['dashboard.crumbs.close_this'] = 'Close Current Page', + + -- 菜单栏 + ['dashboard.menu.home'] = 'Dashboard', + ['dashboard.menu.system'] = 'System Manage', + ['dashboard.menu.system.user'] = 'User Manage', + ['dashboard.menu.system.menu'] = 'Menu Manage', + ['dashboard.menu.system.header'] = 'NaviBar Manage', + ['dashboard.menu.system.role'] = 'Roles Manage', + + -- 用户管理 + ['dashboard.menu.user_manage.title'] = 'User Manage', + ['dashboard.menu.user_manage.table.uid'] = 'UID', + ['dashboard.menu.user_manage.table.name'] = 'Name', + ['dashboard.menu.user_manage.table.username'] = 'User Name', + ['dashboard.menu.user_manage.table.permission_name'] = 'Role Name', + ['dashboard.menu.user_manage.table.phone'] = 'Phone', + ['dashboard.menu.user_manage.table.email'] = 'Email', + ['dashboard.menu.user_manage.table.create_time'] = 'Create Time', + ['dashboard.menu.user_manage.table.update_time'] = 'Update Time', + ['dashboard.menu.user_manage.table.options'] = 'Options', + + ['dashboard.menu.user_manage.table.reflush'] = 'Reflush', + ['dashboard.menu.user_manage.table.adduser'] = 'Add User', + ['dashboard.menu.user_manage.table.edituser'] = 'Edit User', + + ['dashboard.menu.user_manage.table.options.edit'] = 'Edit', + ['dashboard.menu.user_manage.table.options.delete'] = 'Delete', + + ['dashboard.menu.user_manage.table.options.delete.confirm'] = 'confirm Delete it?', + ['dashboard.menu.user_manage.table.options.delete.confirm.success'] = 'Delet Success.', + + -- 用户新增页面 + ['dashboard.menu.user_manage.form.user_add.name'] = 'Name', + ['dashboard.menu.user_manage.form.user_add.name.notice'] = 'User Name', + ['dashboard.menu.user_manage.form.user_add.username'] = 'User Name', + ['dashboard.menu.user_manage.form.user_add.username.notice'] = 'Login User Name', + ['dashboard.menu.user_manage.form.user_add.password'] = 'Password', + ['dashboard.menu.user_manage.form.user_add.password.notice'] = 'Password must between 6 ~ 20', + ['dashboard.menu.user_manage.form.user_add.role'] = 'Role', + ['dashboard.menu.user_manage.form.user_add.phone'] = 'Phone', + ['dashboard.menu.user_manage.form.user_add.email'] = 'Email', + + -- 用户编辑页面 + ['dashboard.menu.user_manage.form.user_edit.name'] = 'Name', + ['dashboard.menu.user_manage.form.user_edit.name.notice'] = 'User Name', + ['dashboard.menu.user_manage.form.user_edit.username'] = 'User Name', + ['dashboard.menu.user_manage.form.user_edit.username.notice'] = 'Login User Name', + ['dashboard.menu.user_manage.form.user_edit.password'] = 'Password', + ['dashboard.menu.user_manage.form.user_edit.role'] = 'Role', + ['dashboard.menu.user_manage.form.user_edit.password.notice'] = 'Password must between 6 ~ 20', + ['dashboard.menu.user_manage.form.user_edit.phone'] = 'Phone', + ['dashboard.menu.user_manage.form.user_edit.email'] = 'Email', + + ['dashboard.menu.user_manage.form.submit'] = 'Submit', + + -- 菜单管理 + ['dashboard.menu.menu_manage.title'] = 'Menu Manage', + ['dashboard.menu.menu_manage.table.id'] = 'ID', + ['dashboard.menu.menu_manage.table.name'] = 'Name', + ['dashboard.menu.menu_manage.table.url'] = 'Link', + ['dashboard.menu.menu_manage.table.icon'] = 'Icon', + ['dashboard.menu.menu_manage.table.options'] = 'Options', + + ['dashboard.menu.menu_manage.table.options.add'] = 'Add', + ['dashboard.menu.menu_manage.table.options.edit'] = 'Edit', + ['dashboard.menu.menu_manage.table.options.delete'] = 'Delete', + ['dashboard.menu.menu_manage.table.options.reflush'] = 'Reflush', + ['dashboard.menu.menu_manage.table.options.confirm'] = 'confirm delete this row?', + + ['dashboard.menu.menu_manage.menu_add.form.name'] = 'Menu Name', + ['dashboard.menu.menu_manage.menu_add.form.name.notice'] = 'Displaye Menu Name', + ['dashboard.menu.menu_manage.menu_add.form.url'] = 'Menu Link', + ['dashboard.menu.menu_manage.menu_add.form.url.notice'] = "Menu Open Link", + ['dashboard.menu.menu_manage.menu_add.form.icon'] = 'Menu Icon', + ['dashboard.menu.menu_manage.menu_add.form.icon.notice'] = 'Menu Left Icon', + + ['dashboard.menu.menu_manage.menu_add.form.submit'] = 'Submit', + + ['dashboard.menu.menu_manage.menu_edit.form.name'] = 'Menu Name', + ['dashboard.menu.menu_manage.menu_edit.form.name.notice'] = 'Displaye Menu Name', + ['dashboard.menu.menu_manage.menu_edit.form.url'] = 'Menu Link', + ['dashboard.menu.menu_manage.menu_edit.form.url.notice'] = "Menu Open Link", + ['dashboard.menu.menu_manage.menu_edit.form.icon'] = 'Menu Icon', + ['dashboard.menu.menu_manage.menu_edit.form.icon.notice'] = 'Menu Left Icon', + + ['dashboard.menu.menu_manage.menu_edit.form.submit'] = 'Submit', + + -- 导航管理 + ['dashboard.menu.header_manage.title'] = 'NaviBar Manage', + -- ['dashboard.menu.header_manage.table.id'] = 'NaviBar ID', + ['dashboard.menu.header_manage.table.name'] = 'NaviBar Name', + ['dashboard.menu.header_manage.table.url'] = 'NaviBar Link', + ['dashboard.menu.header_manage.table.permission_name'] = 'NaviBar Permission', + ['dashboard.menu.header_manage.table.create_time'] = 'Create Time', + ['dashboard.menu.header_manage.table.update_time'] = 'Update Timer', + ['dashboard.menu.header_manage.table.options'] = 'Options', + + ['dashboard.menu.header_manage.table.reflush'] = 'Reflush', + ['dashboard.menu.header_manage.table.addnavi'] = 'Add NaviBar', + ['dashboard.menu.header_manage.table.editnavi'] = 'Edit NaviBar', + + ['dashboard.menu.header_manage.table.options.edit'] = 'Edit', + ['dashboard.menu.header_manage.table.options.delete'] = 'Delete', + + ['dashboard.menu.header_manage.table.options.delete.confirm'] = 'Confirm Delete?', + ['dashboard.menu.header_manage.table.options.delete.confirm.success'] = 'Delete Success', + + -- 导航新增页面 + ['dashboard.menu.header_manage.form.header_add.name'] = 'NavBar Name', + ['dashboard.menu.header_manage.form.header_add.name.notice'] = 'NavBar Name is required', + ['dashboard.menu.header_manage.form.header_add.url'] = 'NavBar URL', + ['dashboard.menu.header_manage.form.header_add.url.notice'] = 'NavBar Url is required', + + -- 导航编辑页面 + ['dashboard.menu.header_manage.form.header_edit.name'] = 'NavBar Name', + ['dashboard.menu.header_manage.form.header_edit.name.notice'] = 'NavBar Name is required', + ['dashboard.menu.header_manage.form.header_edit.url'] = 'NavBar URL', + ['dashboard.menu.header_manage.form.header_edit.url.notice'] = 'NavBar Url is required', + + ['dashboard.menu.header_manage.form.submit'] = 'Submit', + + -- 角色管理 + ['dashboard.menu.role_manage.title'] = 'Role Manager', + ['dashboard.menu.role_manage.table.name'] = 'Role Name', + ['dashboard.menu.role_manage.table.create_time'] = 'Create Time', + ['dashboard.menu.role_manage.table.update_time'] = 'Update Time', + ['dashboard.menu.role_manage.table.options'] = 'Options', + + ['dashboard.menu.role_manage.table.reflush'] = 'Reflush', + + ['dashboard.menu.role_manage.table.options.confirm'] = 'Confirm Delete This Role?', + + ['dashboard.menu.role_manage.table.options.add'] = 'Add Role', + ['dashboard.menu.role_manage.table.options.edit'] = 'Edit Role', + ['dashboard.menu.role_manage.table.options.delete'] = 'Delete Role', + +} diff --git a/lualib/admin/locales/ZH-CN.lua b/lualib/admin/locales/ZH-CN.lua new file mode 100644 index 00000000..88c4f90b --- /dev/null +++ b/lualib/admin/locales/ZH-CN.lua @@ -0,0 +1,178 @@ +return { + -- 错误页 + ['error.404.title'] = 'cfadmin-错误页', + ['error.404.message'] = "页面或数据无法找到, 请联系管理员. ^_^.", + + -- 登录页 + ['login.form.title'] = 'cfadmin 后台管理系统', + ['login.form.username'] = '请输入用户名', + ['login.form.password'] = '请输入用户密码', + ['login.form.submit'] = '登录', + + -- 主页 + ['dashboard.header.logo'] = 'cfadmin 后台管理系统', + ['dashboard.header.title'] = 'cfadmin 后台管理系统', + ['dashboard.header.user.profile'] = '个人中心', + ['dashboard.header.user.quit'] = '注销', + + ['dashboard.header.langs'] = '选择语言', + ['dashboard.header.langs.us-english'] = '英语-美国', + ['dashboard.header.langs.simplified-chinese'] = '简体-中文', + + ['dashboard.header.profile.update_userinfo'] = '更新用户信息', + ['dashboard.header.profile.form.name'] = '用户名称', + ['dashboard.header.profile.form.name.notice'] = '用户显示名称', + ['dashboard.header.profile.form.username'] = '用户账户', + ['dashboard.header.profile.form.username.notice'] = '用户登录账户', + ['dashboard.header.profile.form.email'] = '邮箱', + ['dashboard.header.profile.form.phone'] = '手机', + + ['dashboard.header.profile.update_password'] = '修改用户密码', + ['dashboard.header.profile.form.cupassword'] = '当前密码', + ['dashboard.header.profile.form.password'] = '新密码', + + -- 面包屑 + ['dashboard.crumbs.home'] = '仪表盘', + ['dashboard.crumbs.close_all'] = '关闭所有页面', + ['dashboard.crumbs.close_this'] = '关闭当前页面', + ['dashboard.crumbs.close_other'] = '关闭其它页面', + + -- 菜单栏 + ['dashboard.menu.home'] = '仪表盘', + ['dashboard.menu.system'] = '系统管理', + ['dashboard.menu.system.user'] = '用户管理', + ['dashboard.menu.system.menu'] = '菜单栏管理', + ['dashboard.menu.system.header'] = '导航栏管理', + ['dashboard.menu.system.role'] = '角色管理', + + -- 用户管理 + ['dashboard.menu.user_manage.title'] = '用户管理', + ['dashboard.menu.user_manage.table.uid'] = '用户ID', + ['dashboard.menu.user_manage.table.name'] = '用户名称', + ['dashboard.menu.user_manage.table.username'] = '用户账户', + ['dashboard.menu.user_manage.table.permission_name'] = '角色', + ['dashboard.menu.user_manage.table.phone'] = '用户手机', + ['dashboard.menu.user_manage.table.email'] = '用户邮箱', + ['dashboard.menu.user_manage.table.create_time'] = '创建时间', + ['dashboard.menu.user_manage.table.update_time'] = '修改时间', + ['dashboard.menu.user_manage.table.options'] = '操作', + + ['dashboard.menu.user_manage.table.reflush'] = '刷新', + ['dashboard.menu.user_manage.table.adduser'] = '添加用户', + ['dashboard.menu.user_manage.table.edituser'] = '编辑用户', + ['dashboard.menu.user_manage.table.true'] = '是', + ['dashboard.menu.user_manage.table.false'] = '否', + + ['dashboard.menu.user_manage.table.options.edit'] = '编辑', + ['dashboard.menu.user_manage.table.options.delete'] = '删除', + + ['dashboard.menu.user_manage.table.options.delete.confirm'] = '确认要删除么?', + ['dashboard.menu.user_manage.table.options.delete.confirm.success'] = '删除成功', + + -- 用户新增页面 + ['dashboard.menu.user_manage.form.user_add.name'] = '用户名称', + ['dashboard.menu.user_manage.form.user_add.name.notice'] = '用户显示名称', + ['dashboard.menu.user_manage.form.user_add.username'] = '用户账户', + ['dashboard.menu.user_manage.form.user_add.username.notice'] = '用户登录所用账户', + ['dashboard.menu.user_manage.form.user_add.password'] = '密码', + ['dashboard.menu.user_manage.form.user_add.password.notice'] = '密码必须在6~20位', + ['dashboard.menu.user_manage.form.user_add.role'] = '角色', + ['dashboard.menu.user_manage.form.user_add.phone'] = '手机', + ['dashboard.menu.user_manage.form.user_add.email'] = '邮箱', + + -- 用户编辑页面 + ['dashboard.menu.user_manage.form.user_edit.name'] = '用户名称', + ['dashboard.menu.user_manage.form.user_edit.name.notice'] = '用户显示名称', + ['dashboard.menu.user_manage.form.user_edit.username'] = '用户账户', + ['dashboard.menu.user_manage.form.user_edit.username.notice'] = '用户登录所用账户', + ['dashboard.menu.user_manage.form.user_edit.password'] = '密码', + ['dashboard.menu.user_manage.form.user_edit.password.notice'] = '密码必须在6~20位', + ['dashboard.menu.user_manage.form.user_edit.role'] = '角色', + ['dashboard.menu.user_manage.form.user_edit.phone'] = '手机', + ['dashboard.menu.user_manage.form.user_edit.email'] = '邮箱', + + ['dashboard.menu.user_manage.form.submit'] = '提交', + + -- 菜单管理 + ['dashboard.menu.menu_manage.title'] = '菜单管理', + ['dashboard.menu.menu_manage.table.id'] = 'ID', + ['dashboard.menu.menu_manage.table.name'] = '名称', + ['dashboard.menu.menu_manage.table.url'] = '链接', + ['dashboard.menu.menu_manage.table.icon'] = '图标', + ['dashboard.menu.menu_manage.table.options'] = '操作', + + ['dashboard.menu.menu_manage.table.options.add'] = '添加菜单', + ['dashboard.menu.menu_manage.table.options.edit'] = '编辑菜单', + ['dashboard.menu.menu_manage.table.options.delete'] = '删除菜单', + ['dashboard.menu.menu_manage.table.options.reflush'] = '刷新', + ['dashboard.menu.menu_manage.table.options.confirm'] = '确定要删除么?', + + ['dashboard.menu.menu_manage.menu_add.form.name'] = '菜单名称', + ['dashboard.menu.menu_manage.menu_add.form.name.notice'] = '菜单显示的名称', + ['dashboard.menu.menu_manage.menu_add.form.url'] = '菜单链接', + ['dashboard.menu.menu_manage.menu_add.form.url.notice'] = "菜单打开的链接", + ['dashboard.menu.menu_manage.menu_add.form.icon'] = '菜单图标', + ['dashboard.menu.menu_manage.menu_add.form.icon.notice'] = '菜单左侧图标', + + ['dashboard.menu.menu_manage.menu_add.form.submit'] = '提交', + + ['dashboard.menu.menu_manage.menu_edit.form.name'] = '菜单名称', + ['dashboard.menu.menu_manage.menu_edit.form.name.notice'] = '菜单显示的名称', + ['dashboard.menu.menu_manage.menu_edit.form.url'] = '菜单链接', + ['dashboard.menu.menu_manage.menu_edit.form.url.notice'] = "菜单打开的链接", + ['dashboard.menu.menu_manage.menu_edit.form.icon'] = '菜单图标', + ['dashboard.menu.menu_manage.menu_edit.form.icon.notice'] = '菜单左侧图标', + + ['dashboard.menu.menu_manage.menu_edit.form.submit'] = '提交', + + -- 导航管理 + ['dashboard.menu.header_manage.title'] = '导航管理', + -- ['dashboard.menu.header_manage.table.id'] = '导航ID', + ['dashboard.menu.header_manage.table.name'] = '导航名称', + ['dashboard.menu.header_manage.table.url'] = '导航链接', + ['dashboard.menu.header_manage.table.create_time'] = '创建时间', + ['dashboard.menu.header_manage.table.update_time'] = '修改时间', + ['dashboard.menu.header_manage.table.options'] = '操作', + + ['dashboard.menu.header_manage.table.reflush'] = '刷新', + ['dashboard.menu.header_manage.table.addnavi'] = '添加导航', + ['dashboard.menu.header_manage.table.editnavi'] = '编辑导航', + ['dashboard.menu.header_manage.table.true'] = '是', + ['dashboard.menu.header_manage.table.false'] = '否', + + ['dashboard.menu.header_manage.table.options.edit'] = '编辑', + ['dashboard.menu.header_manage.table.options.delete'] = '删除', + + ['dashboard.menu.header_manage.table.options.delete.confirm'] = '确认要删除么?', + ['dashboard.menu.header_manage.table.options.delete.confirm.success'] = '删除成功', + + -- 导航新增页面 + ['dashboard.menu.header_manage.form.header_add.name'] = '导航名称', + ['dashboard.menu.header_manage.form.header_add.name.notice'] = '导航名称为必填项', + ['dashboard.menu.header_manage.form.header_add.url'] = '导航链接', + ['dashboard.menu.header_manage.form.header_add.url.notice'] = '导航链接为必填项', + + -- 导航编辑页面 + ['dashboard.menu.header_manage.form.header_edit.name'] = '导航名称', + ['dashboard.menu.header_manage.form.header_edit.name.notice'] = '导航名称为必填项', + ['dashboard.menu.header_manage.form.header_edit.url'] = '导航链接', + ['dashboard.menu.header_manage.form.header_edit.url.notice'] = '导航链接为必填项', + + ['dashboard.menu.header_manage.form.submit'] = '提交', + + -- 角色管理 + ['dashboard.menu.role_manage.title'] = '角色管理', + ['dashboard.menu.role_manage.table.name'] = '角色名称', + ['dashboard.menu.role_manage.table.create_time'] = '创建时间', + ['dashboard.menu.role_manage.table.update_time'] = '修改时间', + ['dashboard.menu.role_manage.table.options'] = '操作', + + ['dashboard.menu.role_manage.table.reflush'] = '刷新', + + ['dashboard.menu.role_manage.table.options.confirm'] = '确定要删除该角色吗?', + + ['dashboard.menu.role_manage.table.options.add'] = '添加角色', + ['dashboard.menu.role_manage.table.options.edit'] = '编辑角色', + ['dashboard.menu.role_manage.table.options.delete'] = '删除角色', + +} diff --git a/lualib/admin/locales/init.lua b/lualib/admin/locales/init.lua new file mode 100644 index 00000000..d81c54e3 --- /dev/null +++ b/lualib/admin/locales/init.lua @@ -0,0 +1,4 @@ +return { + ["ZH-CN"] = require "admin.locales.ZH-CN", -- 中文 + ["EN-US"] = require "admin.locales.EN-US", -- 英文 +} diff --git a/lualib/admin/token.lua b/lualib/admin/token.lua new file mode 100644 index 00000000..9103a152 --- /dev/null +++ b/lualib/admin/token.lua @@ -0,0 +1,36 @@ +local config = require 'admin.config' + +local crypt = require 'crypt' +local xor_str = crypt.xor_str +local hexencode = crypt.hexencode +local hexdecode = crypt.hexdecode + +local sys = require "sys" +local now = sys.now +local match = string.match +local concat = table.concat + +local token = {} + +-- token加密与序列化 +function token.encode (str) + return hexencode(xor_str(str, config.secure)):upper() +end + +-- token解密与反序列化 +function token.decode (token) + return xor_str(hexdecode(token:lower()), config.secure) +end + +-- 生成 token +function token.generate (uid) + return token.encode(concat({uid, now()}, ':')) +end + +-- 解析token +function token.parser (token) + local str = token.decode(token) + return match(str, '([%d]+):([%d]+)') +end + +return token diff --git a/lualib/admin/utils/init.lua b/lualib/admin/utils/init.lua new file mode 100644 index 00000000..4bd8f58d --- /dev/null +++ b/lualib/admin/utils/init.lua @@ -0,0 +1,82 @@ +local crypt = require "crypt" +local template = require "template" +local config = require "admin.config" + +local type = type +local assert = assert +local ipairs = ipairs +local tonumber = tonumber + +local io_open = io.open +local split = string.sub +local find = string.find +local concat = table.concat + + +local utils = {} + +-- 页面重定向 +function utils.redirect(path, args) + assert(path ~= '' or type(path) ~= 'string' , '试图传递一个非法的path') + assert(not args or type(args) == 'table' , '试图传递一个非法的args') + local tab, arguments = { }, nil + if args then + for _, arg in ipairs(args) do + tab[#tab+1] = concat(arg, '=') + end + end + local arguments = concat(tab, '&') + return template.compile('lualib/admin/html/redirect.html'){ + path = (path or config.github) .. arguments, + } +end + +-- 404错误页 +function utils.error_404(location) + return template.compile('lualib/admin/html/404.html'){ + cdn = config.cdn, + locale = config.locales[config.locale], + location = location or config.github, + } +end + +-- 获取页面url +function utils.get_path (content) + local path = content['path'] + local _, Pos = find(path, '?') + if Pos then + path = split(path, 1, Pos - 1) + end + return path +end + +-- 获取语言 +function utils.get_locale (lang) + local locale = config.locales[lang] + if locale then + return locale + end + return config.locales[config.locale] +end + +-- 用户是否包含此权限 +function utils.user_have_permission (permissions, id) + for _, permission in ipairs(permissions) do + if permission.menu_id == id then + return true + end + end + return false +end + +-- 角色权限组是否已经有此权限 +function utils.role_already_selected (permissions, id) + for _, permission in ipairs(permissions) do + if permission.menu_id == id then + return true + end + end + return false +end + +return utils diff --git a/lualib/admin/view.lua b/lualib/admin/view.lua new file mode 100644 index 00000000..6b0ef13d --- /dev/null +++ b/lualib/admin/view.lua @@ -0,0 +1,40 @@ +local template = require "template" +local utils = require "admin.utils" +local config = require "admin.config" +local user = require "admin.db.user" +local user_token = require "admin.db.token" +local permission = require "admin.db.permission" + + +local get_path = utils.get_path + +-- 用户自定义view页面需要验权 +local function verify_permission (content, db) + local token = Cookie.getCookie("CFTOKEN") + if not token then + return false, config.login_render + end + local exists = user_token.token_exists(db, token) + if not exists then -- Token不存在需要重新登录 + return false, config.login_render + end + local info = user.user_info(db, exists.uid) + if not info or info.is_admin ~= 1 or not permission.user_have_menu_permission(db, info.id, get_path(content)) + return false, config.login_render + end + return true +end + +local view = {} + +-- 渲染模板 +function view.render (content, path) + local db = config.db + local ok, page = verify_permission(content, db) + if not ok then + return utils.redirect(page) + end + return template.compile(path) +end + +return view diff --git a/script/test_cfadmin.lua b/script/test_cfadmin.lua new file mode 100644 index 00000000..62d5098a --- /dev/null +++ b/script/test_cfadmin.lua @@ -0,0 +1,81 @@ +local httpd = require "httpd" +local http = require "httpd.http" +local DB = require "DB" + +--[[ +请按照以下步奏初始化后台: + 1. 创建一个数据库(名字任意); + 2. 请手动打开lualib/db/database.sql文件, 复制里面的SQL语句在GUI工具中执行一次; + 3. 执行完成之后, 将您填写的数据库替换database字段, 并且charset需要设置一致. +]] + +local db = DB:new({ + host = 'localhost', + port = 3306, + username = 'root', + password = '123456789', + charset = 'utf8', + database = 'cfadmin', +}) + +db:connect() + +-- 导入httpd对象 +local app = httpd:new("App") +-- httpd启用Cookie扩展 +app:enable_cookie() +-- httpd设置Cookie加密的密匙 +app:cookie_secure("https://github.com/CandyMi/core_framework") +-- app:cookie_secure("candymi") + +-- 导入cf内置的admin库 +local cfadmin = require "admin" + +-- 注册后台页面路由 +cfadmin.init_page(app, db) + + +-- 这个函数仅在第一次初始化数据的时候适用 +-- 初始化完成之后, 请不要再运行. +cfadmin.init_db() + +-- 这里设置首页的显示的页面 +-- cfadmin.init_home(location or domain + path) +-- cfadmin.init_home('https://www.baidu.com') + + + +-- 这里是设置语言的地方 +-- 语言表在admin/locales内, 可参照key -> value进行填写. +-- 传入一个数组表: 索引1是key, 索引2为显示内容. + +-- cfadmin.add_locale_item('ZH-CN', { +-- {'login.form.title', '这是登录页Title'}, +-- {'dashboard.header.logo', '仪表盘 Logo'} +-- }) + +-- cfadmin.add_locale_item('EN-US', { +-- {'login.form.title', 'This is Login Page Title'}, +-- {'dashboard.header.logo', 'dashboard Logo'} +-- }) + +-- 开启页面缓存能显著提升页面渲染性能. 生产环境下建议开启. +-- 也因为cf缓存模板页面内容, 所以开发模式下不建议开启. +-- cfadmin.cached() + +-- 这个方法可以用来设置静态文件域名与前缀. +-- 如果静态文件在其它域名或者无法访问, 可以使用这个参数修改.(域名后必须加上'/') +-- cfadmin.static('http://10.0.0.16:8080/') + +-- 设置cfadmin的区域语言, 默认为: ZH-CN +-- cfadmin.set_locale('EN-US') + +-- 设置客户端静态文件ttl值内无需再次请求, 减少服务端消耗 +-- app:static('static', 30) +app:static('static') + +-- httpd监听端口 +app:listen("0.0.0.0", 8080) + +-- 运行 +app:run() diff --git a/static/layui_ext/dist/dtree.js b/static/layui_ext/dist/dtree.js new file mode 100755 index 00000000..390ecdc9 --- /dev/null +++ b/static/layui_ext/dist/dtree.js @@ -0,0 +1 @@ +layui.define(["jquery","layer","form"],function(e){var f=layui.$,b=layui.layer,m=layui.form,y="dtree-nav-ul-sid",v="dtree-nav-item",k="dtree-nav-div",u="dtreefont",g="dtreefont-special",i="dtree-icon-move-down",o="dtree-icon-move-up",n="dtree-icon-refresh",s="dtree-icon-delete1",r="dtree-icon-search_list_light",t="dtree-toolbar",C="dtree-toolbar-tool",j="dtree-icon-dian",I="dtree-nav-checkbox-div",N="dtree-icon-fuxuankuangxuanzhong",T="dtree-icon-fuxuankuang",x="dtree-icon-fuxuankuang-banxuan",P="d-click-checkbar",c="dtree",a="dtree-",d="-item-this",l="-item",h="-dtreefont",p="-ficon",q="-icon",D="-checkbox",S="-choose",A="dtree-nav-this",L="dtree-nav-show",O="dtree-icon-hide",_=f("body"),F="dtree",w={},R={"-1":{open:"dtree-icon-null-open",close:"dtree-icon-null-close"},0:{open:"dtree-icon-jian",close:"dtree-icon-jia"},1:{open:"dtree-icon-xiangxia1",close:"dtree-icon-xiangyou"}},B={"-1":{open:"dtree-icon-null-open",close:"dtree-icon-null-close"},0:{open:"dtree-icon-wenjianjiazhankai",close:"dtree-icon-weibiaoti5"}},M={"-1":"dtree-icon-null",0:"dtree-icon-weibiaoti5",1:"dtree-icon-yonghu",2:"dtree-icon-fenzhijigou",3:"dtree-icon-fenguangbaobiao",4:"dtree-icon-xinxipilu",5:"dtree-icon-shuye1",6:"dtree-icon-caidan_xunzhang",7:"dtree-icon-normal-file"},E="checkNodeClick",U="itemNodeClick",J="addToolbar",z="editToolbar",G="delToolbar",$="moveDown",H="moveUp",X="refresh",K="remove",Q="searchNode",V={getElemId:function(e){var t=e.elem||"",a=e.obj||f(t);return 0==a.length?"":f(a)[0].id},escape:function(e){return"string"!=typeof e?"":e.replace(Z.escape,function(e){return Y.escape[e]})},unescape:function(e){return"string"!=typeof e?"":e.replace(Z.unescape,function(e){return Y.unescape[e]})},cloneObj:function(e,t){var a={};e instanceof Array&&(a=[]);var i="";for(var o in void 0!==t&&(i=t.join(",")),e)if(-1==i.indexOf(o)){var n=e[o];a[o]="object"==typeof n?V.cloneObj(n,void 0!==typeof t?t:[]):n}return a}},W=Object.keys||function(e){e=Object(e);var t=[];for(var a in e)t.push(a);return t},Y={escape:{"&":"&","<":"<",">":">","'":"&quo;"}};Y.unescape=function(e){e=Object(e);var t={};for(var a in e)t[e[a]]=a;return t}(Y.escape);var Z={escape:RegExp("["+W(Y.escape).join("")+"]","g"),unescape:RegExp("("+W(Y.unescape).join("|")+")","g")},ee=function(i){var e=i.data?i.data:{},t="boolean"!=typeof i.async||i.async;f.ajax({type:i.type?i.type:"POST",headers:i.headers,url:i.url,dataType:i.dataType?i.dataType:"json",data:e,async:t,success:i.success,error:function(e,t,a){"function"==typeof i.error?i.error():b.msg("系统异常导致操作失败, 请联系管理员。",{icon:5,shift:6})},statusCode:{404:function(){b.msg("未找到指定请求,请检查访问路径!",{icon:5,shift:6})},500:function(){b.msg("系统错误,请联系管理员。",{icon:5,shift:6})}},complete:function(e,t){"function"==typeof i.complete&&i.complete(e,t)}})},te=function(e){var t="?";for(var a in e)t+=a+"="+e[a]+"&";return t=t.substring(0,t.length-1)},ae=function(e){this.response={statusName:"code",statusCode:200,message:"message",rootName:"data",treeId:"id",parentId:"parentId",title:"title",iconClass:"iconClass",childName:"children",isLast:"isLast",spread:"spread",disabled:"disabled",checkArr:"checkArr",isChecked:"isChecked",type:"type",basicData:"basicData"},this.defaultRequest={nodeId:"nodeId",parentId:"parentId",context:"context",isLeaf:"isLeaf",level:"level",spread:"spread",dataType:"dataType",ischecked:"ischecked",initchecked:"initchecked",basicData:"basicData",recordData:"recordData"},this.toolbarFun={addTreeNode:function(e,t){},editTreeNode:function(e,t){},editTreeLoad:function(e){},delTreeNode:function(e,t){},loadToolbarBefore:function(e,t,a){return e}},this.toolbarStyle={title:"节点",area:["60%","80%"]},this.menubarFun={remove:function(e){return!0}},this.menubarTips={toolbar:[],group:[$,H,X,K,Q],freedom:[]},this.checkbarFun={chooseBefore:function(e,t){return!0},chooseDone:function(e){}},this.iframe={iframeElem:"",iframeUrl:"",iframeLoad:"leaf",iframeDefaultRequest:{nodeId:"nodeId",parentId:"parentId",context:"context",isLeaf:"isLeaf",level:"level",spread:"spread",dataType:"dataType",ischecked:"ischecked",initchecked:"initchecked",basicData:"basicData",recordData:"recordData"},iframeRequest:{}},this.iframeFun={iframeDone:function(e){}},this.style={item:"",itemThis:"",dfont:"",icon:"",cbox:"",chs:""},this.node={nodeId:"",parentId:"",context:"",isLeaf:"",level:"",spread:"",dataType:"",ischecked:"",initchecked:"",basicData:"",recordData:""},this.toolbarMenu={},this.checkbarNode=[],this.checkArrLen=0,this.temp=[],this.setting(e)};ae.prototype.setting=function(e){this.options=e||{},this.elem=this.options.elem||"",this.obj=this.options.obj||f(this.elem),this.initLevel=this.options.initLevel||2,this.type=this.options.type||"load",this.cache="boolean"!=typeof this.options.cache||this.options.cache,this.record="boolean"==typeof this.options.record&&this.options.record,this.load="boolean"!=typeof this.options.load||this.options.load,this.firstIconArray=f.extend(R,this.options.firstIconArray)||R,this.nodeIconArray=f.extend(B,this.options.nodeIconArray)||B,this.leafIconArray=f.extend(M,this.options.leafIconArray)||M,this.skin=this.options.skin||"theme","layui"==this.skin?(this.ficon=this.options.ficon||"1",this.dot="boolean"==typeof this.options.dot&&this.options.dot,this.icon=this.options.icon||"7",this.nodeIcon="string"==typeof this.icon||"number"==typeof this.icon?(this.icon,"-1"):this.icon[0]):(this.ficon=this.options.ficon||"0",this.dot="boolean"!=typeof this.options.dot||this.options.dot,this.icon=this.options.icon||"5",this.nodeIcon="string"==typeof this.icon||"number"==typeof this.icon?"-1"==this.icon?"-1":"0":this.icon[0]),this.ficonOpen=this.firstIconArray[this.ficon].open,this.ficonClose=this.firstIconArray[this.ficon].close,this.nodeIconOpen=this.nodeIconArray[this.nodeIcon].open,this.nodeIconClose=this.nodeIconArray[this.nodeIcon].close,this.leafIcon="string"==typeof this.icon||"number"==typeof this.icon?this.icon:this.icon[1],this.leafIconShow=this.leafIconArray[this.leafIcon],this.style.item=a+this.skin+l,this.style.itemThis=a+this.skin+d,this.style.dfont=a+this.skin+h,this.style.ficon=a+this.skin+p,this.style.icon=a+this.skin+q,this.style.cbox=a+this.skin+D,this.style.chs=a+this.skin+S,this.url=this.options.url||"",this.async="boolean"!=typeof this.options.async||this.options.async,this.headers=this.options.headers||{},this.method=this.options.method||"post",this.dataType=this.options.dataType||"json",this.defaultRequest=f.extend(this.defaultRequest,this.options.defaultRequest)||this.defaultRequest,this.filterRequest=this.options.filterRequest||[],this.request=this.options.request||{},this.response=f.extend(this.response,this.options.response)||this.response,this.data=this.options.data||null,this.dataFormat=this.options.dataFormat||"levelRelationship",this.dataStyle=this.options.dataStyle||"defaultStyle",this.success=this.options.success||function(e,t){},this.done=this.options.done||function(e,t){},this.toolbar=this.options.toolbar||!1,this.toolbarStyle=f.extend(this.toolbarStyle,this.options.toolbarStyle)||this.toolbarStyle,this.toolbarScroll=this.options.toolbarScroll||this.elem,this.toolbarLoad=this.options.toolbarLoad||"node",this.toolbarShow=this.options.toolbarShow||["add","edit","delete"],this.toolbarBtn=this.options.toolbarBtn||null,this.toolbarExt=this.options.toolbarExt||[],this.toolbarFun=f.extend(this.toolbarFun,this.options.toolbarFun)||this.toolbarFun,this.menubar=this.options.menubar||!1,this.menubarTips=f.extend(this.menubarTips,this.options.menubarTips)||this.menubarTips,this.menubarFun=f.extend(this.menubarFun,this.options.menubarFun)||this.menubarFun,this.checkbar=this.options.checkbar||!1,this.checkbarLoad=this.options.checkbarLoad||"node",this.checkbarType=this.options.checkbarType||"all",this.checkbarData=this.options.checkbarData||"choose",this.checkbarFun=f.extend(this.checkbarFun,this.options.checkbarFun)||this.checkbarFun,this.useIframe=this.options.useIframe||!1,this.iframe=f.extend(this.iframe,this.options.iframe)||this.iframe,this.iframeFun=f.extend(this.iframeFun,this.options.iframeFun)||this.iframeFun},ae.prototype.reloadSetting=function(e){this.options=f.extend(this.options,e)||this.options,this.elem=this.options.elem||this.elem,void 0===this.options.obj?this.elem&&0i.checkArrLen&&(i.checkArrLen=e.length),e},children:function(){return a[i.response.childName]||[]},basicData:function(){return V.escape(JSON.stringify(a[i.response.basicData]))||JSON.stringify({})},recordData:function(){var e=i.record?V.cloneObj(a,[i.response.basicData,i.response.childName]):{};return V.escape(JSON.stringify(e))},data:function(){return V.escape(JSON.stringify(a))}}},ae.prototype.getDom=function(s,e,t,r,n,d,a,l,i){var c=this,h=c.obj[0].id,p=(c.toolbar,c.checkbar);return{fnode:function(){var e=c.ficon,t=c.ficonOpen,a=c.ficonClose,i=c.dot;return"-1"!=e&&i?r?"":l?"":"":"-1"==e||i?"-1"==e&&i?r?"":l?"":"":"-1"!=e||i?void 0:r?"":l?"":"":r?"":l?"":""},node:function(){var e=c.nodeIcon,t=c.leafIcon,a=c.leafIconShow,i=c.nodeIconOpen,o=c.nodeIconClose;return n&&(o=i=a=n),"-1"!=e&&"-1"!=t?r?"":l?"":"":"-1"!=e&&"-1"==t?r?"":l?"":"":"-1"==e&&"-1"!=t?r?"":l?"":"":"-1"==e&&"-1"==t?r?"":l?"":"":void 0},checkbox:function(){var e=!1;if("node"==c.checkbarLoad?p&&(e=!0):r&&p&&(e=!0),e){var t="
      ";if(d&&0"}return t+="
      "}return""},text:function(){return""+t+""},ul:function(){return r?"
        ":l?"
          ":"
            "}}},ae.prototype.getLiItemDom=function(e,t,a,i,o,n,s,r,d,l,c,h){var p=this,u=p.obj[0].id,f=p.getDom(e,t,a,i,o,n,s,r,d);l="{}"==l?"":l,c="{}"==c?"":c;var b="
            "),"noleaf"==p.toolbarLoad&&(b+=i?" d-contextmenu='false'>":" d-contextmenu='true'>"),"leaf"==p.toolbarLoad&&(b+=i?" d-contextmenu='true'>":" d-contextmenu='false'>")):b+=" d-contextmenu='false'>",["
          • "+b,f.fnode(),f.node(),f.checkbox(),f.text(),"
          • ",f.ul(),""].join("")},ae.prototype.dataInit=function(e){var t=this,a=t.obj.find("div[data-id='"+e+"']");a.parent().find("."+A).removeClass(A),a.parent().find("."+t.style.itemThis).removeClass(t.style.itemThis),a.addClass(A),a.addClass(t.style.itemThis),t.setNodeParam(a);var i=a.parents("."+v);return i.children("ul").addClass(L),i.children("."+k).children("i[data-spread]."+t.ficonClose).addClass(t.ficonOpen),i.children("."+k).children("i[data-spread]."+t.ficonClose).removeClass(t.ficonClose),i.children("."+k).children("i[data-spread]."+t.nodeIconClose).addClass(t.nodeIconOpen),i.children("."+k).children("i[data-spread]."+t.nodeIconClose).removeClass(t.nodeIconClose),t.getNowParam()},ae.prototype.clickSpread=function(e){var t=e.find("i[data-spread]").eq(0),a=e.find("i[data-spread]").eq(1),i=a.attr("class"),o=(e.find("cite[data-leaf]").eq(0),t.attr("data-spread")),n=e.next("ul"),s=this;if(0
            "),e.toolbar&&e.obj.before("
            ")},ae.prototype.openTreePlus=function(){var e=this,t=[];if(e.toolbar&&e.getToolbarDom(),e.menubar){var a=e.menubarTips,i=a.toolbar,o=a.group;a.freedom;if(i&&0";break;case H:a="";break;case X:a="";break;case K:a=this.checkbar?"":"";break;case Q:a=""}return a},ae.prototype.getExtMenubarDom=function(e){return""},ae.prototype.getMenubarToolDom=function(e){var t=this,a=t.obj[0].id;switch(e){case $:t.toolbarMenu[$]="
             展开"+t.toolbarStyle.title+"
            ";break;case H:t.toolbarMenu[H]="
             收缩"+t.toolbarStyle.title+"
            ";break;case X:t.toolbarMenu[X]="
             刷新
            ";break;case K:t.checkbar&&(t.toolbarMenu[K]="
             删除选中"+t.toolbarStyle.title+"
            ");break;case Q:t.toolbarMenu[Q]="
             查询"+t.toolbarStyle.title+"
            "}},ae.prototype.getExtMenubarToolDom=function(e){this.toolbarMenu[e.menubarId]="
             "+e.title+"
            "},ae.prototype.menubarMethod=function(){var p=this;return{openAllNode:function(e){for(var t=e||p.obj.children("li").children("ul"),a=0;a 新增"+e.toolbarStyle.title+""),"edit"==o&&(e.toolbarMenu[z]="
             编辑"+e.toolbarStyle.title+"
            "),"delete"==o&&(e.toolbarMenu[G]="
             删除"+e.toolbarStyle.title+"
            ")}if(0 "+n.title+""}},ae.prototype.setToolbarDom=function(e){if(e)for(var t in this.obj.prevAll("div#dtree_toolbar_"+this.obj[0].id).find("div.layui-nav-item>dl.layui-nav-child").html(""),e)this.obj.prevAll("div#dtree_toolbar_"+this.obj[0].id).find("div.layui-nav-item>dl.layui-nav-child").append(e[t])},ae.prototype.loadToolBar=function(e,t){var a=this,i=(a.toolbarShow,a.toolbarBtn),o="";switch(t){case J:var n=['
            ','','
            ','',"
            ","
            "].join(""),s=['
            ','",'
            ','',"
            ","
            "].join(""),r=['
            ','
            ','',"
            ","
            "].join(""),d=['
            ',n,s];if(null!=i&&0
            "),o=d.join("");break;case z:n=['
            ','','
            ','',"
            ","
            "].join("");var h=['
            ','",'
            ','',"
            ","
            "].join(""),p=['
            ','
            ','',"
            ","
            "].join(""),u=['
            ',n,h];if(null!=i&&0
            "),o=u.join("")}return o},ae.prototype.loadToolBarDetail=function(){return{text:function(e){return['
            ','",'
            ','',"
            ","
            "].join("")},textarea:function(e){return['
            ','",'
            ','","
            ","
            "].join("")},hidden:function(e){return[''].join("")},select:function(e){var t=e.optionsData,a="",i=e.value?e.value:"";for(var o in t)i==t[o]?a+="":a+="";return['
            ','",'
            ','","
            ","
            "].join("")}}},ae.prototype.changeTreeNodeAdd=function(e){var t=this,a=t.temp,i=a[0],o=a[1],n=a[2],s=a[3];if(e){var r=t.obj.find("[data-id='"+i+"']");if("object"==typeof e){r.remove();var d=t.parseData(e);if(!d.treeId())return b.msg("添加失败,节点ID为undefined!",{icon:5}),o.find("li[data-id='"+i+"']").remove(),t.setNodeParam(n),void(t.temp=[]);o.append(t.getLiItemDom(d.treeId(),d.parentId(),d.title(),d.isLast(0),d.iconClass(),d.checkArr(),s,d.spread(),d.disabled(),d.basicData(),d.recordData(),"item"));var l=o.find("div[data-id='"+e.id+"']");t.setNodeParam(l)}else if("string"==typeof e||"number"==typeof this.icon){r.attr("data-id",e),o.find("li[data-id='"+e+"']").show();l=o.find("div[data-id='"+e+"']");t.setNodeParam(l)}var c=n.find("i[data-spread]");"last"==c.eq(0).attr("data-spread")?(c.attr("data-spread","open"),c.eq(0).removeClass(j),c.eq(0).removeClass(O),c.eq(0).addClass(t.ficonOpen),c.eq(1).removeClass(M[t.leafIcon])):(c.attr("data-spread","open"),c.eq(0).removeClass(t.ficonClose),c.eq(0).addClass(t.ficonOpen),c.eq(1).removeClass(t.nodeIconClose)),c.eq(1).addClass(t.nodeIconOpen),o.addClass(L)}else o.find("li[data-id='"+i+"']").remove(),t.setNodeParam(n);t.temp=[]},ae.prototype.changeTreeNodeEdit=function(e){var t=this.temp,a=t[0],i=t[1];e||(a.html(title),node=this.getNodeParam(i)),this.temp=[]},ae.prototype.changeTreeNodeDone=function(e){m.val("dtree_editNode_form",e),m.render()},ae.prototype.changeTreeNodeDel=function(e){var t=this,a=t.temp,i=a[0],o=i.parent("ul"),n=a[1];if(e){if(i.remove(),0==o.children("li").length){var s=n.find("i[data-spread]");s.attr("data-spread","last"),s.eq(0).removeClass(t.ficonOpen),s.eq(0).removeClass(t.ficonClose),t.dot||s.eq(0).addClass(O),s.eq(0).addClass(j),s.eq(1).removeClass(t.nodeIconOpen),s.eq(1).removeClass(t.nodeIconClose),s.eq(1).addClass(M[t.leafIcon])}t.initNodeParam()}t.temp=[]},ae.prototype.chooseDataInit=function(e){for(var t=this,a=e.split(","),i=0;i."+k+">."+I+">i[data-type='"+i+"']");t.checkStatus(r).noCheck();for(var d=1,l=n;d."+y+" ."+I+">i[data-type='"+i+"'][data-checked='1']").length){var c=l.eq(d).find(">."+k+">."+I+">i[data-type='"+i+"']");t.checkStatus(c).noCheck()}}}else{t.checkStatus(e).check();r=s.find(">."+k+">."+I+">i[data-type='"+i+"']");t.checkStatus(r).check();for(d=1,l=n;d."+k+">."+I+">i[data-type='"+i+"']");t.checkStatus(c).check()}}},ae.prototype.checkAllOrNoallOrNot=function(e){var t=this,a=(e.closest("."+k),e.attr("data-par")),i=e.attr("data-type"),o=e.closest(a),n=e.parents(a),s=o.find(a);if("1"==e.attr("data-checked")){t.checkStatus(e).noCheck();var r=s.find(">."+k+">."+I+">i[data-type='"+i+"']");t.checkStatus(r).noCheck();for(var d=1,l=n;d."+y+" ."+I+">i[data-type='"+i+"'][data-checked='1']").length,h=l.eq(d).find(">."+k+">."+I+">i[data-type='"+i+"']");0==c?t.checkStatus(h).noCheck():t.checkStatus(h).noallCheck()}}else{t.checkStatus(e).check();r=s.find(">."+k+">."+I+">i[data-type='"+i+"']");t.checkStatus(r).check();for(d=1,l=n;d."+y+" ."+I+">i[data-type='"+i+"'][data-checked='1']").length,u=l.eq(d).find(">."+y+" ."+I+">i[data-type='"+i+"']").length;h=l.eq(d).find(">."+k+">."+I+">i[data-type='"+i+"']");p!=u?t.checkStatus(h).noallCheck():t.checkStatus(h).check()}}},ae.prototype.checkAllOrPcascOrNot=function(e){e.closest("."+k);var t=e.attr("data-par"),a=e.attr("data-type"),i=e.closest(t),o=(e.parents(t),i.find(t));if("1"==e.attr("data-checked")){this.checkStatus(e).noCheck();var n=o.find(">."+k+">."+I+">i[data-type='"+a+"']");this.checkStatus(n).noCheck()}else{this.checkStatus(e).check();n=o.find(">."+k+">."+I+">i[data-type='"+a+"']");this.checkStatus(n).check()}},ae.prototype.checkOrNot=function(e){e.closest("."+k);var t=e.attr("data-par"),a=(e.attr("data-type"),e.closest(t));e.parents(t),a.find(t);"1"==e.attr("data-checked")?this.checkStatus(e).noCheck():this.checkStatus(e).check()},ae.prototype.checkOnly=function(e){e.closest("."+k);var t=e.attr("data-par"),a=(e.attr("data-type"),e.closest(t)),i=(e.parents(t),a.find(t),e.attr("data-checked")),o=this.obj.find("i[data-checked]");this.checkStatus(o).noCheck(),"1"!=i&&this.checkStatus(e).check()},ae.prototype.changeCheck=function(){var e=this,t=e.temp[0];"all"==e.checkbarType?e.checkAllOrNot(t):"no-all"==e.checkbarType?e.checkAllOrNoallOrNot(t):"p-casc"==e.checkbarType?e.checkAllOrPcascOrNot(t):"self"==e.checkbarType?e.checkOrNot(t):"only"==e.checkbarType?e.checkOnly(t):e.checkAllOrNot(t);var a=e.setAndGetCheckbarNodesParam();e.checkbarFun.chooseDone(a),layui.event.call(this,F,"chooseDone("+f(e.obj)[0].id+")",{checkbarParams:a}),e.temp=[]},ae.prototype.initNoAllCheck=function(){var e=this.obj.find("i[data-checked='1']");if(0."+y+" ."+I+">i[data-type='"+o+"'][data-checked='1']").length,c=d.eq(r).find(">."+y+" ."+I+">i[data-type='"+o+"']").length,h=d.eq(r).find(">."+k+">."+I+">i[data-type='"+o+"']");l!=c?this.checkStatus(h).noallCheck():this.checkStatus(h).check()}},ae.prototype.initAllCheck=function(){var e=this.obj.find("i[data-checked='1']");if(0."+k+">."+I+">i[data-type='"+o+"']");this.checkStatus(l).check()}},ae.prototype.checkStatus=function(e){var t=this;return{check:function(){e.removeClass(T),e.removeClass(x),e.addClass(N),e.addClass(t.style.chs),e.attr("data-checked","1")},noCheck:function(){e.removeClass(x),e.removeClass(N),e.removeClass(t.style.chs),e.addClass(T),e.attr("data-checked","0")},noallCheck:function(){e.removeClass(T),e.removeClass(N),e.addClass(x),e.addClass(t.style.chs),e.attr("data-checked","2")}}},ae.prototype.setAndGetCheckbarNodesParam=function(){var a=this;return a.checkbarNode=[],"change"==a.checkbarData?a.obj.find("i[data-par]").each(function(){var e=f(this),t=e.closest("."+k);e.attr("data-checked")!=e.attr("data-initchecked")&&a.checkbarNode.push(a.getRequestParam(a.getCheckbarNodeParam(t,e)))}):"all"==a.checkbarData?a.obj.find("i[data-par][data-checked]").each(function(){var e=f(this),t=e.closest("."+k);a.checkbarNode.push(a.getRequestParam(a.getCheckbarNodeParam(t,e)))}):a.obj.find("i[data-par][data-checked='1']").each(function(){var e=f(this),t=e.closest("."+k);a.checkbarNode.push(a.getRequestParam(a.getCheckbarNodeParam(t,e)))}),a.checkbarNode},ae.prototype.getCheckbarNodesParam=function(){return this.setAndGetCheckbarNodesParam()},ae.prototype.getCheckbarNodeParam=function(e,t){var a={};return a.nodeId=e.attr("data-id"),a.parentId=e.parent().attr("data-pid"),a.context=e.find("cite[data-leaf]").eq(0).text(),a.isLeaf="leaf"==e.find("cite[data-leaf]").eq(0).attr("data-leaf"),a.level=e.parent().attr("data-index"),a.spread="open"==e.find("i[data-spread]").eq(0).attr("data-spread"),a.basicData=e.attr("data-basic"),a.recordData=e.attr("data-record"),a.dataType=t.attr("data-type"),a.ischecked=t.attr("data-checked"),a.initchecked=t.attr("data-initchecked"),a},ae.prototype.changeCheckbarNodes=function(){var t=!1;return this.obj.find("i[data-par]").each(function(){var e=f(this);if($div=e.closest("."+k),e.attr("data-checked")!=e.attr("data-initchecked"))return t=!0}),t},ae.prototype.loadIframe=function(e,t){var a=e.find("cite[data-leaf]").eq(0);if(!this.useIframe)return!1;var i=this.iframe.iframeElem,o=this.iframe.iframeUrl,n="leaf"!=this.iframe.iframeLoad||"leaf"==a.attr("data-leaf");if(n){if(!(0i.dtreefont{display: inline-block;margin: 0px 1px;} +.dtree-nav-checkbox-div>i.dtreefont:last-child{margin-right: 4px;} +.dtree-nav-checkbox-div>i.dtreefont:hover{opacity:0.8;filter:Alpha(opacity=80);} + +/* 行 文字*/ +.dtree-nav-div{display:block;vertical-align:top;position:relative;cursor: pointer;} +.dtree-nav-div cite{font-style: normal;cursor: pointer;} +.dtree-nav-div:hover cite{opacity:0.7;filter:Alpha(opacity=70);transition: all .3s;-webkit-transition: all .3s;} + +/* 规则属性*/ +.dtree-nav-show {display: block!important;} +.dtree-nav-this {} +.dtree-icon-hide {opacity:0;filter:Alpha(opacity=0);} +.dtree-icon-null-open,.dtree-icon-null-close,.dtree-icon-null{margin: 0 2px;} + +/* 简单适配*/ +@media screen and (max-width:1700px) and (min-width:1300px){ + .dtree-nav-item {padding-left: 15px;} +} + +/**************** 主题换肤 ****************/ +/* 默认风格*/ +.dtree-theme-item-this{background-color: #d2d2d2!important;} +.dtree-theme-item:hover{background-color: #eaeceb!important;} +.dtree-theme-item cite{font-size:12px!important;} +.dtree-theme-item:hover cite{color:#fe7786!important;} + +.dtree-theme-dtreefont{font-size: 16px!important;} +.dtree-theme-ficon{color:#000!important;} +.dtree-theme-icon{color:orange!important;} +.dtree-theme-checkbox:hover{color:#fe7786!important;} +.dtree-theme-choose{color:#fe7786!important;} + +/* layui主题风格*/ +.dtree-layui-item-this{background-color: #d2d2d2!important;} +.dtree-layui-item:hover{background-color: #eaeceb!important;} +.dtree-layui-item cite{font-size:12px!important;} +.dtree-layui-item:hover cite{color:#01AAED!important;} + +.dtree-layui-dtreefont{font-size: 16px!important;} +.dtree-layui-ficon{font-size: 14px!important;color:#393D49!important;} +.dtree-layui-icon{color:#393D49!important;} +.dtree-layui-checkbox:hover{color:#01AAED!important;} +.dtree-layui-choose{color:#01AAED!important;} + + + + + diff --git a/static/layui_ext/dtree/dtree.js b/static/layui_ext/dtree/dtree.js new file mode 100755 index 00000000..751a3e86 --- /dev/null +++ b/static/layui_ext/dtree/dtree.js @@ -0,0 +1,2808 @@ +/** + + @Name:dtree 树形组件 + @Author:智慧的小西瓜 + @Site:http://www.wisdomelon.com/DTreeHelper/ + @License:LAYUI + + */ +layui.define(['jquery','layer','form'], function(exports) { + var $ = layui.$, + layer = layui.layer, + form = layui.form; + + // 树的公共定义样式汇总 + var LI_NAV_CHILD = "dtree-nav-ul-sid", LI_NAV_ITEM = "dtree-nav-item", + LI_DIV_ITEM = "dtree-nav-div", DTREEFONT = "dtreefont", DTREEFONTSPECIAL="dtreefont-special", + LI_DIV_MENUBAR = "dtree-menubar",LI_DIV_MENUBAR_DOWN = "dtree-icon-move-down", LI_DIV_MENUBAR_UP = "dtree-icon-move-up", LI_DIV_MENUBAR_REFRESH = "dtree-icon-refresh", LI_DIV_MENUBAR_DELETE = "dtree-icon-delete1", LI_DIV_MENUBAR_SEARCH = "dtree-icon-search_list_light", + LI_DIV_TOOLBAR = "dtree-toolbar", TOOLBAR_TOOL = "dtree-toolbar-tool", LI_DIV_TOOLBAR_ADD = "dtree-icon-roundadd", LI_DIV_TOOLBAR_EDIT = "dtree-icon-bianji", LI_DIV_TOOLBAR_DEL = "dtree-icon-roundclose", + LI_DIV_SPREAD_LAST = "dtree-icon-dian", + LI_DIV_CHECKBAR = "dtree-nav-checkbox-div", LI_DIV_CHECKBAR_ON = "dtree-icon-fuxuankuangxuanzhong", LI_DIV_CHECKBAR_OUT = "dtree-icon-fuxuankuang", LI_DIV_CHECKBAR_NOALL = "dtree-icon-fuxuankuang-banxuan", + LI_CLICK_CHECKBAR = "d-click-checkbar", //绑定点击复选框时需要用到 + LI_DIV_TEXT_CLASS = "t-click", UL_ROOT="dtree"; + + // 树的自定义样式 + var DTREE = "dtree-", //自定义样式前缀 + ITEMTHIS = "-item-this", //自定义样式当前行选中后缀 + ITEM = "-item", //自定义样式当前行后缀 + DFONT = "-dtreefont", //自定义样式图标样式后缀 + FICON = "-ficon", //自定义样式一级图标样式后缀 + ICON = "-icon", //自定义样式二级图标样式后缀 + CBOX = "-checkbox", //自定义样式复选框样式后缀 + CHS = "-choose"; //自定义样式复选框选中样式后缀 + + // 树的公共指定 + var NAV_THIS = "dtree-nav-this", //当前节点 + NAV_SHOW = "dtree-nav-show", //显示子节点 + ICON_HIDE = "dtree-icon-hide", //隐藏dot图标 + $BODY = $("body"), //body选择器 + MOD_NAME = "dtree", //模块名称 + VERSION = "v2.4.5_finally_beta", //版本 + DTrees = {}; //当前被实例化的树的集合 + + // 树的一级节点图标集合 + var firstIconArray = { + "-1": {"open": "dtree-icon-null-open", "close": "dtree-icon-null-close"}, //未指定 + "0" : {"open": "dtree-icon-jian", "close": "dtree-icon-jia"}, + "1" : {"open": "dtree-icon-xiangxia1", "close": "dtree-icon-xiangyou"} + }; + + // 树的二级节点图标集合 + var nodeIconArray = { + "-1": {"open": "dtree-icon-null-open", "close": "dtree-icon-null-close"}, //未指定 + "0" : {"open": "dtree-icon-wenjianjiazhankai", "close": "dtree-icon-weibiaoti5"} + }; + + var leafIconArray = { + "-1": "dtree-icon-null", //未指定 + "0" : "dtree-icon-weibiaoti5", //文件夹 + "1" : "dtree-icon-yonghu", //人员 + "2" : "dtree-icon-fenzhijigou", //机构 + "3" : "dtree-icon-fenguangbaobiao", //报表 + "4" : "dtree-icon-xinxipilu", //信息 + "5" : "dtree-icon-shuye1", //叶子 + "6" : "dtree-icon-caidan_xunzhang", //勋章 + "7" : "dtree-icon-normal-file" //文件 + }; + + // 树自定义操作事件名称集合 绑定dtree-click的事件 + var eventName = { + checkNodeClick: "checkNodeClick", //点击复选框 + itemNodeClick: "itemNodeClick" //点击子节点div + }; + + + // 树默认toolbar提供的功能集合 绑定dtree-tool的事件 + var defaultTool = { + addToolbar: "addToolbar", //点击toolbar新增 + editToolbar: "editToolbar", //点击toolbar编辑 + delToolbar: "delToolbar" //点击toolbar删除 + }; + + // 树默认menubar提供的功能集合 绑定dtree-menu的事件 + var defaultMenu = { + moveDown: "moveDown", //menubar展开节点 + moveUp: "moveUp", //menubar收缩节点 + refresh: "refresh", //menubar刷新树 + remove: "remove", //menubar删除选中节点 + searchNode: "searchNode" //menubar查询节点 + }; + + // 树的公共事件 + var event = { + getElemId: function(options){ // 根据传入的参数获取ID + var elem = options.elem || ""; + var obj = options.obj || $(elem); + + if (obj.length == 0) { //页面中未找到绑定id + return ""; + } else { + return $(obj)[0].id; + } + }, + escape: function(html){ + if(typeof html !== 'string') return ''; + return html.replace(entityReg.escape, function(match){return entityMap.escape[match];}); + }, + unescape: function(str){ + if(typeof str !== 'string') return ''; + return str.replace(entityReg.unescape, function(match){return entityMap.unescape[match];}); + }, + cloneObj: function (obj, filter) { //深复制对象方法 + var newObj = {}; + if (obj instanceof Array) { + newObj = []; + } + var str = ""; + if(typeof filter !== 'undefined') {str = filter.join(",");} + for (var key in obj) { + if(str.indexOf(key) == -1){ + var val = obj[key]; + newObj[key] = typeof val === 'object' ? event.cloneObj(val, typeof filter !== undefined ? filter : []): val; + } + + } + return newObj; + } + }; + + // 特殊符号转义 + var keys = Object.keys || function(obj) { + obj = Object(obj); + var arr = []; + for(var a in obj) arr.push(a); + return arr; + }; + var invert = function(obj){ + obj = Object(obj); + var result = {}; + for(var a in obj) result[obj[a]] = a; + return result; + }; + var entityMap = { + escape: { + "&" : "&", + "<" : "<", + ">" : ">", + "'" : "&quo;" + } + }; + entityMap.unescape = invert(entityMap.escape); + var entityReg = { + escape: RegExp('[' + keys(entityMap.escape).join('') + ']', 'g'), + unescape: RegExp('(' + keys(entityMap.unescape).join('|') + ')', 'g') + }; + + //异步加载接口 + var AjaxHelper = { + request : function(config) { + var data = config.data ? config.data : {}; + var async = (typeof (config.async) === "boolean") ? config.async : true; + $.ajax({ + type : config.type ? config.type : "POST", + headers : config.headers, + url : config.url, + dataType : config.dataType ? config.dataType : "json", + data : data, + async : async, + success : config.success, + error : function(XMLHttpRequest, textStatus, errorThrown) { + if (typeof (config.error) === "function") { + config.error(); + } else { + layer.msg('系统异常导致操作失败, 请联系管理员。',{icon:5, shift:6}); + } + }, + statusCode : { + 404 : function() { + layer.msg('未找到指定请求,请检查访问路径!',{icon:5, shift:6}); + }, + 500 : function() { + layer.msg('系统错误,请联系管理员。',{icon:5, shift:6}); + } + }, + complete : function(XMLHttpRequest, textStatus) { + if (typeof (config.complete) === "function") { + config.complete(XMLHttpRequest, textStatus); + } + } + }); + }, + serialize: function(param){ //json序列化 key=value&key1=value1 + var p = "?"; + for (var key in param) { + p += key + "=" + param[key] + "&"; + } + p = p.substring(0, p.length-1); + return p; + } + }; + + // 树类 + var DTree = function(options){ + + /** 默认赋值**/ + this.response = { // 树返回的json格式 + statusName: "code", //返回标识 + statusCode: 200, //返回码 + message: "message", //返回信息 + rootName: "data", //根节点名称 + treeId: "id", //节点ID + parentId: "parentId", //父节点ID + title: "title", //节点名称 + iconClass: "iconClass", //自定义图标 + childName: "children", //子节点名称 + isLast: "isLast", //是否最后一级节点 +// level: "level", //层级 + spread: "spread", //展开 + disabled: "disabled", //禁用 + checkArr: "checkArr", //复选框列表 + isChecked: "isChecked", //是否选中 + type: "type", //复选框标记 + basicData: "basicData" //表示用户自定义需要存储在树节点中的数据 + }; + this.defaultRequest = { // 树的默认发起请求参数格式,最后会将value作为参数名称传递 + nodeId: "nodeId", //节点ID + parentId: "parentId", //父节点ID + context: "context", //节点内容 + isLeaf: "isLeaf", //是否叶子节点 + level: "level", //层级 + spread: "spread", //节点展开状态 + dataType: "dataType", //节点标记 + ischecked: "ischecked", //节点复选框选中状态 + initchecked: "initchecked", //节点复选框初始状态 + basicData: "basicData", //用户自定义的记录节点数据 + recordData: "recordData", //当前data数据(排除basicData和children字段) + }; + this.toolbarFun = { + addTreeNode: function(param, $div) { //添加树节点后调用的函数,用于用户自定义,如未指定则树不会发生变化 + return ; + }, + editTreeNode: function(param, $div) { //编辑树节点后调用的函数,用于用户自定义,如未指定则树不会发生变化 + return ; + }, + editTreeLoad: function(param){ // 编辑树的数据回显,用于打开编辑时,回填数据 + return ; + }, + delTreeNode: function(param, $div){ //删除树后调用的函数,用于用户自定义,如未指定则树不会发生变化 + return ; + }, + loadToolbarBefore: function(buttons, param, $div){ // 右键菜单加载前的函数 + return buttons; + } + }; + this.toolbarStyle = { + title: "节点", + area: ["60%","80%"] + }; + this.menubarFun = { + remove: function(checkbarNodes){ //删除复选框选中节点,需要用户自定义,如未指定则树只是页面上做了修改 + return true; + } + }; + this.menubarTips = { + toolbar: [], + group: [defaultMenu.moveDown, defaultMenu.moveUp, defaultMenu.refresh, defaultMenu.remove, defaultMenu.searchNode], + freedom: [] + }; + this.checkbarFun = { + chooseBefore: function($i, node){ // 复选框点击前回调 + return true; + }, + chooseDone: function(checkbarNodesParam) { //复选框点击事件完毕后,返回该树关于复选框操作的全部信息,用于用户自定义,如未指定则树只是页面上做了修改 + return ; + } + }; + this.iframe = { // 树点击节点时,打开iframe页面参数配置 + iframeElem: "", //iframe的ID + iframeUrl: "", //树关联的frame地址 + iframeLoad: "leaf", //点击哪一层加载frame: node:所有节点, leaf:默认,最后一级 + iframeDefaultRequest: { //iframe的默认参数,目的是与加载树的参数不一样 + nodeId: "nodeId", //节点ID + parentId: "parentId", //父节点ID + context: "context", //节点内容 + isLeaf: "isLeaf", //是否叶子节点 + level: "level", //层级 + spread: "spread", //节点展开状态 + dataType: "dataType", //节点标记 + ischecked: "ischecked", //节点复选框选中状态 + initchecked: "initchecked", //节点复选框初始状态 + basicData: "basicData", //用户自定义的记录节点数据 + recordData: "recordData", //当前data数据(排除basicData和children字段) + }, + iframeRequest: {} //iframe的自定义参数 + }; + this.iframeFun = { + iframeDone: function(iframeParam){ //iframe加载完毕后,用于用户自定义事件 + return ; + } + }; + this.style = { + item: "", + itemThis: "", + dfont: "", + icon: "", + cbox: "", + chs: "" + }; + + /** 数据绑定**/ + this.node = { // 树节点选中时,包含当前节点的全部信息 + nodeId: "", //节点ID + parentId: "", //父节点ID + context: "", //节点内容 + isLeaf: "", //是否叶子节点 + level: "", //层级 + spread: "", //节点展开状态 + dataType: "", //节点标记 + ischecked: "", //节点复选框选中状态 + initchecked: "", //节点复选框初始状态 + basicData: "", //用户自定义的记录节点数据 + recordData: "", //当前data数据(排除basicData和children字段) + }; + this.toolbarMenu = {}; // 工具栏右键菜单绑定的所有元素 + this.checkbarNode = []; // 复选框标记的全部节点数据 + this.checkArrLen = 0; //添加节点的时判断复选框个数 + this.temp = []; // 临时变量 + + this.setting(options); + }; + + /******************** 初始参数加载 ********************/ + // 设置值 + DTree.prototype.setting = function(options) { + this.options = options || {}; + + /** 绑定元素参数(必填,2个参数项必填一个)**/ + this.elem = this.options.elem || ""; //树绑定的元素ID:#elem + this.obj = this.options.obj || $(this.elem); //树绑定的jquery元素,用于当元素是延迟加载出来的话,可以用这个找到 + + /** 基本参数**/ + this.initLevel = this.options.initLevel || 2; //默认展开节点 2节 + this.type = this.options.type || "load"; // 树的加载方式 all,全量树, load,增量树,默认load + this.cache = (typeof (this.options.cache) === "boolean") ? this.options.cache : true; //开启数据缓存 + this.record = (typeof (this.options.record) === "boolean") ? this.options.record : false; //开启数据记录模式 + this.load = (typeof (this.options.load) === "boolean") ? this.options.load : true; //开启加载动画 + + /** 样式相关参数**/ + this.firstIconArray = $.extend(firstIconArray, this.options.firstIconArray) || firstIconArray; //用户自定义一级图标集合,node + this.nodeIconArray = $.extend(nodeIconArray, this.options.nodeIconArray) || nodeIconArray; //用户自定义二级图标集合,node + this.leafIconArray = $.extend(leafIconArray, this.options.leafIconArray) || leafIconArray; //用户自定义二级图标集合,leaf + this.skin = this.options.skin || "theme"; // 自定义样式 + if(this.skin == "layui"){ // layui主题 + this.ficon = this.options.ficon || "1"; // 一级图标样式,0:+,- + this.dot = (typeof (this.options.dot) === "boolean") ? this.options.dot : false; //是否显示一级图标的小圆点,默认不显示 + this.icon = this.options.icon || "7"; //二级图标样式,0:文件夹,1:人员,2:机构,3:报表,4:信息,5:叶子,6:勋章, -1:不显示二级图标。默认'1' + this.nodeIcon = (typeof this.icon === 'string' || typeof this.icon === 'number') ? (this.icon == "-1" ? "-1" : "-1") : this.icon[0]; // 二级图标中的node节点图标 + } else { // 默认主题 或者自定义主题 + this.ficon = this.options.ficon || "0"; // 一级图标样式,0:+,- + this.dot = (typeof (this.options.dot) === "boolean") ? this.options.dot : true; //是否显示一级图标的小圆点,默认显示 + this.icon = this.options.icon || "5"; //二级图标样式,0:文件夹,1:人员,2:机构,3:报表,4:信息,5:叶子,6:勋章, -1:不显示二级图标。默认'5' + this.nodeIcon = (typeof this.icon === 'string' || typeof this.icon === 'number') ? (this.icon == "-1" ? "-1" : "0") : this.icon[0]; // 二级图标中的node节点图标 + } + + /** 内置样式属性*/ + this.ficonOpen = this.firstIconArray[this.ficon]["open"]; // 一级图标中的node节点open图标 + this.ficonClose = this.firstIconArray[this.ficon]["close"]; // 一级图标中的node节点close图标 + this.nodeIconOpen = this.nodeIconArray[this.nodeIcon]["open"]; // 二级图标中的node节点open图标 + this.nodeIconClose = this.nodeIconArray[this.nodeIcon]["close"]; // 二级图标中的node节点close图标 + this.leafIcon = (typeof this.icon === 'string' || typeof this.icon === 'number') ? this.icon : this.icon[1]; // 二级图标中的leaf节点图标 + this.leafIconShow = this.leafIconArray[this.leafIcon]; // 二级图标中的leaf节点图标 + + this.style.item = DTREE + this.skin + ITEM; + this.style.itemThis = DTREE + this.skin + ITEMTHIS; + this.style.dfont = DTREE + this.skin + DFONT; + this.style.ficon = DTREE + this.skin + FICON; + this.style.icon = DTREE + this.skin + ICON; + this.style.cbox = DTREE + this.skin + CBOX; + this.style.chs = DTREE + this.skin + CHS; + + /** 数据加载参数**/ + this.url = this.options.url || ""; //请求地址 + this.async = (typeof (this.options.async) === "boolean") ? this.options.async : true; //异步同步加载,默认异步加载 + this.headers = this.options.headers || {}; // ajax header属性 + this.method = this.options.method || "post"; //请求类型 + this.dataType = this.options.dataType || "json"; //参数类型 + this.defaultRequest = $.extend(this.defaultRequest, this.options.defaultRequest) || this.defaultRequest; //默认请求参数 + this.filterRequest = this.options.filterRequest || []; //过滤请求参数 + this.request = this.options.request || {}; //用户自定义请求参数 + this.response = $.extend(this.response, this.options.response) || this.response; //返回json格式 + this.data = this.options.data || null; //初始化指定该参数,则不会访问异步接口 + this.dataFormat = this.options.dataFormat || "levelRelationship"; //用于用户配置的data数据格式,list:列表, levelRelationship:层级关系,默认 + this.dataStyle = this.options.dataStyle || "defaultStyle"; //用于用户配置layui通用的json数据风格,layuiStyle:layui风格,defaultStyle:默认风格 + this.success = this.options.success || function(data, obj){}; //树加载完毕后执行解析树之前的回调(仅限异步加载) + this.done = this.options.done || function(data, obj){}; //树加载完毕后的回调(仅限异步加载) + + /** 工具栏参数**/ + this.toolbar = this.options.toolbar || false; //是否开启可编辑模式 + this.toolbarStyle = $.extend(this.toolbarStyle, this.options.toolbarStyle) || this.toolbarStyle; //toolbar的自定义风格,标题,弹框大小 + this.toolbarScroll = this.options.toolbarScroll || this.elem; //树的上级div容器,让树可以显示滚动条的div容器 + this.toolbarLoad = this.options.toolbarLoad || "node"; //toolbar作用范围:node:所有节点,noleaf:非最后一级节点,leaf:最后一级 + this.toolbarShow = this.options.toolbarShow || ["add","edit","delete"]; // toolbar三个按钮自定义加载 + this.toolbarBtn = this.options.toolbarBtn || null; // toolbar增删改中内容的自定义加载 + this.toolbarExt = this.options.toolbarExt || []; // toolbar按钮扩展 + this.toolbarFun = $.extend(this.toolbarFun, this.options.toolbarFun) || this.toolbarFun; // toolbar事件加载 + + /** 菜单栏参数**/ + this.menubar = this.options.menubar || false; //是否打开菜单栏 + this.menubarTips = $.extend(this.menubarTips, this.options.menubarTips) || this.menubarTips; // 菜单栏吸附, toolbar:依附在工具栏,group:依附在按钮组,freedom,自由 + this.menubarFun = $.extend(this.menubarFun, this.options.menubarFun) || this.menubarFun; //menubar事件加载 + + /** 复选框参数**/ + this.checkbar = this.options.checkbar || false; //是否开启复选框模式 + this.checkbarLoad = this.options.checkbarLoad || "node"; // 复选框作用范围,node:所有节点, leaf:最后一级;默认所有节点 + this.checkbarType = this.options.checkbarType || "all" ; //复选框选中形式 all:子集选中父级也选中, no-all:子集选中父级半选中,子集全选父级选中,p-casc:父级选中子集全选,子集无法改变父级选中状态, self:没有任何级联关系,only:只能选中一个复选框。 默认all + this.checkbarData = this.options.checkbarData || "choose" ; //复选框记录数据类型形式, change表示记录变更数据,choose表示记录选中数据,all,记录全部数据,默认choose + this.checkbarFun = $.extend(this.checkbarFun, this.options.checkbarFun) || this.checkbarFun; // checkbar事件加载 + + /** iframe模式参数**/ + this.useIframe = this.options.useIframe || false; // 是否加载iframe 默认false, + this.iframe = $.extend(this.iframe, this.options.iframe) || this.iframe; //iframe配置 + this.iframeFun = $.extend(this.iframeFun, this.options.iframeFun) || this.iframeFun; //iframe事件加载 + + }; + + // 设置值 + DTree.prototype.reloadSetting = function(options) { + this.options = $.extend(this.options, options) || this.options; + + /** 绑定元素参数**/ + this.elem = this.options.elem || this.elem; //树绑定的元素ID:#elem + if(typeof this.options.obj === 'undefined'){ + if(this.elem) { + if($(this.elem).length > 0) { + this.obj = $(this.elem); + } + } + } else { + this.obj = this.options.obj || this.obj; //树绑定的jquery元素,用于当元素是延迟加载出来的话,可以用这个找到 + } + + /** 基本参数**/ + this.initLevel = this.options.initLevel || this.initLevel; //默认展开节点 2节 + this.type = this.options.type || this.type; // 树的加载方式 all,全量树, load,增量树,默认load + this.cache = (typeof (this.options.cache) === "boolean") ? this.options.cache : this.cache; //开启数据缓存 + this.record = (typeof (this.options.record) === "boolean") ? this.options.record : this.record; //开启数据记录模式 + this.load = (typeof (this.options.load) === "boolean") ? this.options.load : this.load; //开启加载动画 + + /** 样式相关参数**/ + this.firstIconArray = $.extend(firstIconArray, this.options.firstIconArray) || this.firstIconArray; //用户自定义一级图标集合,node + this.nodeIconArray = $.extend(nodeIconArray, this.options.nodeIconArray) || this.nodeIconArray; //用户自定义二级图标集合,node + this.leafIconArray = $.extend(leafIconArray, this.options.leafIconArray) || this.leafIconArray; //用户自定义二级图标集合,leaf + this.skin = this.options.skin || this.skin; // 自定义样式 + if(this.skin == "layui"){ // layui主题 + this.ficon = this.options.ficon || this.ficon; // 一级图标样式,0:+,- + this.dot = (typeof (this.options.dot) === "boolean") ? this.options.dot : false; //是否显示一级图标的小圆点,默认不显示 + this.icon = this.options.icon || this.icon; //二级图标样式,0:文件夹,1:人员,2:机构,3:报表,4:信息,5:叶子,6:勋章, -1:不显示二级图标。默认'1' + this.nodeIcon = (typeof this.icon === 'string' || typeof this.icon === 'number') ? (this.icon == "-1" ? "-1" : "-1") : this.icon[0]; // 二级图标中的node节点图标 + } else { // 默认主题 或者自定义主题 + this.ficon = this.options.ficon || this.ficon; // 一级图标样式,0:+,- + this.dot = (typeof (this.options.dot) === "boolean") ? this.options.dot : true; //是否显示一级图标的小圆点,默认显示 + this.icon = this.options.icon || this.icon; //二级图标样式,0:文件夹,1:人员,2:机构,3:报表,4:信息,5:叶子,6:勋章, -1:不显示二级图标。默认'5' + this.nodeIcon = (typeof this.icon === 'string' || typeof this.icon === 'number') ? (this.icon == "-1" ? "-1" : "0") : this.icon[0]; // 二级图标中的node节点图标 + } + + /** 内置样式属性*/ + this.ficonOpen = this.firstIconArray[this.ficon]["open"]; // 一级图标中的node节点open图标 + this.ficonClose = this.firstIconArray[this.ficon]["close"]; // 一级图标中的node节点close图标 + this.nodeIconOpen = this.nodeIconArray[this.nodeIcon]["open"]; // 二级图标中的node节点open图标 + this.nodeIconClose = this.nodeIconArray[this.nodeIcon]["close"]; // 二级图标中的node节点close图标 + this.leafIcon = (typeof this.icon === 'string' || typeof this.icon === 'number') ? this.icon : this.icon[1]; // 二级图标中的leaf节点图标 + this.leafIconShow = this.leafIconArray[this.leafIcon]; // 二级图标中的leaf节点图标 + + this.style.item = DTREE + this.skin + ITEM; + this.style.itemThis = DTREE + this.skin + ITEMTHIS; + this.style.dfont = DTREE + this.skin + DFONT; + this.style.ficon = DTREE + this.skin + FICON; + this.style.icon = DTREE + this.skin + ICON; + this.style.cbox = DTREE + this.skin + CBOX; + this.style.chs = DTREE + this.skin + CHS; + + /** 数据加载参数**/ + this.url = this.options.url || this.url; //请求地址 + this.async = (typeof (this.options.async) === "boolean") ? this.options.async : this.async; //异步同步加载,默认异步加载 + this.headers = this.options.headers || this.headers; // ajax header属性 + this.method = this.options.method || this.method; //请求类型 + this.dataType = this.options.dataType || this.dataType; //参数类型 + this.defaultRequest = $.extend(this.defaultRequest, this.options.defaultRequest) || this.defaultRequest; //默认请求参数 + this.filterRequest = this.options.filterRequest || this.filterRequest; //过滤请求参数 + this.request = this.options.request || this.request; //用户自定义请求参数 + this.response = $.extend(this.response, this.options.response) || this.response; //返回json格式 + this.data = this.options.data || this.data; //初始化指定该参数,则不会访问异步接口 + this.dataFormat = this.options.dataFormat || this.dataFormat; //用于用户配置的data数据格式,list:列表, levelRelationship:层级关系,默认 + this.dataStyle = this.options.dataStyle || this.dataStyle; //用于用户配置layui通用的json数据风格,layuiStyle:layui风格,defaultStyle:默认风格 + this.success = this.options.success || this.success; //树加载完毕后执行解析树之前的回调(仅限异步加载) + this.done = this.options.done || this.done; //树加载完毕后的回调(仅限异步加载) + + /** 可编辑模式参数**/ + this.toolbar = this.options.toolbar || this.toolbar; //是否开启可编辑模式 + this.toolbarStyle = $.extend(this.toolbarStyle, this.options.toolbarStyle) || this.toolbarStyle; //toolbar的自定义风格,标题,弹框大小 + this.toolbarScroll = this.options.toolbarScroll || this.toolbarScroll; //树的上级div容器,让树可以显示滚动条的div容器 + this.toolbarLoad = this.options.toolbarLoad || this.toolbarLoad; //toolbar作用范围:node:所有节点,noleaf:非最后一级节点,leaf:最后一级 + this.toolbarShow = this.options.toolbarShow || this.toolbarShow; // toolbar三个按钮 + this.toolbarBtn = this.options.toolbarBtn || this.toolbarBtn; // toolbar增删改中内容的自定义加载 + this.toolbarExt = this.options.toolbarExt || this.toolbarExt; // toolbar按钮扩展 + this.toolbarFun = $.extend(this.toolbarFun, this.options.toolbarFun) || this.toolbarFun; // toolbar事件加载 + + /** 菜单栏参数**/ + this.menubar = this.options.menubar || this.menubar; //是否打开菜单栏 + this.menubarTips = $.extend(this.menubarTips, this.options.menubarTips) || this.menubarTips; // 菜单栏吸附, toolbar:依附在工具栏,group:依附在按钮组,freedom,自由 + this.menubarFun = $.extend(this.menubarFun, this.options.menubarFun) || this.menubarFun; //menubar事件加载 + + /** 复选框参数**/ + this.checkbar = this.options.checkbar || this.checkbar; //是否开启复选框模式 + this.checkbarLoad = this.options.checkbarLoad || this.checkbarLoad; // 复选框作用范围,node:所有节点, leaf:最后一级;默认所有节点 + this.checkbarType = this.options.checkbarType || this.checkbarType ; //复选框选中形式 all:子集选中父级也选中, no-all:子集选中父级半选中,子集全选父级选中,p-casc:父级选中子集全选,子集无法改变父级选中状态, self:没有任何级联关系,only:只能选中一个复选框。 默认all + this.checkbarData = this.options.checkbarData || this.checkbarData ; //复选框记录数据类型形式, change表示记录变更数据,choose表示记录选中数据,all,记录全部数据,默认choose + this.checkbarFun = $.extend(this.checkbarFun, this.options.checkbarFun)|| this.checkbarFun ; // checkbar事件加载 + + /** iframe模式参数**/ + this.useIframe = this.options.useIframe || this.useIframe; // 是否加载iframe 默认false, + this.iframe = $.extend(this.iframe, this.options.iframe) || this.iframe; //iframe配置 + this.iframeFun = $.extend(this.iframeFun, this.options.iframeFun) || this.iframeFun; //iframe事件加载 + + }; + + /******************** 初始化数据区域 ********************/ + // 重载树 + DTree.prototype.reload = function(options){ + var _this = this; + _this.reloadSetting(options); + _this.init(); + }; + + // 初始化树 + DTree.prototype.init = function(){ + var _this = this; + if (typeof _this !== "object") { + layer.msg("树组件未成功加载,请检查配置", {icon:5}); + return ; + } + + if(_this.data) { + if(typeof _this.data.length === 'undefined'){ + layer.msg("数据解析异常,data数据格式不正确", {icon:5}); + return ; + } + + //先将ul中的元素清空 + _this.obj.html(""); + + // 加载完毕后执行树解析前的回调 + _this.success(_this.data, _this.obj); + + // 第一次解析树 + if (_this.dataFormat == 'list'){ + //1.识别根节点ul中的data-id标签,判断顶级父节点 + var pid = _this.obj.attr("data-id"); + //2.构建一个存放节点的树组 + var rootListData = _this.queryListTreeByPid(pid, _this.data); + _this.loadListTree(rootListData, _this.data, 1); + } else { + _this.loadTree(_this.data, 1); + } + + // 加载完毕后的回调 + _this.done(_this.data, _this.obj); + + } else { + if (!_this.url) { + layer.msg("数据请求异常,url参数未指定", {icon:5}); + return ; + } + + //先将ul中的元素清空 + _this.obj.html(""); + + var index = _this.load ? layer.load(1) : ""; + + AjaxHelper.request({ + async: _this.async, + headers: _this.headers, + type: _this.method, + url: _this.url, + dataType: _this.dataType, + data: _this.getFilterRequestParam(_this.getRequestParam()), + success: function(result) { + if (typeof result === 'string') { + result = $.parseJSON(result); + } + var code = ""; + if (_this.dataStyle == 'layuiStyle'){ + code = result[_this.response.statusName]; + } else { + code = result.status[_this.response.statusName]; + } + + if (code == _this.response.statusCode) { + // 加载完毕后执行树解析前的回调 + _this.success(result, _this.obj); + + // 第一次解析树 + if (_this.dataFormat == 'list'){ + //1.识别根节点ul中的data-id标签,判断顶级父节点 + var pid = _this.obj.attr("data-id"); + //2.构建一个存放节点的树组 + var rootListData = _this.queryListTreeByPid(pid, result[_this.response.rootName]); + _this.loadListTree(rootListData, result[_this.response.rootName], 1); + } else { + _this.loadTree(result[_this.response.rootName], 1); + } + + // 加载完毕后的回调 + _this.done(result, _this.obj); + } else { + if (_this.dataStyle == 'layuiStyle'){ + layer.msg(result[_this.response.message], {icon:2}); + } else { + layer.msg(result.status[_this.response.message], {icon:2}); + } + } + }, + complete: function(){if(_this.load){layer.close(index);}} + }); + } + }; + + // 加载子节点 + DTree.prototype.getChild = function($div, data) { + var _this = this, + $ul = $div.next("ul"); + + _this.setNodeParam($div); + + if(typeof data !== 'undefined') { + if(typeof data.length === 'undefined'){ + layer.msg("数据解析异常,data数据格式不正确", {icon:5}); + return ; + } + + //先将ul中的元素清空 + $ul.html(""); + + // 解析树 + if (_this.dataFormat == 'list'){ + var pid = _this.node.nodeId; + var level = parseInt(_this.node.level)+1; + + var listData = _this.queryListTreeByPid(pid, data); + _this.loadListTree(listData, _this.data, level); + } else { + _this.loadTree(data, level); + } + + } else { + if (!_this.url) { + layer.msg("数据请求异常,url参数未指定", {icon:5}); + return ; + } + + $ul.html(""); + var index = _this.load ? layer.load(1) : ""; + AjaxHelper.request({ + async: _this.async, + headers: _this.headers, + type: _this.method, + url: _this.url, + dataType: _this.dataType, + data: _this.getFilterRequestParam(_this.getRequestParam()), + success: function(result) { + if (typeof result === 'string') { + result = $.parseJSON(result); + } + var code = ""; + if (_this.dataStyle == 'layuiStyle'){ + code = result[_this.response.statusName]; + } else { + code = result.status[_this.response.statusName]; + } + + if (code == _this.response.statusCode) { + // 解析树 + var pid = _this.node.nodeId; + var level = parseInt(_this.node.level)+1; + if (_this.dataFormat == 'list'){ + var pListData = _this.queryListTreeByPid(pid, result[_this.response.rootName]); + _this.loadListTree(pListData, result[_this.response.rootName], level, $ul); + } else { + _this.loadTree(result[_this.response.rootName], level, $ul); + } + + $ul.addClass(NAV_SHOW); + } else { + if (_this.dataStyle == 'layuiStyle'){ + layer.msg(result[_this.response.message], {icon:2}); + } else { + layer.msg(result.status[_this.response.message], {icon:2}); + } + } + }, + complete: function(){if(_this.load){layer.close(index);}} + }); + } + }; + + // 初始化树或者拼接树 + DTree.prototype.loadListTree = function(pListData, listData, level, $ul){ + var _this = this; + $ul = $ul || _this.getNowNodeUl(); //当前选中的节点或根节点 + if (pListData.length > 0){ + for (var i = 0; i < pListData.length; i++) { + // 1.获取已知节点的全部数据 + var data = pListData[i]; + if(typeof data !== "object") continue; + var parseData = _this.parseData(data); + var childListData = _this.queryListTreeByPid(parseData.treeId(), listData); // 根据已知数据的id判断该条数据是否还有子数据 + + // 3. 页面元素加载数据 + $ul.append(_this.getLiItemDom(parseData.treeId(), parseData.parentId(), parseData.title(), parseData.isLast(childListData.length), parseData.iconClass(), parseData.checkArr(), level, parseData.spread(level), parseData.disabled(), parseData.basicData(), parseData.recordData(), ($ul.hasClass(UL_ROOT) ? "root" : "item"))); + // 4.有子数据的元素加载子节点 + if(childListData.length > 0){ + var cLevel = parseInt(level)+1; + _this.loadListTree(childListData, listData, cLevel, _this.obj.find("ul[data-id='"+parseData.treeId()+"']")); + } + } + } + }; + + // 根据父ID查找list数据中匹配的元素 + DTree.prototype.queryListTreeByPid = function(pid, listData){ + var _this = this; + var rootListData = []; + if (listData) { + for (var i = 0; i < listData.length; i++) { + var data = listData[i]; + if(typeof data !== "object") continue; + if(pid == "null" || pid == null){ + if(data[_this.response.parentId] == null) { + rootListData.push(data); + } + } else { + if (data[_this.response.parentId] == pid){ + rootListData.push(data); + } + } + } + } + return rootListData; + }; + + // 初始化树或者拼接树 + DTree.prototype.loadTree = function(root, level, $ul){ + var _this = this; + if (root) { + $ul = $ul || _this.getNowNodeUl(); //当前选中的节点或根节点 + for (var i = 0; i < root.length; i++) { // 遍历跟节点或追加的跟节点 + var data = root[i]; + if(typeof data !== "object") continue; + var parseData = _this.parseData(data); + var children = parseData.children(); + $ul.append(_this.getLiItemDom(parseData.treeId(), parseData.parentId(), parseData.title(), parseData.isLast(children.length), parseData.iconClass(), parseData.checkArr(), level, parseData.spread(level), parseData.disabled(), parseData.basicData(), parseData.recordData(), ($ul.hasClass(UL_ROOT) ? "root" : "item"))); + if (children.length != 0) { + var cLevel = parseInt(level)+1; + _this.loadTree(children, cLevel, _this.obj.find("ul[data-id='"+parseData.treeId()+"']")); + } + } + } + }; + + // 解析data数据 + DTree.prototype.parseData = function(data) { + var _this = this; + + return { + treeId: function(){ + return data[_this.response.treeId]; + }, + parentId: function(){ + return data[_this.response.parentId]; + }, + title: function(){ + return data[_this.response.title] || ""; + }, + level: function(){ + return data[_this.response.level] || ""; + }, + iconClass: function(){ + return data[_this.response.iconClass] || ""; + }, + isLast: function(len){ + return ((len == 0) ? + ((typeof (data[_this.response.isLast]) === "boolean") ? data[_this.response.isLast] : true) : + ((typeof (data[_this.response.isLast]) === "boolean") ? data[_this.response.isLast] : false)); + }, + spread: function(level){ + return ((level < _this.initLevel) ? + ((typeof (data[_this.response.spread]) === "boolean") ? data[_this.response.spread] : true) : + ((typeof (data[_this.response.spread]) === "boolean") ? data[_this.response.spread] : false)); + }, + disabled: function(){ + return (typeof (data[_this.response.disabled]) === "boolean") ? data[_this.response.disabled] : false; + }, + checkArr: function(){ + var checkArr = []; + var checkArrData = data[_this.response.checkArr]; + if(typeof checkArrData === 'string'){ + if(checkArrData.indexOf("{") > -1 && checkArrData.indexOf("}") > -1){ + checkArrData = JSON.parse(checkArrData); + } else { + checkArrData = {"type":"0","isChecked":checkArrData}; + } + } + if(typeof checkArrData === 'object'){ + if(typeof checkArrData.length === 'undefined'){ + checkArr.push(checkArrData); + } else { + checkArr = checkArrData; + } + } + + if(checkArr.length > 0 && checkArr.length > _this.checkArrLen){ + _this.checkArrLen = checkArr.length; // 获取复选框个数 + } + return checkArr; + + }, + children: function(){ + return data[_this.response.childName] || []; + }, + basicData: function(){ + return event.escape(JSON.stringify(data[_this.response.basicData])) || JSON.stringify({}); + }, + recordData: function(){ + var recordData = _this.record ? event.cloneObj(data, [_this.response.basicData, _this.response.childName]) : {}; + return event.escape(JSON.stringify(recordData)); + }, + data: function(){ + return event.escape(JSON.stringify(data)); + } + } + + }; + + //新增节点的dom值 + DTree.prototype.getDom = function(treeId, parentId, title, isLast, iconClass, checkArr, level, spread, disabled) { + var _this = this, + rootId = _this.obj[0].id, + toolbar = _this.toolbar, + checkbar = _this.checkbar; + return { + fnode: function() { // + - 图标 + // 获取图标的变量 + var ficon = _this.ficon, + ficonOpen = _this.ficonOpen, + ficonClose = _this.ficonClose, + dot = _this.dot; + + if(ficon != "-1" && dot){ // 都加载 + return isLast ? "" : + (spread ? "" : ""); + } + + if(ficon != "-1" && !dot){ // 加载node 隐藏leaf + return isLast ? "" : + (spread ? "" : ""); + } + + if(ficon == "-1" && dot){ // 隐藏node 加载leaf + return isLast ? "" : + (spread ? "" : ""); + } + + if(ficon == "-1" && !dot){ // 都隐藏 + return isLast ? "" : + (spread ? "" : ""); + } + }, + node: function() { // 二级图标样式 + // 获取图标的变量 + var nodeIcon = _this.nodeIcon, + leafIcon = _this.leafIcon; + + var leafIconShow = _this.leafIconShow, + nodeIconOpen = _this.nodeIconOpen, + nodeIconClose = _this.nodeIconClose; + if(iconClass){ + leafIconShow = iconClass; + nodeIconOpen = iconClass; + nodeIconClose = iconClass; + } + + if(nodeIcon != "-1" && leafIcon != "-1"){ // 都加载 + return isLast ? "" : + (spread ? "" : ""); + } + + if(nodeIcon != "-1" && leafIcon == "-1"){ // 加载node 隐藏leaf + return isLast ? "" : + (spread ? "" : ""); + } + + if(nodeIcon == "-1" && leafIcon != "-1"){ // 隐藏node 加载leaf + return isLast ? "" : + (spread ? "" : ""); + } + + if(nodeIcon == "-1" && leafIcon == "-1"){ // 都隐藏 + return isLast ? "" : + (spread ? "" : ""); + } + }, + checkbox: function() { // 复选框 + var flag = false; + if(_this.checkbarLoad == "node"){if (checkbar) {flag = true;}} else {if (isLast) {if (checkbar) {flag = true;}}} + + if(flag){ + var result = "
            "; + if(checkArr && checkArr.length > 0){ + for (var i = 0; i < checkArr.length; i++) { + var checkData = checkArr[i]; + var isChecked = checkData.isChecked; + var CHOOSE_CLASS = LI_DIV_CHECKBAR_OUT; + if (isChecked == "2") { //半选择 + CHOOSE_CLASS = LI_DIV_CHECKBAR_NOALL + " " + _this.style.chs; + } else if (isChecked == "1") { //选择 + CHOOSE_CLASS = LI_DIV_CHECKBAR_ON + " " + _this.style.chs; + } else { //未选择或者无值 + CHOOSE_CLASS = LI_DIV_CHECKBAR_OUT; + } + result += ""; + } + } + result += "
            "; + return result; + } + + return ""; + }, + text: function() { // 文字显示 + return ""+title+""; + }, + ul: function() { //子节点ul + return isLast ? "
              " : + (spread ? "
                " : "
                  "); + } + }; + + }; + + // 获取拼接好的li + DTree.prototype.getLiItemDom = function(treeId, parentId, title, isLast, iconClass, checkArr, level, spread, disabled, basicData, recordData, flag) { + var _this = this, + rootId = _this.obj[0].id; + + var dom = _this.getDom(treeId, parentId, title, isLast, iconClass, checkArr, level, spread, disabled); + basicData = (basicData == "{}") ? "" : basicData; + recordData = (recordData == "{}") ? "" : recordData; + var div = "
                  " + + div , + dom.fnode(), + dom.node(), + dom.checkbox(), + dom.text(), + "
                  ", dom.ul(), ""].join(""); + return li; + }; + + // 初始化节点,用于数据回显 + DTree.prototype.dataInit = function(chooseId){ + var _this = this; + var $div = _this.obj.find("div[data-id='"+chooseId+"']"); + $div.parent().find("."+NAV_THIS).removeClass(NAV_THIS); + $div.parent().find("."+_this.style.itemThis).removeClass(_this.style.itemThis); + $div.addClass(NAV_THIS); + $div.addClass(_this.style.itemThis); + _this.setNodeParam($div); + // 将该节点的父节点全部展开 + var $li_parents = $div.parents("."+LI_NAV_ITEM); + $li_parents.children("ul").addClass(NAV_SHOW); + $li_parents.children("."+LI_DIV_ITEM).children("i[data-spread]."+_this.ficonClose).addClass(_this.ficonOpen); + $li_parents.children("."+LI_DIV_ITEM).children("i[data-spread]."+_this.ficonClose).removeClass(_this.ficonClose); + $li_parents.children("."+LI_DIV_ITEM).children("i[data-spread]."+_this.nodeIconClose).addClass(_this.nodeIconOpen); + $li_parents.children("."+LI_DIV_ITEM).children("i[data-spread]."+_this.nodeIconClose).removeClass(_this.nodeIconClose); + return _this.getNowParam(); + }; + + /******************** 基础事件区域 ********************/ + // 展开或隐藏节点 作用点: div + DTree.prototype.clickSpread = function($div) { + var $i_spread = $div.find("i[data-spread]").eq(0), + $i_node = $div.find("i[data-spread]").eq(1), + i_node_class = $i_node.attr("class"), + $cite = $div.find("cite[data-leaf]").eq(0), + spread = $i_spread.attr("data-spread"), + $ul = $div.next("ul"); + var _this = this; + + if ($ul.length > 0) { + if (spread == "close") { + if (_this.type=="load") { //增加加载 + if (_this.cache) { //开启缓存 + if ($ul.html()) { + $ul.addClass(NAV_SHOW); + } else { //加载节点 + _this.getChild($div); + } + }else { //每次取新的数据 + $ul.html(""); + _this.getChild($div); + } + } else { // 全量加载 + $ul.addClass(NAV_SHOW); + } + $div.find("i[data-spread]").attr("data-spread","open"); + $i_spread.removeClass(_this.ficonClose); + $i_spread.addClass(_this.ficonOpen); + + var node_class = _this.nodeIconClose; + if(i_node_class.indexOf(node_class) > 0){ + $i_node.removeClass(_this.nodeIconClose); + $i_node.addClass(_this.nodeIconOpen); + } + + } else if (spread == "open") { + $ul.removeClass(NAV_SHOW); + $div.find("i[data-spread]").attr("data-spread","close"); + $i_spread.removeClass(_this.ficonOpen); + $i_spread.addClass(_this.ficonClose); + + var node_class = _this.nodeIconOpen; + if(i_node_class.indexOf(node_class) > 0){ + $i_node.removeClass(_this.nodeIconOpen); + $i_node.addClass(_this.nodeIconClose); + } + } + } + }; + + // 数据格式化 + DTree.prototype.escape = function(html){ + return event.escape(html); + }; + + // 格式化数据转回正常数据 + DTree.prototype.unescape = function(str){ + return event.unescape(str); + }; + + /******************** 工具栏及菜单栏区域 ********************/ + + + // 初始化菜单栏和工具栏的div + DTree.prototype.initTreePlus = function(){ + var _this = this; + // 初始化菜单栏和工具栏的div + _this.obj.prevAll('div#dtree_menubar_'+_this.obj[0].id).remove(); + _this.obj.prevAll('div#dtree_toolbar_'+_this.obj[0].id).remove(); + _this.toolbarMenu = {}; + if(_this.menubar && _this.menubarTips.group && _this.menubarTips.group.length > 0) _this.obj.before("
                  "); + if(_this.toolbar) _this.obj.before("
                  "); + + }; + + // 开启工具栏和菜单栏 + DTree.prototype.openTreePlus = function(){ + var _this = this; + // 先对工具栏做处理,因为菜单栏可能会与工具栏产生关联。 + var ggMenu = []; + if(_this.toolbar) _this.getToolbarDom(); + + if(_this.menubar) { + var menubarTips = _this.menubarTips, + mtbar = menubarTips.toolbar, + group = menubarTips.group, + freedom = menubarTips.freedom; + if(mtbar && mtbar.length > 0){ + // 菜单栏吸附工具栏上 + for(var i=0; i 0){ + // 菜单栏吸附在上方的按钮组div中 + for(var i=0; i"; + break; + case defaultMenu.moveUp: + gg = ""; + break; + case defaultMenu.refresh: + gg = ""; + break; + case defaultMenu.remove: + gg = (_this.checkbar) ? "" : ""; + break; + case defaultMenu.searchNode: + gg = ""; + break; + } + return gg; + }; + + // 获取扩展菜单栏 + DTree.prototype.getExtMenubarDom = function(menu){ + var _this = this; + return ""; + }; + + // 获取依附在工具栏的菜单栏 + DTree.prototype.getMenubarToolDom = function(menu){ + var _this = this; + var rootId = _this.obj[0].id; + switch (menu) { + case defaultMenu.moveDown: + _this.toolbarMenu[defaultMenu.moveDown] = "
                   展开"+_this.toolbarStyle.title+"
                  "; + break; + case defaultMenu.moveUp: + _this.toolbarMenu[defaultMenu.moveUp] = "
                   收缩"+_this.toolbarStyle.title+"
                  "; + break; + case defaultMenu.refresh: + _this.toolbarMenu[defaultMenu.refresh] = "
                   刷新
                  "; + break; + case defaultMenu.remove: + if(_this.checkbar) + _this.toolbarMenu[defaultMenu.remove] = "
                   删除选中"+_this.toolbarStyle.title+"
                  "; + break; + case defaultMenu.searchNode: + _this.toolbarMenu[defaultMenu.searchNode] = "
                   查询"+_this.toolbarStyle.title+"
                  "; + break; + } + }; + + // 获取依附在工具栏的扩展菜单栏 + DTree.prototype.getExtMenubarToolDom = function(menu){ + var _this = this; + _this.toolbarMenu[menu.menubarId] = "
                   "+menu.title+"
                  "; + }; + + + // menubar内置方法 + DTree.prototype.menubarMethod = function(){ + var _this = this; + return { + openAllNode: function(obj){ // 展开所有节点 + var $ulNode = obj || _this.obj.children("li").children("ul"); + // 遍历所有ul子节点 + for (var i = 0; i < $ulNode.length; i++) { + // 获取当前节点的信息 + var $ul = $($ulNode[i]), + $div = $ul.prev("div"), + $i_spread = $div.find("i[data-spread]").eq(0), + $i_node = $div.find("i[data-spread]").eq(1), + i_node_class = $i_node.attr("class"), + $cite = $div.find("cite[data-leaf]").eq(0), + spread = $i_spread.attr("data-spread"), + leaf = $cite.attr("data-leaf"); + + if (leaf == "leaf") { continue; } // 说明是叶子了,则继续循环下一个 + + if (spread == "open") { + // 说明该节点已经展开了,则进行子节点循环 + } else { + if (_this.type=="load") { //是否全量加载 + if (_this.cache) { //是否开启缓存 + if ($ul.html()) { + $ul.addClass(NAV_SHOW); + } else { //加载节点 + _this.getChild($div); + } + }else { //每次取新的数据 + $ul.html(""); + _this.getChild($div); + } + } else { // 全量加载 + $ul.addClass(NAV_SHOW); + } + $div.find("i[data-spread]").attr("data-spread","open"); + $i_spread.removeClass(_this.ficonClose); + $i_spread.addClass(_this.ficonOpen); + + var node_class = _this.nodeIconClose; + if(i_node_class.indexOf(node_class) > 0){ + $i_node.removeClass(_this.nodeIconClose); + $i_node.addClass(_this.nodeIconOpen); + } + } + var $childUl = $ul.children("li").children("ul"); + _this.menubarMethod().openAllNode($childUl); + } + }, + closeAllNode: function(){ //收缩所有节点 + _this.obj.find("."+LI_NAV_CHILD).each(function(){ + // 获取当前节点的信息 + var $ul = $(this), + $div = $ul.prev("div"), + $i_spread = $div.find("i[data-spread]").eq(0), + $i_node = $div.find("i[data-spread]").eq(1), + i_node_class = $i_node.attr("class"), + $cite = $div.find("cite[data-leaf]").eq(0), + spread = $i_spread.attr("data-spread"), + leaf = $cite.attr("data-leaf"); + + $ul.removeClass(NAV_SHOW); + $div.find("i[data-spread]").attr("data-spread","close"); + $i_spread.removeClass(_this.ficonOpen); + $i_spread.addClass(_this.ficonClose); + + var node_class = _this.nodeIconOpen; + if(i_node_class.indexOf(node_class) > 0){ + $i_node.removeClass(_this.nodeIconOpen); + $i_node.addClass(_this.nodeIconClose); + } + }); + }, + refreshTree: function(){// 刷新树 + _this.obj.html(""); // 清空树结构 + _this.initNodeParam(); // 清空参数 + _this.init(); //执行初始化方法 + }, + remove: function(){// 删除选中节点 + var len = _this.obj.find("i[data-par][data-checked='1']").length; + if(len == 0){ + layer.msg("请至少选中一个节点",{icon:2}); + }else{ + //操作前先清空 + _this.checkbarNode = []; + // 选择所有复选框节点 + var i_node = {}; + _this.obj.find("i[data-par][data-checked='1']").each(function(){ + var $i = $(this), $div = $i.closest("."+LI_DIV_ITEM); + + _this.checkbarNode.push(_this.getRequestParam(_this.getCheckbarNodeParam($div, $i))); + }); + + layer.confirm('确定要删除选中节点?', {icon: 3, title:'删除选中节点'}, function(index1){ + var flag = _this.menubarFun.remove(_this.checkbarNode); + if(flag){ + _this.obj.find("i[data-par][data-checked='1']").closest("."+LI_DIV_ITEM).next("ul").remove(); + _this.obj.find("i[data-par][data-checked='1']").closest("."+LI_DIV_ITEM).remove(); + _this.checkbarNode=[]; + } + + layer.close(index1); + }); + } + }, + searchNode: function(){//模糊查询该值,展开该值节点 + layer.prompt({ + formType: 0, + value: "", + title: '查询节点' + }, function(value, index1, elem){ + if (value) { + var flag = _this.searchNode(value); + if (!flag) { + layer.msg("该名称节点不存在!", {icon:5}); + } + } else { + layer.msg("未指定查询节点名称", {icon:5}); + } + layer.close(index1); + }); + }, + extMethod: function(menuId, $div, flag){ + if(_this.menubar && _this.menubarTips.group && _this.menubarTips.group.length > 0 && flag == "group"){ + for(var i=0; i<_this.menubarTips.group.length; i++){ + var ext = _this.menubarTips.group[i]; + if (menuId == ext.menubarId){ + ext.handler(_this.getRequestParam(_this.getNodeParam($div), $div)); + break; + } + } + } + if(_this.menubar && _this.menubarTips.toolbar && _this.menubarTips.toolbar.length > 0 && flag == "toolbar"){ + for(var i=0; i<_this.menubarTips.toolbar.length; i++){ + var ext = _this.menubarTips.toolbar[i]; + if (menuId == ext.menubarId){ + ext.handler(_this.getRequestParam(_this.getNodeParam($div), $div)); + break; + } + } + } + if(_this.menubar && _this.menubarTips.freedom && _this.menubarTips.freedom.length > 0 && flag == "freedom"){ + for(var i=0; i<_this.menubarTips.freedom.length; i++){ + var ext = _this.menubarTips.freedom[i]; + if (menuId == ext.menubarId){ + ext.handler(_this.getRequestParam(_this.getNodeParam($div), $div)); + break; + } + } + } + } + }; + }; + + // menubar监听方法 + DTree.prototype.menubarListener = function(menuId, flag){ + var _this = this; + var $div = _this.getNowNode(); + switch (menuId) { + case defaultMenu.moveDown: // 展开节点 + _this.menubarMethod().openAllNode(); + break; + case defaultMenu.moveUp: // 收缩节点 + _this.menubarMethod().closeAllNode(); + break; + case defaultMenu.refresh: + _this.menubarMethod().refreshTree(); // 刷新树 + break; + case defaultMenu.remove: + _this.menubarMethod().remove(); + break; + case defaultMenu.searchNode: + _this.menubarMethod().searchNode(); + break; + default: + _this.menubarMethod().extMethod(menuId, $div, flag); + break; + } + }; + + //模糊查询该值,展开该值节点 + DTree.prototype.searchNode = function(value){ + var _this = this; + var b = false; + var $lis = []; + _this.obj.find("cite[data-leaf]").each(function(){ + var $nthis = $(this); + var html = $nthis.html(); + if(html.indexOf(value) > -1){ + if($nthis.attr("data-leaf") == "leaf") { + // 叶子节点提供包含父节点的所有信息 + var title = ""; + $nthis.parents("li").each(function(){ + title = "-" + $(this).find("cite[data-leaf]").html() + title; + }); + title = title.substring(1, title.length); + $nthis.attr("title", title); + } + // 保存当前cite所在的li及父li中包含该值,则只保留父的 + var i = 0; + $nthis.parents("li").each(function(){ + var html2 = $(this).find("cite[data-leaf]").html(); + if(html2.indexOf(value) > -1){ + i++; + } + if(i >= 2){ + return true; + } + }); + if (i < 2){ + $lis.push($nthis.closest("li").prop("outerHTML")); + } + } + }); + if($lis.length > 0) { + b = true; + // 1.将树节点清空 + _this.obj.html(""); + // 2.遍历所有cite节点,展开当前cite节点 + for(var i=0; i<$lis.length; i++){ + _this.obj.append($lis[i]); + } + } + return b; + }; + + + /******************** 工具栏区域 ********************/ + + // 获取工具栏 + DTree.prototype.getToolbarDom = function(){ + var _this = this; + var toolbarShow = _this.toolbarShow; + var toolbarExt = _this.toolbarExt; + + if(toolbarShow.length > 0){ + for(var i=0; i 新增"+_this.toolbarStyle.title+""; + } + if(show == "edit"){ + _this.toolbarMenu[defaultTool.editToolbar] = "
                   编辑"+_this.toolbarStyle.title+"
                  "; + } + if(show == "delete"){ + _this.toolbarMenu[defaultTool.delToolbar] = "
                   删除"+_this.toolbarStyle.title+"
                  "; + } + } + } + if(toolbarExt.length > 0){ + for(var i=0; i "+ext.title+""; + } + } + }; + + + // 设置工具栏按钮 + DTree.prototype.setToolbarDom = function(toolbarMenu){ + var _this = this; + if(toolbarMenu){ + _this.obj.prevAll('div#dtree_toolbar_'+_this.obj[0].id).find('div.layui-nav-item>dl.layui-nav-child').html(""); + for(var key in toolbarMenu){ + _this.obj.prevAll('div#dtree_toolbar_'+_this.obj[0].id).find('div.layui-nav-item>dl.layui-nav-child').append(toolbarMenu[key]); + } + } + } + + + // 加载toolBar中的内容 + DTree.prototype.loadToolBar = function(title, name){ + var _this = this; + var toolbarShow = _this.toolbarShow; + var nodeBarContents = _this.toolbarBtn; + var html = ""; + switch (name) { + case defaultTool.addToolbar: + + //1. 必须加载的节点内容 + var nowNode = ['
                  ', + '', + '
                  ', + '', + '
                  ', + '
                  '].join(''); + + var addNodeName = ['
                  ', + '', + '
                  ', + '', + '
                  ', + '
                  '].join(''); + + var addNodeBtn = ['
                  ', + '
                  ', + '', + '
                  ', + '
                  '].join(''); + //2. 用户自定义的节点内容 + var addNodeBar = ['
                  ', nowNode, addNodeName]; + if(nodeBarContents != null && nodeBarContents.length > 0){ + if(nodeBarContents[0] != null && nodeBarContents[0] != undefined && nodeBarContents[0].length > 0){ + var addNodeBarContents = nodeBarContents[0]; + + for(var j=0; j
                  '); + html = addNodeBar.join(''); + break; + + case defaultTool.editToolbar: + + //1. 必须加载的节点内容 + var nowNode = ['
                  ', + '', + '
                  ', + '', + '
                  ', + '
                  '].join(''); + + var editNodeName = ['
                  ', + '', + '
                  ', + '', + '
                  ', + '
                  '].join(''); + + + var editNodeBtn = ['
                  ', + '
                  ', + '', + '
                  ', + '
                  '].join(''); + + var editNodeBar = ['
                  ', nowNode, editNodeName]; + //2. 用户自定义的节点内容 + if(nodeBarContents != null && nodeBarContents.length > 0){ + + if(nodeBarContents[1] != null && nodeBarContents[1] != undefined && nodeBarContents[1].length > 0){ + var editNodeBarContents = nodeBarContents[1]; + + for(var j=0; j
                  '); + html = editNodeBar.join(''); + break; + } + return html; + }; + + // 获取toolbar详细的标签信息 + DTree.prototype.loadToolBarDetail = function(){ + var _this = this; + return{ + text: function(nodeBarContents){ + return ['
                  ', + '', + '
                  ', + '', + '
                  ', + '
                  '].join(''); + }, + textarea: function(nodeBarContents){ + return ['
                  ', + '', + '
                  ', + '', + '
                  ', + '
                  '].join(''); + }, + hidden: function(nodeBarContents){ + return [''].join(''); + }, + select: function(nodeBarContents){ + var optionsData = nodeBarContents.optionsData; + var options = ""; + var defaultValue = nodeBarContents.value ? nodeBarContents.value : ""; + for(var key in optionsData){ + if(defaultValue == optionsData[key]){ + options += ""; + } else { + options += ""; + } + } + return ['
                  ', + '', + '
                  ', + '', + '
                  ', + '
                  '].join(''); + } + } + }; + + // 新增节点后改变节点内容 + DTree.prototype.changeTreeNodeAdd = function(returnID){ + var _this = this; + var temp = _this.temp; + var id = temp[0], $ul = temp[1], $div = temp[2], level = temp[3]; + if(returnID){ + var $thisDiv = _this.obj.find("[data-id='"+id+"']"); + if(typeof returnID === "object"){ + // 如果是JSON格式数据,则将当前DIV删除,重新建造DIV + $thisDiv.remove(); + var parseData = _this.parseData(returnID); + + if(parseData.treeId()){ + $ul.append(_this.getLiItemDom(parseData.treeId(), parseData.parentId(), parseData.title(), parseData.isLast(0), parseData.iconClass(), parseData.checkArr(), level, parseData.spread(), parseData.disabled(), parseData.basicData(), parseData.recordData(), "item")); + + // 建造完毕后,选中该DIV + var $addDiv = $ul.find("div[data-id='"+returnID.id+"']"); + _this.setNodeParam($addDiv) + } else { + layer.msg("添加失败,节点ID为undefined!",{icon:5}); + // 将li节点删除 + $ul.find("li[data-id='"+id+"']").remove(); + // 重新赋值 + _this.setNodeParam($div); + // 临时变量制空 + _this.temp = []; + return ; + } + }else if(typeof returnID === "string" || typeof this.icon === 'number'){ + $thisDiv.attr("data-id", returnID); + // 将li节点展示 + $ul.find("li[data-id='"+returnID+"']").show(); + var $addDiv = $ul.find("div[data-id='"+returnID+"']"); + _this.setNodeParam($addDiv) + } + + // 判断当前点击的节点是否是最后一级节点,如果是,则需要修改节点的样式 + var $icon_i = $div.find("i[data-spread]"); + if ($icon_i.eq(0).attr("data-spread") == "last") { + $icon_i.attr("data-spread","open"); + $icon_i.eq(0).removeClass(LI_DIV_SPREAD_LAST); + $icon_i.eq(0).removeClass(ICON_HIDE); + $icon_i.eq(0).addClass(_this.ficonOpen); + $icon_i.eq(1).removeClass(leafIconArray[_this.leafIcon]); + $icon_i.eq(1).addClass(_this.nodeIconOpen); + } else { //如果不是,也要修改节点样式 + $icon_i.attr("data-spread","open"); + $icon_i.eq(0).removeClass(_this.ficonClose); + $icon_i.eq(0).addClass(_this.ficonOpen); + $icon_i.eq(1).removeClass(_this.nodeIconClose); + $icon_i.eq(1).addClass(_this.nodeIconOpen); + + // _this.clickSpread($div); + } + $ul.addClass(NAV_SHOW); //展开UL + } else { + // 将li节点删除 + $ul.find("li[data-id='"+id+"']").remove(); + // 重新赋值 + _this.setNodeParam($div); + } + + _this.temp = []; // 临时变量制空 + + }; + + // 修改节点后改变节点内容 + DTree.prototype.changeTreeNodeEdit = function(flag){ + var _this = this; + var temp = _this.temp; + var $cite = temp[0], + $div = temp[1]; + + if(!flag){ + $cite.html(title); + node = _this.getNodeParam($div); + } + + _this.temp = []; // 临时变量制空 + }; + + // 编辑页打开后显示编辑页内容 + DTree.prototype.changeTreeNodeDone = function(param){ + var _this = this; + form.val('dtree_editNode_form', param); + form.render(); + }; + + // 删除节点后改变节点内容 + DTree.prototype.changeTreeNodeDel = function(flag){ + var _this = this; + var temp = _this.temp; + var $p_li = temp[0], + $p_ul = $p_li.parent("ul"), + $p_div = temp[1]; + + if(flag){ + $p_li.remove(); + // 判断父级ul中是否还存在li,如果不存在,则需要修改节点的样式 + if($p_ul.children("li").length == 0){ + var $icon_i = $p_div.find("i[data-spread]"); + $icon_i.attr("data-spread","last"); + $icon_i.eq(0).removeClass(_this.ficonOpen); + $icon_i.eq(0).removeClass(_this.ficonClose); + if(!_this.dot){$icon_i.eq(0).addClass(ICON_HIDE);} + $icon_i.eq(0).addClass(LI_DIV_SPREAD_LAST); + + $icon_i.eq(1).removeClass(_this.nodeIconOpen); + $icon_i.eq(1).removeClass(_this.nodeIconClose); + $icon_i.eq(1).addClass(leafIconArray[_this.leafIcon]); + } + _this.initNodeParam(); + } + + _this.temp = []; // 临时变量制空 + }; + + + /******************** 复选框区域 ********************/ + // 初始化复选框的值 + DTree.prototype.chooseDataInit = function(chooseIds){ + var _this = this; + var chooseId = chooseIds.split(","); + for (var i=0; i."+LI_DIV_ITEM+">."+LI_DIV_CHECKBAR+">i[data-type='"+dataType+"']"); + _this.checkStatus($child_li_i).noCheck(); + + // 处理父级节点的选中状态 + for (var i = 1, item = $parent_li; i < item.length; i++) { + var flag = item.eq(i).find(">."+LI_NAV_CHILD+" ."+LI_DIV_CHECKBAR+">i[data-type='"+dataType+"'][data-checked='1']").length; + if (flag == 0) { + //把父级去掉选中 + var $item_i = item.eq(i).find(">."+LI_DIV_ITEM+">."+LI_DIV_CHECKBAR+">i[data-type='"+dataType+"']"); + _this.checkStatus($item_i).noCheck(); + } + } + } else { + // 处理当前节点的选中状态 + _this.checkStatus($i).check(); + + // 处理子级节点的选中状态 + var $child_li_i = $child_li.find(">."+LI_DIV_ITEM+">."+LI_DIV_CHECKBAR+">i[data-type='"+dataType+"']"); + _this.checkStatus($child_li_i).check(); + + // 处理父级节点的选中状态 + for (var i = 1, item = $parent_li; i < item.length; i++) { + var $item_i = item.eq(i).find(">."+LI_DIV_ITEM+">."+LI_DIV_CHECKBAR+">i[data-type='"+dataType+"']"); + //把父级选中 + _this.checkStatus($item_i).check(); + } + } + }; + + //实现复选框点击, no-all 子集选中父级半选中,子集全选父级选中 + DTree.prototype.checkAllOrNoallOrNot = function($i) { + var _this = this; + //$i 当前点击的checkbox + var $div = $i.closest("."+LI_DIV_ITEM), + dataPar = $i.attr("data-par"), + dataType = $i.attr("data-type"), + $li = $i.closest(dataPar), //当前checkbox的上级li节点 + $parent_li = $i.parents(dataPar), //当前checkbox的所有父级li节点 + $child_li = $li.find(dataPar); //当前checkbox的上级li节点下的所有子级li节点 + + if ($i.attr("data-checked") == "1") { //当前复选框为选中状态,点击后变为未选中状态 + // 处理当前节点的选中状态 + _this.checkStatus($i).noCheck(); + + // 处理子级节点的选中状态 + var $child_li_i = $child_li.find(">."+LI_DIV_ITEM+">."+LI_DIV_CHECKBAR+">i[data-type='"+dataType+"']"); + _this.checkStatus($child_li_i).noCheck(); + + // 处理父级节点的选中状态 + for (var i = 1, item = $parent_li; i < item.length; i++) { + var flag = item.eq(i).find(">."+LI_NAV_CHILD+" ."+LI_DIV_CHECKBAR+">i[data-type='"+dataType+"'][data-checked='1']").length; + var $item_i = item.eq(i).find(">."+LI_DIV_ITEM+">."+LI_DIV_CHECKBAR+">i[data-type='"+dataType+"']"); + if (flag == 0) { + //把父级去掉选中 + _this.checkStatus($item_i).noCheck(); + } else { + //把父级半选 + _this.checkStatus($item_i).noallCheck(); + } + } + } else { //当前复选框为未选中状态,点击后变为选中状态 + // 处理当前节点的选中状态 + _this.checkStatus($i).check(); + + // 处理子级节点的选中状态 + var $child_li_i = $child_li.find(">."+LI_DIV_ITEM+">."+LI_DIV_CHECKBAR+">i[data-type='"+dataType+"']"); + _this.checkStatus($child_li_i).check(); + + // 处理父级节点的选中状态 + for (var i = 1, item = $parent_li; i < item.length; i++) { + var flag1 = item.eq(i).find(">."+LI_NAV_CHILD+" ."+LI_DIV_CHECKBAR+">i[data-type='"+dataType+"'][data-checked='1']").length; + var flag2 = item.eq(i).find(">."+LI_NAV_CHILD+" ."+LI_DIV_CHECKBAR+">i[data-type='"+dataType+"']").length; + var $item_i = item.eq(i).find(">."+LI_DIV_ITEM+">."+LI_DIV_CHECKBAR+">i[data-type='"+dataType+"']"); + if (flag1 != flag2) { + // 父级复选框半选 + _this.checkStatus($item_i).noallCheck(); + } else { + // 父级复选框全选 + _this.checkStatus($item_i).check(); + } + } + } + }; + + //实现复选框点击,p-casc:父级选中子集全选,子集无法改变父级选中状态 + DTree.prototype.checkAllOrPcascOrNot = function($i) { + var _this = this; + //$i 当前点击的checkbox + var $div = $i.closest("."+LI_DIV_ITEM), + dataPar = $i.attr("data-par"), + dataType = $i.attr("data-type"), + $li = $i.closest(dataPar), //当前checkbox的上级li节点 + $parent_li = $i.parents(dataPar), //当前checkbox的所有父级li节点 + $child_li = $li.find(dataPar); //当前checkbox的上级li节点下的所有子级li节点 + + if ($i.attr("data-checked") == "1") { //当前复选框为选中状态,点击后变为未选中状态 + // 处理当前节点的选中状态 + _this.checkStatus($i).noCheck(); + + // 处理子级节点的选中状态 + var $child_li_i = $child_li.find(">."+LI_DIV_ITEM+">."+LI_DIV_CHECKBAR+">i[data-type='"+dataType+"']"); + _this.checkStatus($child_li_i).noCheck(); + + } else { //当前复选框为未选中状态,点击后变为选中状态 + // 处理当前节点的选中状态 + _this.checkStatus($i).check(); + + // 处理子级节点的选中状态 + var $child_li_i = $child_li.find(">."+LI_DIV_ITEM+">."+LI_DIV_CHECKBAR+">i[data-type='"+dataType+"']"); + _this.checkStatus($child_li_i).check(); + } + }; + + //实现复选框点击,self:各自选中互不影响 + DTree.prototype.checkOrNot = function($i) { + var _this = this; + //$i 当前点击的checkbox + var $div = $i.closest("."+LI_DIV_ITEM), + dataPar = $i.attr("data-par"), + dataType = $i.attr("data-type"), + $li = $i.closest(dataPar), //当前checkbox的上级li节点 + $parent_li = $i.parents(dataPar), //当前checkbox的所有父级li节点 + $child_li = $li.find(dataPar); //当前checkbox的上级li节点下的所有子级li节点 + + if ($i.attr("data-checked") == "1") { //当前复选框为选中状态,点击后变为未选中状态 + // 处理当前节点的选中状态 + _this.checkStatus($i).noCheck(); + } else { //当前复选框为未选中状态,点击后变为选中状态 + // 处理当前节点的选中状态 + _this.checkStatus($i).check(); + } + }; + + //实现复选框点击,only:只能选中1个复选框 + DTree.prototype.checkOnly = function($i) { + var _this = this; + //$i 当前点击的checkbox + var $div = $i.closest("."+LI_DIV_ITEM), + dataPar = $i.attr("data-par"), + dataType = $i.attr("data-type"), + $li = $i.closest(dataPar), //当前checkbox的上级li节点 + $parent_li = $i.parents(dataPar), //当前checkbox的所有父级li节点 + $child_li = $li.find(dataPar); //当前checkbox的上级li节点下的所有子级li节点 + + var checked = $i.attr("data-checked"); + // 将全部节点全部设为未选中状态 + var $all_i = _this.obj.find("i[data-checked]"); + _this.checkStatus($all_i).noCheck(); + + if (checked != "1") { //当前复选框为未选中状态,点击后变为选中状态 + // 处理当前节点的选中状态 + _this.checkStatus($i).check(); + } + + + }; + + //实现复选框点击 + DTree.prototype.changeCheck = function() { + var _this = this; + var temp = _this.temp; + var $i = temp[0]; + // 复选框选中事件 + if (_this.checkbarType == "all") { + _this.checkAllOrNot($i); + } else if(_this.checkbarType == "no-all") { + _this.checkAllOrNoallOrNot($i); + } else if(_this.checkbarType == "p-casc") { + _this.checkAllOrPcascOrNot($i); + } else if(_this.checkbarType == "self") { + _this.checkOrNot($i); + } else if(_this.checkbarType == "only") { + _this.checkOnly($i); + } else { + _this.checkAllOrNot($i); + } + + // 获取复选框选中节点的内容 + var checkbarNodes = _this.setAndGetCheckbarNodesParam(); + + // 用户自定义想做的事情 + _this.checkbarFun.chooseDone(checkbarNodes); + layui.event.call(this, MOD_NAME, "chooseDone("+$(_this.obj)[0].id+")", {"checkbarParams": checkbarNodes}); + _this.temp = []; + }; + + //复选框半选状态初始化设置 + DTree.prototype.initNoAllCheck = function(){ + var _this = this; + //1.获取所有选中节点 + var $is = _this.obj.find("i[data-checked='1']"); + if($is.length > 0){ + for ( var key = 0; key < $is.length; key++) { + var $i = $($is[key]), + dataPar = $i.attr("data-par"), + dataType = $i.attr("data-type"), + $li = $i.closest(dataPar), //当前checkbox的上级li节点 + $parent_li = $i.parents(dataPar), //当前checkbox的所有父级li节点 + $child_li = $li.find(dataPar); //当前checkbox的上级li节点下的所有子级li节点 + + // 处理父级节点的选中状态 + for (var i = 1, item = $parent_li; i < item.length; i++) { + var flag1 = item.eq(i).find(">."+LI_NAV_CHILD+" ."+LI_DIV_CHECKBAR+">i[data-type='"+dataType+"'][data-checked='1']").length; + var flag2 = item.eq(i).find(">."+LI_NAV_CHILD+" ."+LI_DIV_CHECKBAR+">i[data-type='"+dataType+"']").length; + var $item_i = item.eq(i).find(">."+LI_DIV_ITEM+">."+LI_DIV_CHECKBAR+">i[data-type='"+dataType+"']"); + if (flag1 != flag2) { + // 父级复选框半选 + _this.checkStatus($item_i).noallCheck(); + } else { + // 父级复选框全选 + _this.checkStatus($item_i).check(); + } + } + } + } + }; + + //复选框选中状态初始化设置 + DTree.prototype.initAllCheck = function(){ + var _this = this; + //1.获取所有选中节点 + var $is = _this.obj.find("i[data-checked='1']"); + if($is.length > 0){ + for ( var key = 0; key < $is.length; key++) { + var $i = $($is[key]), + dataPar = $i.attr("data-par"), + dataType = $i.attr("data-type"), + $li = $i.closest(dataPar), //当前checkbox的上级li节点 + $parent_li = $i.parents(dataPar), //当前checkbox的所有父级li节点 + $child_li = $li.find(dataPar); //当前checkbox的上级li节点下的所有子级li节点 + + // 处理父级节点的选中状态 + for (var i = 1, item = $parent_li; i < item.length; i++) { + var $item_i = item.eq(i).find(">."+LI_DIV_ITEM+">."+LI_DIV_CHECKBAR+">i[data-type='"+dataType+"']"); + // 父级复选框全选 + _this.checkStatus($item_i).check(); + } + } + } + }; + + // 设置复选框选中/未选中/半选 _this.checkStatus($i).check(); _this.checkStatus($i).noCheck(); _this.checkStatus($i).noallCheck(); + DTree.prototype.checkStatus = function($i) { + var _this = this; + return { + check: function(){ + $i.removeClass(LI_DIV_CHECKBAR_OUT); + $i.removeClass(LI_DIV_CHECKBAR_NOALL); + $i.addClass(LI_DIV_CHECKBAR_ON); + $i.addClass(_this.style.chs); + $i.attr("data-checked","1"); + }, + noCheck: function(){ + $i.removeClass(LI_DIV_CHECKBAR_NOALL); + $i.removeClass(LI_DIV_CHECKBAR_ON); + $i.removeClass(_this.style.chs); + $i.addClass(LI_DIV_CHECKBAR_OUT); + $i.attr("data-checked","0"); + }, + noallCheck: function(){ + $i.removeClass(LI_DIV_CHECKBAR_OUT); + $i.removeClass(LI_DIV_CHECKBAR_ON); + $i.addClass(LI_DIV_CHECKBAR_NOALL); + $i.addClass(_this.style.chs); + $i.attr("data-checked","2"); + } + } + }; + + // 设置树的复选框操作值的全部参数,并获取 + DTree.prototype.setAndGetCheckbarNodesParam = function() { + var _this = this; + //操作前先清空 + _this.checkbarNode = []; + // 选择所有复选框节点 + if (_this.checkbarData == "change"){ //记录变更数据 + _this.obj.find("i[data-par]").each(function(){ + var $i = $(this), $div = $i.closest("."+LI_DIV_ITEM); + + if ($i.attr("data-checked") != $i.attr("data-initchecked")) { + _this.checkbarNode.push(_this.getRequestParam(_this.getCheckbarNodeParam($div, $i))); + } + }); + } else if (_this.checkbarData == "all"){ //记录全部数据 + _this.obj.find("i[data-par][data-checked]").each(function(){ + var $i = $(this), $div = $i.closest("."+LI_DIV_ITEM); + _this.checkbarNode.push(_this.getRequestParam(_this.getCheckbarNodeParam($div, $i))); + + }); + } else { //记录选中数据 + _this.obj.find("i[data-par][data-checked='1']").each(function(){ + var $i = $(this), $div = $i.closest("."+LI_DIV_ITEM); + _this.checkbarNode.push(_this.getRequestParam(_this.getCheckbarNodeParam($div, $i))); + + }); + } + return _this.checkbarNode; + }; + + // 获取树的复选框操作值的全部参数 + DTree.prototype.getCheckbarNodesParam = function() { + var _this = this; + return _this.setAndGetCheckbarNodesParam(); + }; + + // 获取树的一个复选框的参数 + DTree.prototype.getCheckbarNodeParam = function($div, $i){ + var _this = this; + var temp_node = {}; + temp_node.nodeId = $div.attr("data-id"); + temp_node.parentId = $div.parent().attr("data-pid"); + temp_node.context = $div.find("cite[data-leaf]").eq(0).text(); + temp_node.isLeaf = $div.find("cite[data-leaf]").eq(0).attr("data-leaf") == "leaf" ? true : false; + temp_node.level = $div.parent().attr("data-index"); + temp_node.spread = $div.find("i[data-spread]").eq(0).attr("data-spread") == "open" ? true : false; + temp_node.basicData = $div.attr("data-basic"); + temp_node.recordData = $div.attr("data-record"); + temp_node.dataType = $i.attr("data-type"); + temp_node.ischecked = $i.attr("data-checked"); + temp_node.initchecked = $i.attr("data-initchecked"); + return temp_node; + }; + + //判断复选框是否发生变更 + DTree.prototype.changeCheckbarNodes = function(){ + var flag = false; + var _this = this; + _this.obj.find("i[data-par]").each(function(){ + var $i = $(this); + $div = $i.closest("."+LI_DIV_ITEM); + + if ($i.attr("data-checked") != $i.attr("data-initchecked")) { + flag = true; + return true; + } + }); + return flag; + }; + + + /******************** iframe区域 ********************/ + // 加载iframe + DTree.prototype.loadIframe = function($div, iframeParam) { + var _this = this; + var $cite = $div.find("cite[data-leaf]").eq(0); + if (!_this.useIframe) { // 启用iframe + return false; + } + var iframeElem = _this.iframe.iframeElem, + iframeUrl = _this.iframe.iframeUrl, + iframeLoad = _this.iframe.iframeLoad; + + var flag = iframeLoad == "leaf" ? (($cite.attr("data-leaf") == "leaf") ? true : false) : true; + + if (flag) { + if ($(iframeElem).length > 0) { //iframe存在 + if (!iframeUrl) { + layer.msg("数据请求异常,iframeUrl参数未指定", {icon:5}); + return false; + } + var param = AjaxHelper.serialize(iframeParam); + if(iframeUrl.indexOf("?")> -1){ + param = "&"+param.substring(1, param.length); + } + var url = iframeUrl + param; + $(iframeElem).attr("src", url); + } else { + layer.msg("iframe绑定异常,请确认页面中是否有iframe页对应的容器", {icon:5}); + return false; + } + } + return flag; + }; + + // 获取传递出去的参数,根据iframe.iframeDefaultRequest、iframe.iframeRequest和node拼出发出请求的参数 + DTree.prototype.getIframeRequestParam = function(nodes){ + var _this = this; + var request = _this.iframe.iframeRequest, + defaultRequestNames = _this.iframe.iframeDefaultRequest, + node = nodes || _this.node, + requestParam = {}; + + // 先拼用户自定义的,在拼树生成的,这样的话用户可以自定义当树未生成时的节点的初始值 + for ( var key in request) { + requestParam[key] = request[key]; + } + for ( var key in defaultRequestNames) { + var paramName = defaultRequestNames[key]; + var paramValue = node[key]; + if(typeof paramValue === "boolean"){ + requestParam[paramName] = paramValue; + }else { + if(paramValue){ + requestParam[paramName] = paramValue; + } + } + } + + // 解决传递中文的乱码问题 + var reg = /[\u4E00-\u9FA5\uF900-\uFA2D]/; //正则匹配中文 + for(var key in requestParam){ + if(reg.test(requestParam[key])) { + var str = requestParam[key]; + requestParam[key] = encodeURI(encodeURI(str)); + } + } + + return requestParam; + }; + + /******************** 数据回调区域 ********************/ + // 获取当前选中节点下一个UL 或根节点。为了将新节点放入ul下 + DTree.prototype.getNowNodeUl = function() { + var _this = this; + return (_this.obj.find("div[data-id]").parent().find("."+NAV_THIS).length == 0) ? _this.obj : _this.obj.find("div[data-id]").parent().find("."+NAV_THIS).next("ul"); + }; + + // 获取当前选中节点 或根节点。 + DTree.prototype.getNowNode = function() { + var _this = this; + return (_this.obj.find("div[data-id]").parent().find("."+NAV_THIS).length == 0) ? _this.obj.children("li").eq(0).children("div").eq(0) : _this.obj.find("div[data-id]").parent().find("."+NAV_THIS); + }; + + // 设置当前选中节点的全部参数 + DTree.prototype.setNodeParam = function($div) { + var _this = this; + _this.node.nodeId = $div.attr("data-id"); + _this.node.parentId = $div.parent().attr("data-pid"); + _this.node.context = $div.find("cite[data-leaf]").eq(0).text(); + _this.node.isLeaf = $div.find("cite[data-leaf]").eq(0).attr("data-leaf") == "leaf" ? true : false; + _this.node.level = $div.parent().attr("data-index"); + _this.node.spread = $div.find("i[data-spread]").eq(0).attr("data-spread") == "open" ? true : false; + _this.node.basicData = $div.attr("data-basic"); + _this.node.recordData = $div.attr("data-record"); + if ($div.find("i[data-par]")) { + var dataTypes = "", ischeckeds = "", initcheckeds = ""; + $div.find("i[data-par]").each(function(){ + dataTypes += $(this).attr("data-type") + ","; + ischeckeds += $(this).attr("data-checked") + ","; + initcheckeds += $(this).attr("data-initchecked") + ","; + }); + dataTypes = dataTypes.substring(0, dataTypes.length-1); + ischeckeds = ischeckeds.substring(0, ischeckeds.length-1); + initcheckeds = initcheckeds.substring(0, initcheckeds.length-1); + + _this.node.dataType = dataTypes; + _this.node.ischecked = ischeckeds; + _this.node.initchecked = initcheckeds; + } + }; + + // 获取当前选中节点的全部参数 + DTree.prototype.getNodeParam = function($div) { + var _this = this; + if ($div) { + _this.setNodeParam($div); + } else { + if(_this.obj.find("div[data-id]").parent().find("."+NAV_THIS).length == 0){ + _this.initNodeParam(); + } + } + return this.node; + }; + + // 获取一个临时的node参数 + DTree.prototype.getTempNodeParam = function($div) { + var _this = this; + var temp_node = {}; + temp_node.nodeId = $div.attr("data-id"); + temp_node.parentId = $div.parent().attr("data-pid"); + temp_node.context = $div.find("cite[data-leaf]").eq(0).text(); + temp_node.isLeaf = $div.find("cite[data-leaf]").eq(0).attr("data-leaf") == "leaf" ? true : false; + temp_node.level = $div.parent().attr("data-index"); + temp_node.spread = $div.find("i[data-spread]").eq(0).attr("data-spread") == "open" ? true : false; + temp_node.basicData = $div.attr("data-basic"); + temp_node.recordData = $div.attr("data-record"); + if ($div.find("i[data-par]")) { + var dataTypes = "", ischeckeds = "", initcheckeds = ""; + $div.find("i[data-par]").each(function(){ + dataTypes += $(this).attr("data-type") + ","; + ischeckeds += $(this).attr("data-checked") + ","; + initcheckeds += $(this).attr("data-initchecked") + ","; + }); + dataTypes = dataTypes.substring(0, dataTypes.length-1); + ischeckeds = ischeckeds.substring(0, ischeckeds.length-1); + initcheckeds = initcheckeds.substring(0, initcheckeds.length-1); + + temp_node.dataType = dataTypes; + temp_node.ischecked = ischeckeds; + temp_node.initchecked = initcheckeds; + } + return temp_node; + }; + + // 重置参数 + DTree.prototype.initNodeParam = function(){ + var _this = this; + _this.node.nodeId = ""; + _this.node.parentId = ""; + _this.node.context = ""; + _this.node.isLeaf = ""; + _this.node.level = ""; + _this.node.spread = ""; + _this.node.dataType = ""; + _this.node.ischecked = ""; + _this.node.initchecked = ""; + _this.node.basicData = ""; + }; + + // 获取传递出去的参数,根据defaultRequest、request和node拼出发出请求的参数 + DTree.prototype.getRequestParam = function(nodes){ + var _this = this; + var request = _this.request, + defaultRequestNames = _this.defaultRequest, + node = nodes || _this.node, + requestParam = {}; + + // 先拼用户自定义的,在拼树生成的,这样的话用户可以自定义当树未生成时的节点的初始值 + for ( var key in request) { + requestParam[key] = request[key]; + } + for ( var key in defaultRequestNames) { + var paramName = defaultRequestNames[key]; + var paramValue = node[key]; + if(typeof paramValue === "boolean"){ + requestParam[paramName] = paramValue; + }else { + if(paramValue){ + requestParam[paramName] = paramValue; + } + } + + } + return requestParam; + }; + + // 获取filterParam过滤后的requestParam + DTree.prototype.getFilterRequestParam = function(requestParam){ + var _this = this; + var filterRequest = _this.filterRequest; + return event.cloneObj(requestParam, filterRequest); + }; + + // 获取当前选中值 + DTree.prototype.getNowParam = function(){ + var _this = this; + + return _this.getRequestParam(_this.getNodeParam()); + }; + + // 获取参数的上级节点 + DTree.prototype.getParentParam = function(id){ + var _this = this; + var $div = _this.obj.find("div[data-id='"+id+"']"); + if($div.length > 0){ return _this.callbackData().parentNode($div); } else { return {}; } + }; + + // 获取参数的下级节点 + DTree.prototype.getChildParam = function(id){ + var _this = this; + var $div = _this.obj.find("div[data-id='"+id+"']"); + if($div.length > 0){ return _this.callbackData().childNode($div); } else { return []; } + }; + + // 获取回调数据 + DTree.prototype.callbackData = function(){ + var _this = this; + return { + dom: function($dom){ // 获取dom + return $dom; + }, + node: function(node){ // 获取当前节点值 + return _this.getRequestParam(node); + }, + childNode: function($div){ // 获取下级节点值 + var $childDivs = $div.next("ul").find("li."+LI_NAV_ITEM+" div."+LI_DIV_ITEM); + var childNode = []; + if($childDivs && $childDivs.length > 0){ + $childDivs.each(function(){ + var $cDiv = $(this); + childNode.push(_this.getRequestParam(_this.getTempNodeParam($cDiv))); + }); + } + return childNode; + }, + parentNode: function($div){ // 获取上级节点值 + var pId = $div.parent().attr("data-pid"); + var $pdiv = _this.obj.find("div[data-id='"+pId+"']"); + if($pdiv.length > 0) {return _this.getRequestParam(_this.getTempNodeParam($pdiv));} else {return {};} + + } + } + }; + + /******************** 事件回调区域 ********************/ + // 绑定浏览器事件 + DTree.prototype.bindBrowserEvent = function(){ + var _this = this; + + // 绑定文件夹展开/收缩的图标的点击事件,点击时给当前节点的div添加选中class + _this.obj.on("click", "i[data-spread]", function(event) { + event.stopPropagation(); + var $i = $(this), + $div = $i.parent("div"), + $cite = $div.find("cite"), + node = _this.getNodeParam($div), + $ul = $div.next("ul"), + $p_li = $div.parent("li[data-index]"), //当前选中节点的顶级li节点 + $p_ul = $p_li.parent("ul"); + var $toolBarDiv = _this.obj.prevAll('div#dtree_toolbar_'+_this.obj[0].id); + $toolBarDiv.find(".layui-nav-child").removeClass('layui-anim-fadein layui-show'); + + _this.obj.find("div[data-id]").parent().find("."+NAV_THIS).removeClass(NAV_THIS); + _this.obj.find("div[data-id]").parent().find("."+_this.style.itemThis).removeClass(_this.style.itemThis); + $div.addClass(NAV_THIS); + $div.addClass(_this.style.itemThis); + + _this.clickSpread($div); // 展开或隐藏节点 + + // 树状态改变后,用户自定义想做的事情 + layui.event.call(this, MOD_NAME, "changeTree("+$(_this.obj)[0].id+")", {param: _this.callbackData().node(node), dom: _this.callbackData().dom($i), show: _this.callbackData().dom($i).attr("data-spread") == "open" ? true : false}); + }); + + // 绑定所有子节点div的单击事件,点击时触发加载iframe或用户自定义想做的事情 + _this.obj.on("click", "div[dtree-click='"+eventName.itemNodeClick+"']", function(event) { + event.stopPropagation(); + var $div = $(this), + $cite = $div.find("cite"), + node = _this.getNodeParam($div), + $ul = $div.next("ul"), + $p_li = $div.parent("li[data-index]"), //当前选中节点的顶级li节点 + $p_ul = $p_li.parent("ul"); + var $toolBarDiv = _this.obj.prevAll('div#dtree_toolbar_'+_this.obj[0].id); + $toolBarDiv.find(".layui-nav-child").removeClass('layui-anim-fadein layui-show'); + + _this.obj.find("div[data-id]").parent().find("."+NAV_THIS).removeClass(NAV_THIS); + _this.obj.find("div[data-id]").parent().find("."+_this.style.itemThis).removeClass(_this.style.itemThis); + $div.addClass(NAV_THIS); + $div.addClass(_this.style.itemThis); + + if (_this.useIframe) { + var iframeParam = _this.getFilterRequestParam(_this.getIframeRequestParam(node)); + var flag = _this.loadIframe($div, iframeParam); // 加载iframe + if (flag) { + // iframe加载完毕后,用户自定义想做的事情 + _this.iframeFun.iframeDone(iframeParam); + + layui.event.call(this, MOD_NAME, "iframeDone("+$(_this.obj)[0].id+")", {"iframeParam": iframeParam, dom: _this.callbackData().dom($div)}); + } + } else { + // 单击事件执行完毕后,用户自定义想做的事情 + layui.event.call(this, MOD_NAME, "node("+$(_this.obj)[0].id+")", {param: _this.callbackData().node(node), childParams: _this.callbackData().childNode($div), parentParam: _this.callbackData().parentNode($div), dom: _this.callbackData().dom($div)}); + } + }); + + // 绑定所有子节点div的双击事件,暴露on给用户自定义 + _this.obj.on("dblclick", "div[dtree-click='"+eventName.itemNodeClick+"']", function(event) { + event.stopPropagation(); + var $div = $(this), + $cite = $div.find("cite"), + node = _this.getNodeParam($div), + $ul = $div.next("ul"), + $p_li = $div.parent("li[data-index]"), //当前选中节点的顶级li节点 + $p_ul = $p_li.parent("ul"); + var $toolBarDiv = _this.obj.prevAll('div#dtree_toolbar_'+_this.obj[0].id); + $toolBarDiv.find(".layui-nav-child").removeClass('layui-anim-fadein layui-show'); + + _this.obj.find("div[data-id]").parent().find("."+NAV_THIS).removeClass(NAV_THIS); + _this.obj.find("div[data-id]").parent().find("."+_this.style.itemThis).removeClass(_this.style.itemThis); + $div.addClass(NAV_THIS); + $div.addClass(_this.style.itemThis); + // 双击事件执行完毕后,用户自定义想做的事情 + layui.event.call(this, MOD_NAME, "nodedblclick("+$(_this.obj)[0].id+")", {param: _this.callbackData().node(node), childParams: _this.callbackData().childNode($div), parentParam: _this.callbackData().parentNode($div), dom: _this.callbackData().dom($div)}); + }); + + //绑定所有子节点div的右键点击事件,用于显示toolbar + _this.obj.on("contextmenu", "div[dtree-click='"+eventName.itemNodeClick+"'][d-contextmenu]", function(e){ + var $div = $(this), + node = _this.getNodeParam($div), + contextmenu = $div.attr("d-contextmenu"); + if(_this.toolbar){ + var $toolBarDiv = _this.obj.prevAll('div#dtree_toolbar_'+_this.obj[0].id); + $toolBarDiv.find(".layui-nav-child").removeClass('layui-anim-fadein layui-show'); + + // toolbar加载前执行的方法,执行完毕之后创建按钮 + _this.setToolbarDom(_this.toolbarFun.loadToolbarBefore(event.cloneObj(_this.toolbarMenu), _this.getRequestParam(node), $div)); + + var e = e || window.event, + mx = e.pageX - $div.offset().left +45 , + my = $div.offset().top - _this.obj.closest(_this.toolbarScroll).offset().top +15; + if(contextmenu == "true"){ + _this.obj.find("div[data-id]").parent().find("."+NAV_THIS).removeClass(NAV_THIS); + _this.obj.find("div[data-id]").parent().find("."+_this.style.itemThis).removeClass(_this.style.itemThis); + $div.addClass(NAV_THIS); + $div.addClass(_this.style.itemThis); + $toolBarDiv.find(".layui-nav-child").addClass('layui-anim-fadein layui-show'); + $toolBarDiv.css({'left':mx+'px','top':my+'px'}); + } + } + e.stopPropagation(); + return false; + }); + + // 绑定装载树的上层出现滚动条的容器,让toolbar隐藏 + _this.obj.closest(_this.toolbarScroll).scroll(function() { + var $toolBarDiv = _this.obj.prevAll('div#dtree_toolbar_'+_this.obj[0].id); + $toolBarDiv.find(".layui-nav-child").removeClass('layui-anim-fadein layui-show'); + }); + + // 绑定toolbar的点击事件 + _this.obj.prevAll('div#dtree_toolbar_'+_this.obj[0].id).on("click", "a[dtree-tool]", function(event) { + event.stopPropagation(); + var $div = _this.getNowNode(), + node = _this.getNodeParam($div), + $ul = $div.next("ul"), + $p_li = $div.parent("li[data-index]"), //当前选中节点的顶级li节点 + $p_ul = $p_li.parent("ul"), //当前选中节点的顶级li节点的父级ul + $p_div = $p_ul.prev("div"), //当前选中节点的顶级li节点的父级ul的前一个div + $cite = $div.children("cite"), //当前选中节点的text + title = $cite.html(); + var $toolBarDiv = _this.obj.prevAll('div#dtree_toolbar_'+_this.obj[0].id); + $toolBarDiv.find(".layui-nav-child").removeClass('layui-anim-fadein layui-show'); + var tool = $(this).attr("dtree-tool"); + switch (tool) { + case defaultTool.addToolbar: + var content = _this.loadToolBar(title, defaultTool.addToolbar); + + layer.open({ + title: "新增"+_this.toolbarStyle.title, + type: 1, + area: _this.toolbarStyle.area, + content: content, + success: function(layero, index){ + form.render(); + form.on("submit(dtree_addNode_form)",function(data){ + var data = data.field; + var parentId = $div.attr("data-id"), + id = $div.attr("data-id")+"_node_"+$ul[0].childNodes.length, + isLeaf = true, + isChecked = "0", + level = parseInt($p_li.attr("data-index"))+1; + + // 创建子节点的DOM,添加子节点 + var checkArr = []; + if (_this.checkArrLen > 0) { + for (var i = 0; i < _this.checkArrLen; i++) { + checkArr.push({"type":i,"isChecked":"0"}); + } + } + + $ul.append(_this.getLiItemDom(id, parentId, data.addNodeName, true, "", checkArr, level, false, false, "", "", "item")); + // 先将li节点隐藏 + $ul.find("li[data-id='"+id+"']").hide(); + // 重新赋值 + var $addDiv = $ul.find("div[data-id='"+id+"']"); + node = _this.getNodeParam($addDiv); + + //获取组装后的requestNode,组合参数 + var requestNode = _this.getRequestParam(node); + requestNode = $.extend(requestNode, data); + + _this.temp = [id, $ul, $div, level]; + // 用户自定义想做的事情 + _this.toolbarFun.addTreeNode(requestNode, $div); + + layer.close(index); + return false; + }); + } + }); + break; + case defaultTool.editToolbar: + var content = _this.loadToolBar(title, defaultTool.editToolbar); + + layer.open({ + title: "编辑"+_this.toolbarStyle.title, + type: 1, + area: _this.toolbarStyle.area, + content: content, + success: function(layero, index){ + _this.toolbarFun.editTreeLoad(_this.getRequestParam(node)); + form.render(); + form.on("submit(dtree_editNode_form)",function(data){ + var data = data.field; + $cite.html(data.editNodeName); + node = _this.getNodeParam($div); + var requestNode = _this.getRequestParam(node); + requestNode = $.extend(requestNode, data); + _this.temp = [$cite, $div]; + _this.toolbarFun.editTreeNode(requestNode, $div); + + layer.close(index); + }); + } + }); + break; + case defaultTool.delToolbar: + layer.confirm('确定要删除该'+_this.toolbarStyle.title+'?', {icon: 3, title:'删除'+_this.toolbarStyle.title}, function(index){ + var node = _this.getNodeParam($div); + _this.temp = [$p_li, $p_div]; + _this.toolbarFun.delTreeNode(_this.getRequestParam(node), $div); + + layer.close(index); + }); + break; + default: + var toolbarId = $(this).attr("dtree-tool"); + if(_this.toolbarExt.length > 0){ + for(var i=0; i<_this.toolbarExt.length; i++){ + var ext = _this.toolbarExt[i]; + if (toolbarId == ext.toolbarId){ + ext.handler(_this.getRequestParam(_this.getNodeParam($div), $div)); + break; + } + } + } + break; + } + }); + + // 绑定menubar的点击事件 + _this.obj.prevAll('div#dtree_menubar_'+_this.obj[0].id).on("click", "button[d-menu]", function(event) { + event.stopPropagation(); + var $toolBarDiv = _this.obj.prevAll('div#dtree_toolbar_'+_this.obj[0].id); + $toolBarDiv.find(".layui-nav-child").removeClass('layui-anim-fadein layui-show'); + _this.menubarListener($(this).attr("d-menu"), "group"); + }); + + // 绑定menubar的点击事件 + _this.obj.prevAll('div#dtree_toolbar_'+_this.obj[0].id).on("click", "a[d-menu]", function(event) { + event.stopPropagation(); + var $toolBarDiv = _this.obj.prevAll('div#dtree_toolbar_'+_this.obj[0].id); + $toolBarDiv.find(".layui-nav-child").removeClass('layui-anim-fadein layui-show'); + _this.menubarListener($(this).attr("d-menu"), "toolbar"); + }); + + // 绑定menubar的点击按钮事件 + _this.obj.closest('body').find("*[dtree-id='"+_this.obj[0].id+"'][dtree-menu]").on("click", function(event) { + event.stopPropagation(); + var $toolBarDiv = _this.obj.prevAll('div#dtree_toolbar_'+_this.obj[0].id); + $toolBarDiv.find(".layui-nav-child").removeClass('layui-anim-fadein layui-show'); + _this.menubarListener($(this).attr("dtree-menu"), "freedom"); + }); + + // 绑定cheboxbar的节点复选框 + _this.obj.on("click", "i[dtree-click='"+eventName.checkNodeClick+"']", function(event) { + var $toolBarDiv = _this.obj.prevAll('div#dtree_toolbar_'+_this.obj[0].id); + $toolBarDiv.find(".layui-nav-child").removeClass('layui-anim-fadein layui-show'); + var $i = $(this), + $div = $i.closest("div[dtree-click='"+eventName.itemNodeClick+"']"), + node = _this.getNodeParam($div); + // 复选框选中前的回调 + var flag = _this.checkbarFun.chooseBefore($i, _this.getRequestParam(node)); + _this.temp = [$i]; + if(flag){_this.changeCheck();} + event.stopPropagation(); + }); + }; + + // 绑定body的单击,让本页面所有的toolbar隐藏 + $BODY.on("click", function(event){ + $("div."+LI_DIV_TOOLBAR).find(".layui-show").removeClass('layui-anim-fadein layui-show'); + }); + + // 解绑浏览器事件 + DTree.prototype.unbindBrowserEvent = function(){ + var _this = this; + + // 本身事件解绑 + _this.obj.unbind(); + // 菜单栏解绑 + if(_this.menubar){ + _this.obj.prevAll('div#dtree_menubar_'+_this.obj[0].id).unbind(); + if(_this.obj.closest('body').find("*[dtree-id='"+_this.obj[0].id+"'][dtree-menu]").length > 0){ + _this.obj.closest('body').find("*[dtree-id='"+_this.obj[0].id+"'][dtree-menu]").unbind(); + } + } + + // 工具栏解绑 + if(_this.toolbar){ + _this.obj.prevAll('div#dtree_toolbar_'+_this.obj[0].id).unbind(); + if(_this.obj.closest(_this.toolbarScroll).length > 0){ + _this.obj.closest(_this.toolbarScroll).unbind(); + } + } + }; + + + /** 外部访问 **/ + var dtree = { + render: function(options){ // 初始化树 + var dTree = null; + var id = event.getElemId(options); + if(id == "") { + layer.msg("页面中未找到绑定id", {icon:5}); + } else { + dTree = DTrees[id]; + if(typeof dTree === 'object'){ + dTree.reloadSetting(options); + dTree.initTreePlus(); + dTree.openTreePlus(); + dTree.init(); + dTree.unbindBrowserEvent(); + dTree.bindBrowserEvent(); + } else { + // 创建树 + dTree = new DTree(options); + // 添加到树数组中去 + DTrees[id] = dTree; + dTree.initTreePlus(); + dTree.openTreePlus(); + dTree.init(); + dTree.bindBrowserEvent(); + } + } + + return dTree; + }, + reload: function(dTree, options){ + if(typeof dTree === "string"){ + dTree = DTrees[dTree]; + } + if(typeof dTree === "undefined"){ + layer.msg("方法获取失败,请检查ID或对象传递是否正确",{icon:2}); + return ; + } + dTree.reloadSetting(options); + dTree.initTreePlus(); + dTree.openTreePlus(); + dTree.init(); + dTree.unbindBrowserEvent(); + dTree.bindBrowserEvent(); + }, + on: function(events, callback) { // 绑定事件 + if(events.indexOf("'") > 0){ + events = events.replace(/'/g,""); + } + if(events.indexOf('"') > 0) { + events = events.replace(/"/g,""); + } + return layui.onevent.call(this, MOD_NAME, events, callback); + }, + getNowParam: function(dTree){ + if(typeof dTree === "string"){ + dTree = DTrees[dTree]; + } + if(typeof dTree === "undefined"){ + layer.msg("方法获取失败,请检查ID或对象传递是否正确",{icon:2}); + return ; + } + return dTree.getNowParam(); // 获取当前选中值 + }, + getParentParam: function(dTree, id){ // 获取参数的上级节点 + if(typeof dTree === "string"){ + dTree = DTrees[dTree]; + } + if(typeof dTree === "undefined"){ + layer.msg("方法获取失败,请检查ID或对象传递是否正确",{icon:2}); + return ; + } + return dTree.getParentParam(id); + }, + getChildParam: function(dTree, id){ // 获取参数的全部下级节点 + if(typeof dTree === "string"){ + dTree = DTrees[dTree]; + } + if(typeof dTree === "undefined"){ + layer.msg("方法获取失败,请检查ID或对象传递是否正确",{icon:2}); + return ; + } + return dTree.getChildParam(id); + }, + getCheckbarNodesParam: function(dTree){ + if(typeof dTree === "string"){ + dTree = DTrees[dTree]; + } + if(typeof dTree === "undefined"){ + layer.msg("方法获取失败,请检查ID或对象传递是否正确",{icon:2}); + return {}; + } + return dTree.getCheckbarNodesParam(); // 获取复选框选中值 + }, + dataInit: function(dTree, chooseId){ // 初始化选中树,针对数据返选 + if(typeof dTree === "string"){ + dTree = DTrees[dTree]; + } + if(typeof dTree === "undefined"){ + layer.msg("方法获取失败,请检查ID或对象传递是否正确",{icon:2}); + return ; + } + if(chooseId){ + return dTree.dataInit(chooseId); + } + }, + chooseDataInit: function(dTree, chooseIds){ // 初始化复选框的值 + if(typeof dTree === "string"){ + dTree = DTrees[dTree]; + } + if(typeof dTree === "undefined"){ + layer.msg("方法获取失败,请检查ID或对象传递是否正确",{icon:2}); + return ; + } + if(chooseIds){ + return dTree.chooseDataInit(chooseIds); + } + }, + changeCheckbarNodes: function(dTree){ //判断复选框是否发生变更 + if(typeof dTree === "string"){ + dTree = DTrees[dTree]; + } + if(typeof dTree === "undefined"){ + layer.msg("方法获取失败,请检查ID或对象传递是否正确",{icon:2}); + return ; + } + return dTree.changeCheckbarNodes(); + }, + refreshTree: function(dTree){ //刷新树,并具有数据回显的功能,自动识别复选框or单选(未完成) + if(typeof dTree === "string"){ + dTree = DTrees[dTree]; + } + if(typeof dTree === "undefined"){ + layer.msg("方法获取失败,请检查ID或对象传递是否正确",{icon:2}); + return ; + } + }, + escape: function(html){ + return event.escape(html); + }, + unescape: function(str){ + return event.unescape(str); + }, + version: function(){ + return VERSION; + } + }; + + exports('dtree', dtree); +}); \ No newline at end of file diff --git a/static/layui_ext/dtree/font/dtreefont.css b/static/layui_ext/dtree/font/dtreefont.css new file mode 100755 index 00000000..df7af653 --- /dev/null +++ b/static/layui_ext/dtree/font/dtreefont.css @@ -0,0 +1,229 @@ +@font-face { + font-family: 'dtreefont'; + src: url('dtreefont.eot?x3m8fp'); + src: url('dtreefont.eot?x3m8fp#iefix') format('embedded-opentype'), + url('dtreefont.ttf?x3m8fp') format('truetype'), + url('dtreefont.woff?x3m8fp') format('woff'), + url('dtreefont.svg?x3m8fp#dtreefont') format('svg'); + font-weight: normal; + font-style: normal; +} + +[class^="dtree-icon-"], [class*=" dtree-icon-"] { + /* use !important to prevent issues with browser extensions that change font */ + font-family: 'dtreefont' !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; +} + +.dtree-icon-xiangxia1:before { + content: "\e771"; +} +.dtree-icon-normal-file:before { + content: "\e60c"; +} +.dtree-icon-xiangyou:before { + content: "\e78f"; +} +.dtree-icon-ok-circle:before { + content: "\1005"; +} +.dtree-icon-close1:before { + content: "\1006"; +} +.dtree-icon-close-fill:before { + content: "\1007"; +} +.dtree-icon-jian1:before { + content: "\e600"; +} +.dtree-icon-jia1:before { + content: "\e601"; +} +.dtree-icon-bianji:before { + content: "\e602"; +} +.dtree-icon-yonghu:before { + content: "\e603"; +} +.dtree-icon-shijian:before { + content: "\e606"; +} +.dtree-icon-fuxuankuang-banxuan:before { + content: "\e607"; +} +.dtree-icon-star:before { + content: "\e608"; +} +.dtree-icon-wenjianjiazhankai:before { + content: "\e60e"; +} +.dtree-icon-xiangmuxiaoxi:before { + content: "\e60f"; +} +.dtree-icon-search2:before { + content: "\e615"; +} +.dtree-icon-weibiaoti5:before { + content: "\e618"; +} +.dtree-icon-layim-theme:before { + content: "\e61b"; +} +.dtree-icon-shuye1:before { + content: "\e61e"; +} +.dtree-icon-add-circle:before { + content: "\e61f"; +} +.dtree-icon-xinxipilu:before { + content: "\e620"; +} +.dtree-icon-set-sm:before { + content: "\e621"; +} +.dtree-icon-about:before { + content: "\e623"; +} +.dtree-icon-chart-screen:before { + content: "\e62a"; +} +.dtree-icon-delete1:before { + content: "\e640"; +} +.dtree-icon-share3:before { + content: "\e641"; +} +.dtree-icon-youjian:before { + content: "\e642"; +} +.dtree-icon-check:before { + content: "\e645"; +} +.dtree-icon-close:before { + content: "\e646"; +} +.dtree-icon-favorfill:before { + content: "\e64b"; +} +.dtree-icon-favor:before { + content: "\e64c"; +} +.dtree-icon-fuxuankuangxuanzhong:before { + content: "\e652"; +} +.dtree-icon-fenguangbaobiao:before { + content: "\e655"; +} +.dtree-icon-jian:before { + content: "\e656"; +} +.dtree-icon-jia:before { + content: "\e657"; +} +.dtree-icon-fenzhijigou:before { + content: "\e658"; +} +.dtree-icon-roundcheckfill:before { + content: "\e659"; +} +.dtree-icon-roundcheck:before { + content: "\e65a"; +} +.dtree-icon-roundclosefill:before { + content: "\e65b"; +} +.dtree-icon-roundclose:before { + content: "\e65c"; +} +.dtree-icon-roundrightfill:before { + content: "\e65d"; +} +.dtree-icon-roundright:before { + content: "\e65e"; +} +.dtree-icon-like:before { + content: "\e66c"; +} +.dtree-icon-samefill:before { + content: "\e671"; +} +.dtree-icon-same:before { + content: "\e672"; +} +.dtree-icon-evaluate:before { + content: "\e674"; +} +.dtree-icon-circle1:before { + content: "\e687"; +} +.dtree-icon-radio:before { + content: "\e688"; +} +.dtree-icon-caidan_xunzhang:before { + content: "\e68e"; +} +.dtree-icon-pulldown:before { + content: "\e6a0"; +} +.dtree-icon-pullup:before { + content: "\e6a1"; +} +.dtree-icon-refresh:before { + content: "\e6a4"; +} +.dtree-icon-qrcode1:before { + content: "\e6b0"; +} +.dtree-icon-profile1:before { + content: "\e6b7"; +} +.dtree-icon-home1:before { + content: "\e6b8"; +} +.dtree-icon-homefill:before { + content: "\e6bb"; +} +.dtree-icon-roundaddfill:before { + content: "\e6d8"; +} +.dtree-icon-roundadd:before { + content: "\e6d9"; +} +.dtree-icon-fuxuankuang:before { + content: "\e6f2"; +} +.dtree-icon-wefill:before { + content: "\e6f5"; +} +.dtree-icon-sort:before { + content: "\e701"; +} +.dtree-icon-repair:before { + content: "\e738"; +} +.dtree-icon-shujudaping:before { + content: "\e742"; +} +.dtree-icon-dian:before { + content: "\e7a5"; +} +.dtree-icon-search_list_light:before { + content: "\e807"; +} +.dtree-icon-round_list_light:before { + content: "\e82b"; +} +.dtree-icon-star-fill:before { + content: "\e832"; +} +.dtree-icon-rate:before { + content: "\e833"; +} +.dtree-icon-move-up:before { + content: "\ea47"; +} +.dtree-icon-move-down:before { + content: "\ea48"; +} diff --git a/static/layui_ext/dtree/font/dtreefont.eot b/static/layui_ext/dtree/font/dtreefont.eot new file mode 100755 index 0000000000000000000000000000000000000000..68bf5f2f2dd33daa67373cca102f8ad764d84c22 GIT binary patch literal 19508 zcmb_^37i~NoqxUe>gcNO>Z|&mt9$yKdwQ;(Ofr+ngiKEoLXt@!h&d)eNCLUI1Ok!) zl_Q`F=pV$t!f@@PD7w1qx+pTRu%e4=KCYs&s~gcB5LpEO%Zj_1n*F|4JxS*v{^9?x znR@lBSFhgjd%yR)-mkjnOi8+9r6duFO2;2Wx`XhXGuIrS&&xW;E8bb}iO(d-EA5mn zkoHQuaqgA&pnz8zm$pgUr7NXf(pKp*^P}6hUcL|ceo3NJxVpCQy5^#R%oT6r z{!vL4Uc^Kw-OLAP9sL7IX5XyGMsoYk9=Mx$rI5UZ?P87 zoGTNhY9*QMK>j(>FxrnGFX`82*oPchndX_1nbDauXU1nX&+MAH zZ03rYTV_5n^T5o*Ghdnc#>_Woem3*_nYUjduk^n%^2(*J{MjpyysEw0@@n^MV{@}} zbG!qnSDBG!RMhLO)LSp=9iCY>vl8{5je0Ldy*H!YDN*m=px&RJvfi%O%A#H}_p9H{ zCA5T+pa~K`692FGJMsUFza9Tw{H6F$<3Ec3ApZUMbo^WKZ^l0#e>nch_?_{a;*;^~ z&313zHpqJ7D(cG~%PoKx6 zk-4YY-?KBM4r!&dR+5y)T)v$+|GhnZr6J<{cXvgi^~CuPhg8i=oPXBHuWC5NA4R&m zcfcQj?{T2Y^&?g;8i)i+nqO_(Fzx}4BIi>x0fF(|)4@oI@ko95xEJ8O< zgT=HJ32AC$2e90j&-E+@VtYz`T@Aoj{#mDjEvD?4K1eo0$u9 z(l34B^y5kgPB*YLA5WuNN04BI3ke@)SKlB(QW@%4*%>Ojyyh6ZrfPWQ(W;^D_Zn{R zem>CsUbo>u%mq}HfvR%(5>DWkI56s+P1hs@vWDi71;hb~i5jwJzuW6|qdLR4pO1Y% zM**1s6LWKHJEKwnJUb<2B`Hh#avdb6Y9Ktm_7@W?O7f%x&`0u^&M07w{FvMoQlp=W zF0$<1QKG1dSNp?dU$B4v7|Cq@ltSdpV;R|_*yH|BKe~kcA~I-wIHW}F#yQQD6-Gzs zqC4zAzk{6jfe)ip%N;FvWA>8AZ)n4OP~ws-X3`^&!b!|VNKH59z>~m=tO|S_QPFSj zT)ATB&J`le^S)s2 zZ`pTPSF51)^vnY3K&qEYo7b(oHiP5Zb?Y`)!|Rrh?c(IUYi#+tYIp&uW95^J zJFgyj?Bo}mm&qqct^L>n^#8-4+mB)H42%FeLwH~SpT&;F*XT<)ULu2E9%n*B|*$CtwKaJhDiH%N)>M-(oVvi+2#`#7goJFG!Dz8~1#HG7vfym02G=<52<8{r$E>ePScTl;Ad9>FJzswZvONvYi6h$M( zKjJm}dBsK0PeU>IMJJ>O8B|k0GeF&{BGW;tum|aSRoSt_!@T}t;+GFuUGaEIPJJg< zR~P#ZfOx#idgzylqEB~yh0?tWQB}+Moo1pVmZ6(8OaR< z!%`Kh?4^`^P*YTcq66pw)m7Y(`j4S?^x<|U6No$}P0t->!z?Ljl8Lo1S%FlbgQlXX zaCaa@qUqjgI;l?^_PSv*JZ(#4a(tX6k4)3u#Q&{vUjKoDHi!YqLrRgF2+SF-v^kryDV$REj)j>GtGACcv$+FUy=+FED6%YWN-$QC)Q9^KaP=4#^e`zdCt|3ODENSF#vgO9) z!gxII_4<5XZ$2Jhm~1R(vty~$KsnIGiSxqIEF>2WvsXYWfkHYRgjUAb#`Rq@0?vT-c?Zp}6( z2NJw9UixtCq;^F6u_IqK-S_71l@&#H8YG{~e$G=L@HeE}-qyAj?R~r}$LYII zGT9FqlcLZLBygYT0CAWqXS4H6HeAF5@(+5k)`dlRezdVMdgLe3bUI2>;RcmHA5W5W zZ1(L~Ivt~vu{0qCEIBGIkyf#%=wD;)3~{PWYn`elX0ix4T4E^{EooND((HTarozG_6oLFPZXr>l@CfuQ&4>jD|D(M!3Fok4$t|Q*+ZuI82DH*FiyO zSQ}AXitI8Bmu486?1Ejb-l+OyexWLrT~wj!KPWyKm+vuTsPqYN62+(R3)AQ?1t z#EkqYm9%&|ODKY;upQx2QC{&|OLLI8qut{iGN50748(UXh-#XOYz@^zF z`!le|~(;f9(7$#L(lF}W9{pTaSTnDWz zcA?Y5N?gp>qQfA$dLIe1@V2KLVthZ?^RVXmCYYyDijT-s)<7XEyvz{xvML=Ae~;2{B^f;?e2qW&utnn z*|)OySk0aHv@q8C>Dg&sCplc`e_C@hCNEe(p(rr7_B7g?n12;g^pd$BvahnZ^d#9& z?j{d{f_j8!z>zRHXBCSa7|mX+ZWSxG*ePmdfE;4B195@bo8mv97}yppM<^XJcZ<1L^Sv;<~!urGq9aSE0rE= zYTwZgwQzNE-U~9{>*MM@_OzW066n{Jj=(ZMuAI~BN;lCFC_%jMRiml&x{~8`0bM&f zTnO7}_B2u&T+y+KP>_8!;Poz5xW4IPlqxb1P7R}=YBjpP*yC~~T;PU`Mm=WH^n@vK zQ>sT?F1Op0GrcvstHI+&`M9bjCnk+V8F{L#-plBFc$u**X3dXe?oWlD2ki1Jfyc=tLsp5 zQ7G(oFG9n*%=*3F7_3n+Z#^kP#^+?&kd;oiJK-^@$21u=(4aEJm_~?6sNbbQG{j|P zsmxHYfT1#$b%DX7fq-d_C}@D#H#TQ7B_Fz8%4C|GvY9?Vj=oH`Y1zcYvNN`BC3pLy zl(~scSs3e9(|RCD3?r{;n%V}=?AG1JLemUbZey|Aqj99VFgu3g(i`0F2Gg6?4G);S z%cWx6l*N2Yf+3l4;{i43hT-uPOb^Bj{L6;Xpelq=pwF%Ay#ap!^FIVP6lLf%I-Z2> z;&#VOv)-lmt6WU^_%Nu!7}#{KPf>}7QojK?W5hMrLRr>j85ku@uJP&^I@7d--&X_+ zQZ%jC>x;;YXs{~O9PmRKrqivc1&>+d^%Ae?LCvt*l@U!PI-{l+nD!(*o>o}xXX!OLzpG4wDj7T8gWvQU2QWXwA= zVt;b$8O|I)H3t70n)@dFnoET*=Pa%ppVr5AI`PI}ISB4_R1M}V!2%}}iymsR!y;hE z@`ZkUX_&kq+fD1nuT5t%>1)T=ZQ49Oer<-!vGMWE>sBn^oy4(w`HFSxm#^5(rQ+@t z%hzM7F!(`V4&F4e(zNOKZeN&qt(Bp$|uOaz}YQ(eF{h zbHTXb7=iFnvz@{ZS>)b1SgCa+3uhBA0G&V-$af0$pxE1)gLqJjVU5dqC<#Jo5U!LH zGkCehEprq~i_SXg1ImKSznz9e)a zj!dCgg%zj-0^(~3#aoK7)cAH;%}<@{v%CKaWRlRNShlU?cniAC~sB8|aPLlZ$}|w17Ui z2E*r;Pl@n32b3aQ>Li6F8i;a(p}VuI*agQ^G*wzm0(o{rA~?G)7>@_(mx2lVelu&@ zzoPr?m#6;upX&>rdOz{kds<$a{n*d`?Zp@A242u!9ZV#G3IzVD!*dDwLGiVjCrm(VPbiGS=uu&a%P zZlC=dq=te0AA)T@LKvi|$_*WU`?OBa(TQOX%s^z<=(D^4KPdmBtqyp)> z{h#Cjap?&Zx8DJvqqO~w{SGc%E)kdgHkVGQihzF*Y02C)dy)-fJ63}Amd`R!3kD2w zVxSe_?qd~SAM_$;mN?sG;J10aY3lY{SId6g@X7n_w{$OMvidBXjxH_e`u*>jOD3j8 zNZWHzX~^p?4{0Whn{>9_MBo7os^7DJr3WAjm5<#-sb#{BBhl|_0c?~KX}Hq2HOF}} zOBOUhw7z)W8b!Z+j@`-Kj*jCJ${a7nJzp3Nls-$r+;WGfJuk4IN-lzoQom#-#1-2|UdpKHfh zu@Z(Uz3F4{{V4&R)O}A4x^y>9pe{4a{9k}h@!LJJvgdWpr+mJV>e`Qge zjM--5ap?i=y8;=oA?Z;Y%7-;1k>i$5PY$a_?C%l!3;T?vTed7E-$H8NO33Zm9YoIB zKV{OErN7&uW z>?F^lNN3Nble3dpqfEL-O0cW2|G^{|d_$0FaLM8fU@C;t@8E}BHJhY|XUp`i**&yi zFM&GmB2OVr(3fYI(NE3pA|i~|k;Ai!Jme=!Hf7HG< z>i-R|14$nB@tU7;x%2NKm(MfB-u^S}+iVE(kH^}a#)wQ?q%*{247jcM2XZSWZf8#s z8z*AV8M`ig&l#3Gl{T&&zb1|2n(?(8tKpx_NmS<2rXrNMbwbGXLKba*4FUkNi4C3n zo>PZ?yFJTat~zjY0|W)6%f+t?L4wu5dGa*m1>b+Zi1TIMSF8xGeq(zOJ&b~J@R7i2 zC@!arPq4U82d_f*HW0|(Q0H;7*D>jVlzupVVAfM<69&yycMN({HN1LsbO#Sw*fBb~ zx(dDp!+mW#5<|cKRH9lkh(3IBZBET>_@w$Ob=!Z?_17;39wA_FI2l`np+#r@kKt+L z^z+X{4B$`Y-c#p#%0PqYyJFxxy(0T>Jt(vRSf&RL3SHsWgF;t1c#ys8NN2x}4s(m* z;H}UK6pZSYgB)qb!COTYw;bfW@Yq;N(&qO+=Zvc|V%~+W)4<2ebqb;O&FeZHh^eTa z-)gvOeyiuY@u0y~BXpT-J-6J7>5<|4a4QGrF{0o0t{6DYTpzfF6Tf~dPyw9W%1Mf& z>XzFp3e;_Uogc#u-#<7V#h+q!1%Y$I2LQF?T}QrP%2D)meb6!^f(*Qc=SsaqO|^Z5BZ@BPmw-$lgYcLw3;(U?;x)0H`eg7~f@+Jw2>S&w6< zvu@|%voNsx(P4uVqC?p718`5AZvIZJgVP4;vu|SxojQK*?$a!>K8cjX-s@rbs8{gu z=kj7ht-`~&KM<1z?&R1_(TXCD-5$rP!tS>d4g~FTp5q}_1FM)yk(P-4bZvVmG}_+L zTUeQN!-7k=WBv83FRia5i##Fh6?@2FD0k`V%+i{h1j*iPpyuhZ>Th zFj<;ey~N3kMK$FW0Wto^jxZJ1;@Jlekc(nRbg5JbYbIhA&r9NJiGPe($S zXIYxS81KRUGLxEwyq8FKI8B6^D7bd|1aifO;`}n?+l%w&H+RV@A(dZ5E*JdJ4#>ph zBrV<5Ts&zKSC=* zYL5GwN=N(lrO8Pm9UGZNELdfKq3i9P@%V;?10y2?3pemhR}R+=)R-|@aeGqB(kG9+ zqxCLM_8e35wGU6)hmVdNjo613`7gkk+O8a~uGfLxhT=9oF~wt^JS)o%^xTQ}WQ3+F z(hZQ~H?T#}PX?eDentAWvkofOpHOif(yj!EbPKIyeh1Mp4*JOg?sov)=dtOKcf9Z7 zBFb_4i8_+JcytqCh=^<+D}wz=XHx>vb?H2u^F^*hU}jK-(1bYp@h@OaM6u-Y3SI@7 zMmxK@qZL1rqn47p5!bZ%v(&VLCabMo)83wgBv9PSi>h0J4pV4vUsF@V%%Ek`&n!_q zTK`4ZL2SxqM`~-MalhYC)BrbR+s?_=EzVGxy8IgSq0CTSenSVh`2wn9`2F!{ZS6=l z+k|%~&Ld>Qjzrir;S4ugS7%fcjyx9csjKUaF+;^|dQ}UY{@6wjCw6Rr9qbUXl~L4X z@u2CoC=JJ=t&r1j!pM4!(Grcp<7%1SV63bt4-v)ZZL4p{wR9CP=;&&Axp&pQ`SBHC)yC>Vc30tHpc!R+B^TV@R6x$GNw$FX0AH>xXZ5 z3G}T?q^mJ5bX}NN@C1kryJD1k^7ulS-@oTx87_>mBDoDGR`LQ;g0-H)9%`?3n(-nQ zMMQ%-Td)DaG&zfhtw#@*DXk0JUk}$&I@&i93YR-N`}#UN%HhyR>2`0em+nHq)+2+1 z_SXjoA8F0y2e1|5AIRrgtKsrR7nS+hJvP?OPx6zCp}ySCy`;ATxTNulWu=bJawr@Q zl{-61%dTu}V(jo?#?IMz_*Yv#I=jjr7|7*YTk`n@0UQhR`IgpvZXn<%liSK;+qR9B zx7j~kE~@Tcexi@Fk+4@BA=%T(tv0UHzGp3Ghf$;NFu82mJb!hTTo#D3x3z-fsVZ zql$E2%yGmqM5JZVlb&KB>|k)8u#fve#lF(Kd~yU1mpYXod%;I4TTeoURb(HWwRq$d z=NnT_u%vLBa`eDs3CHPUo|qhuoY6A6cJ0*I@}77w7{Y$B>h(6))He3txTLeAV?#dI zP@C16*EA!{m5jCy4&A(L*}?^5hLLV=9U8m^H_eNKd(0U2VLcCW4>Y8K5nuzd+r$Qj z+gw&WJJ}twqD@W9QrYZTO@-#Bd=5eR@#ZGC(cF+MWY)H#Zss#H%jyTxY0DpIX}M~2 z^ri)so8&pg7SxRv7Q-XpF&=_GpsWZ&)dPP}+i6 zLF|Qatp+RwniKzgBNClggCbE-Nkzwk8%>Z|_EfGFE$t{?&Qn_o1Q#_mb#ycp z2JyK^a8aSquD2KR3j=VE&WRC=ako!TPbT^Jk%Xw^yzDeH{?SaZwsu8ZYquvxy`KI~ z#DIs0^XzD~XrjGQE1J&f37axgs;1YvNS|fd z-`8pl)Ii!~*?vK$_HfX9cc3;Nx*K&gl1@G#`;ex8sB>_z^F!ELX>)Qi2pR`TNJ}x! zX=X~zd{4+36!^_TQgN|!%f|7mRyI8aq2$=t?1Qm#%)Z$`ynx?Gc4WTq4K_z2Z9(s$ zOl#5zfU}U7NH_ZTT{#RliTz1Fl1~O|YXhJ32HSFEH-eWro`>PDoQA)$Upkj_!DjaoY%rP4tZiv&Yins)o5>~{;;}~TQMnpp z@rHMQvT2&j0mmMihv+wxv3QEVn~KMh4cW{Zr&cJE4e$QMe&qOCOi{C=FF4~bVJ92b zWJO=7@b!v&v$8TPHh%>X6=!D!Nle&7d`&w_g0m$HUCjCuL4Z_xPbjG%Yt`+gzJYCuC-YxKFzHx;+)8&YVGE8_OO?phIq$qD7E0R=@Mb7P9!T&^B_LT@*+UhmGERjRyJ^z-=bmvRSoOJKDr8en_yFP3SUpu z!1^n}{#(cC&aq-R`Gq3b&lHx8lc^nd=djdpQE;r@up=!_c}FIxBou6(QqvYURavHh zkNlr(4fK&yoOGD8Eq+eT1Bd4UGBy9s;gbOPxw!8BIQVj%Gz}FGdkOGX3I~?>3~~N@ zMDbw&|2~I7GY5S-`Z-9nC&yj1;6F$^b9`sNqS-=^52;0m$VL3c30`>Nr!N5~Jza>L zL}V3;B9ODPOBEF%0ntwJF^~{NJ)%>viBS0HKU_LIkfMi&X!djo_fAg{9yqb)_*OkC zbzr`bN+PlqfvlvP|Zn=jE7~y(2 z)?g(r9c$D_SP|~I7lmhdI`x#G<`=oYakcDum2ZDx-wUB|IC$F9+O{EINFnz@z5>qY zwwg!oKcnK`)IW$N(BFF-p4YF$w@O4aRpN^yAQK8X0yB6Oqw{6YK-CDO40#Yx94R3z z9v*7E$RkOh!Q1~7X$A+tc9nW=>Rbiiw0c-P(wx1x`wf3QS2LOqH6g5kB7W}!sSl_= zmu9j=xTR)U-q+y!_W&=E(`vdxwcemCYs9UE^vv4YkVVIw=Figga-tZl^9JzP{trA( zuc@`HQP@Cd@zIx?WFUpR-k(Z6g+Up{kHLH}*42v4ns^8_VlxoPgoI-VwFJW96rcy4 z-Int?dVm)ksO5t?uyT+WtWP7Rksv;Z5MMILZIxh?g)vj$1raGIyA<4zW5HYF#$zjj zM*{Z(F-N9#UB(W-7f9KFud9fml|Iy*i~E1qy)#!+rz1elrTNqkq|Q_DP}fS=G}qKA z${XDm=WFVQiDlKT$?74F4<-k^wN`h{axvFnZDkS))}6(l0v-6k3{Q)RQC&aFK^Q(` z(&2kC5X6CyEtOAO2=Kd_=|}!Re=}R6-@T8A+*|_> zCnj$ZCGays(O!Yc6g&vQ59SWsf00enEfOLctVT7;KNE*lolZSe zUmGQxd<7rb6ip;&N9mTKsQsYNM>aE%hCU`F!ir^hYRpp?dkkn9=Z1(x= z2P2EqC)B~;n~)CEDVl_gIl;F`CN|?+5`L0N=en7H3%3*TWlzu_!!llqAHk@6_W+LU z@VuiopI~vEafSi$E4<$1RF+=aaoc4b`VnD`Af~XOE_l)%L+^Mgmu64gStt@Kw6wLY zySue*sTB%YOJR|v25au*QN}96$27L;_m{F+1X5>9Jdhf2Q?r2W>1hL7Od{dHq)5m* z`!NoXvk?bvTsYl$et&mc+lYl@qz&x|IN+nx;1)!)RB~N&Cfm#r%mcis_r0 zUpw?ef7a{svX+Ovx}w%v|8|ip84M=1?Y|Dz;adswktW&a)hu_5EbrK%Gq;fm9=_Mi z`GaxVd?*kK1r8NhEEvp~|NC&L5&VGnhazV28Tjjl!JV;ZL9%Y5L|Im02m;J6CfI_G zvZw49ZoAEX;a_|4>1Vk2@S(!je!p|aTYs4)uXT{T{j-i=+uyqPUUJ68SN`_v$-{>m z|N6J6*DYvoicU)^zFhK3LBxF}qwS%&Px6muJipH78o+{4M4qgpY#&xClAv`x{FRR ziH*wj@`!w+d_?)Ga#$VG4DFz+**NGPc3sz6X;T_@WNFnkX(zy@OMAKrPv-ralm?tzG}`n9Wbwq17l&b@n*#rDoq|HRcP-#TUH__pm=?%I0U z@xPsQ%95jd_gs + + +Generated by IcoMoon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/static/layui_ext/dtree/font/dtreefont.ttf b/static/layui_ext/dtree/font/dtreefont.ttf new file mode 100755 index 0000000000000000000000000000000000000000..78bacd13a0a16b55e6b3be536b35e34e147a396a GIT binary patch literal 19344 zcmb_^37lM2neRR4+@cfRv&=ev=JBuQ>*N@7xR?S`&yYeM}GcZI>pfB&{r>{O;Y`uG%M^Ngl!T^LS3~*|vN8y{q*9 z#`CMFf78CbmtBr=;ZCf*--K@sp2cjY!gK z<7{evN}6K0P{46f`y~ayd*fF<%A7kBwdy6A>_q;V(g^yGATR0HX4$N3)-&sy4bR4A z>t<84>DkuV(b=)t_svesZk@e&_R`tQXK$JP`0N9-56^yi_Up6XnEmPO?`GeAg}gHG z%IGVXyz*zSJo2jcYTK(luZ_>o&CiPwB>l>)G^?UrZ?)Y<(eB9XirLj@_YAaq3EI6G z?M{n!{|4>;Z{sh;e-i&;{QL3m z#b@H*jDI8kx%k8JPsHzx-xQyUUl%_&zA8Q*ABt-+J9aSk)!2Qpp;#goig4U4#GM%U z|EGjV1eowGXdm=aUL?9Z+UKeBm^3>7H2ZsYn$#t&mexy>(wxh866de4x4%40oWGv# zNVJhSf8mg-d5QDKn)zJ~hj>w>r<>RA@9X6anEvR|4RkX0M z)s~9WDjRERyl||tZv9y0!p0`Rm9h2fDr11>uKNXVZY`(l7o;;-FMU#Kk|dqxnl&{P z)d`YFcUdP$`g`*_L6U1`KKm{EEmzp(auI;Ig#i$s$g$r7fE@ay18EV>epA%eG~&14 z)HJ8I9|hRrRQjNlVh311_RkUw;}lp-Tal2aHg^Hb&G}sKQXsas+~3^&Gk3oGRn%E5l2-)Zw>f>2Rg8bl8E1m%h(_$?*9MlH)1UF(1@Dv5mRpkvF&UR<;f= ztvD@`eLlnRdJV(JwUhPODu%r7D77Mmk`Jr zt|1GE0}>N;WY2!L*Xu@ehHpQg`+klBF#jj!=h+TMr2u$#O3F%7mh|VkNKVy2czo|K zC03N=Ng1Gz|Lif6+4@O^$eG77vPZEe z{GWPs8Tol+$of!7iQ3Kcnkg%cj?yJ}*nfTpIs5$|Lanwt+VIBQMUUUmf%TxoC0Wd* zharWNSdEaHZq9)xffZR*_!y$1-`cf$)vjHuR_|K-sf#YUClso5b@lakb&Z9>bmi(@ zqR=+C6pLFr3oS#O>kPFNI{CUUn*Uq&ZB`NVJap=HKNUT*Ksu1>qtez58?MRVcg=(TYb5?=V*TJ*WHy$H;s0WkxKX6 zufq)uVH)M>+^?1XEUBmV%h`VSST~JzkBxQDz1lrSpF!cPqGBxEfHjk)^!yjt&)7z( zO}aq3Qu?@%D4ch3u>#)J$>qsXI*gqVX`)N2AB~7hchLfol`m=vll{i)nulGW z1l{hSasl&buN!`uDcTnmnG`6hMvQ;NYxeVsi(s6FV(^ZG{LsVf8 z(v7OJbEk)S{iVb&9 z^As)WJMFtjK4^H!$7{ebg_lA;e`Fh^qO?ZZEnN+s{T{V9NU?WU-@q#%064#g)WW8O z;Oe6M(b50X*qB^Hj6zw`$lqiu&B?{_c;4&v`MlnIJia*DT*+p~Q>npt0=fK`Ps9gP zsqyT{qD33y_-$OYXk^)%{1wxEZ|+`MQDmn>^4aWX zJ&gf>liOpGtCABP9qZBG$GUTzzWXJU{eUqk3hh7w_lXV?hpBQlTVt}}5(>ya=%soW zHs!g|=H}?(A4k*aC`pBzRQh~8Nz$>ow`1vaj84VUgcPvlsI*L4!=9pljlDC>sWz<@ zRZXm932?N`QY>25tdynY%XwTq=mdWyY@qJqBCts=2797fUc<$ndTG17WLcsst1@J(CyXD+98pEfp;NW@=n zeER-n_8ny3vipJz6Y2x0OPDe3!x3baH%gKU87)y824FKZ!Ht6F z--0;+Il^9uXu?!b)2*ab6q;aJ*cAOeeP9KGl?(c+Sq|lXQHdv=ByCmk%4JtbWe!Ux zRUQw45k5XA&nXrnhO5Vo+(My@P-3Lhs~Z{!iN}_uQUOgX6wXejeBQ>U(;6Gi{3fI6 zG`|sUEZ-v&-PO|CG8zsOqU#M%5SrFU6qh2q48x@vh9hkY0-QwgDg4GX`g6HZxO&y9mBZ;wT9GxayHwh^espweJl=e+xAnl5^5)F) zG;zDT3x&bfmew`tbba@E;MQ}Jq0#U0PUm_wgS^0)>XkRkRN*&De#2zNE8pWh-%aVc zls+JP6;N0czk?fg6l-dB-e>1$Y&R}5Q+FitK>nU8f_8k zP_>0ELc==z1^(XnhK22IK-*1RnoU6-Oo1*9DJ*5AUTH|$Cc!t7&1>nXR*vS&=>?F& zr4-lG9rauoCJVGAr6&yg&qw;W4qDypLZ^q7xRmcjhe2}nJ`!ffwnZ+rtezID*k!eJ zFM!CdgIxO-$$%~OtzHeq5vl$ChfY(xN`<^{pkOBp0|SM>S5?JSwtj%AP&AmTCJF<$ z9DVoY?(XEGbb3+pS3SR%p$wYKGfG^f-_8wkX*!`4K)upZ}b*z*fYd%#|VNa96DN`>ZF?svu{ z>`kW>l4}Xk;IpU=$gyW&JBwbbBi7XZBLiyV>SWCeQXBO#?H(=dBtr!Hb#)-H%#W$( zjJi5Z3>Pxe_jLLq?+>vt)Y0l(;F?BQBTQ?a7(mI^EUeaie}*)u1DS+}vKX+v^R4 zEUQyDbjE*Rkqx@8y7WfFX!M%xE|*_cbeBs*!<0q*{#Ya&X3QWI-Uhd!dsMB%<7sev zVx||wTL9;zF3NOO8}j>oS(UM6UNbB+F6&jL6%?uD6vd?~1?7J z{;JpIVgY|bcl9cYo3cElw_LC5P;yZz>~=3f$GXf0yxthBQ7~^kDMQBRWZ96FqT8MD znABsMj2h@r8D>l)#3a=3(jXe*va(!es93;M8OyrB;L$AxR7)uW6dv0nY5!-Ns_m3|MYs zsoSG*q`I&=hT_tj-0mjRo7N2vn7qrSV&0U+e9M9%nQ`L*E$D{f@f1uC<_rAGhS8)d zgixT*t?PXOe*o)03^x>I7&Hc+gze&X$4s-)r4OiFO!@dUsKFT6bgo}fiHB0Z0XbvD zHP>QU)@2zOB}}gK>XQHruye(gleJ1b&;V%urX8bCD${~DhE2K<`Kg)irHt{b1y$95|5 z#$Y)J?sP;A<}ASiCli|rNZezI*c^j(}yQ$z^Oj&5$6A(s1LlCuAm{F(2X z$FJWK-}{lrzM5v_S7T##Q~G1+^v7sEowhGqSwAwpdB63E$5`@wqTq(zA@OsD8pue=2gc=$`& zcsh4Jl*}m^oNx{)nf?-ggD2mW*_U`_B)&$)_vo9DTahs_2)_0KNF{w;5r1bBLK z82BY)uh|DDM4 z!m=$}mW^&9_J4;5k{nJAh6zT!Wt2aV?&gLM84pF{pr8G_#)lJ zE81&=iA0e6uQy z9TapAo35tla8z{*%>tSD!zl#2%1G$;8NWtq7&!kSIOZdSL5ix}(BY3y>-0>W81~>S zM0TA%$1Ct3<^L%8SG|t!9U^^48fInsR}zfd>iLGIlLhA=j#8_0tY92C03oyktXMY^ zhW&f{4`>=A(CptEy5dn)k3w`(g>>EiPjWqR=?PS~-vOYbw*8L%4sKj75tscomriJk zfPWBa+58N9l8s0uJV5QrGEfU9402+i72)n>n;yzC5)SNw%tPD0Sv0&wSTDxAPbd` z-bATo!j2=+?`Q!WloDyAI<|GkcrwctG(hyev}TQBTt3I{@E>1u;_6^{gs$?dCIPm@>b4&YwdJ=bwjC^6eav)rWuKGTHg( zD{+rUS8kMjhVGZIB0k*&pHQFc#9XlxMku}MqwxJH0i85_R}H#!H%*`|GtB&-hfeXE zJ+iXrbfJtUE1woY#jyGESv z5&CocwB_fVvz&YrseLOUw`X?}IcxufN#`s-S~T}EA-83Bh!XJbee-X!JD4B5J1ym< zL3lpaOJ_(srHg@Q&by&gm$=g!7MEZhCC)NRpv6AHqqV-Cu*F$Qsm6j!oCmW&PvE^} zvuo#EE-0Gx9iGlzTnI#?0VgRu|Nr*jhh~j;L0CJZf#>=ANZx$@d2%Qku*dl``FbE~ zC&}0CadHRpJ|*&YB5xnrX@8178V$@%@jQxj?kqYrH-$aQq2muLYkm2&#j=JoV%Em-8#?J-F)xM$F<8P z_Q_j@6G|K&RQ)gS*3T=v?UVQSV+`eSUTjZj9`x;!u9ceEN9fPtYtBlGr1wda(q+<3 z+p+_ueoD6$4LQsQBDRm@_@{SQ@Q*nS35~# ztZe^F&i|}~cqIxwG z)yZF2ix+Pj#P4+YS^b~k?R>@`wXcc#f6d!Kl1F>Ir8RB{|x&U8;1Nt zq{%7uUyBq&T*iRgia#K?YT_1qOE@?YXU;fv;b+dU+^MvA{lwL2{H~r@zj-12<9Uh7 zT-sEH61PqWxlYKU9j`$EKsK@A6W?>%uy42L_{$4^c(?(A0@CHe*M%U#Zs0t52J(WR zKVQK4G9N2;1XsUtJctoS!8rI#;4~Dslg1}l+^2(AA$yw$WN)Z=jO=wxdLU&0jvtuy zRN8_`bJZP_-m(y0J2tkH2QBOz8(X^oz6H~LZ3hxVzwTtBS~7?}a$;*v%WUL?_Nr~$ zzt{EGF9aSTU~f1PTZExUXZ?@jY4p_V&qEC0PvzcI=YG;agXp_r;B37r`)@rUv;kPA z2M!2b;no8}S2=Kiz3WWpzJ>vFi{iko&Xri>X~uzDMH9Ci;JonYT*}hcKYh&^ zS7pSy3tgv)&zI{ILhY;RI$emVSh&6m;RWlvaIKpU7+f{Nkh#`#%dJ=*8NLs$d_Gz{#zgq&TW>xxK1D-NyI%QQYwJgHuubNmf@7I467nP)pu* z=8L8sMNc8mFGRrGYq)UC*=s^!4PYh}itj zBpf{&YYJt$x~5PN-*rY?u$H?T@!RF>+Zud2CU!ptY;Zz!2wQ#t?uk>a-|=m5+CY2u zZA_ul$FALdnkDupk&-xjJp>>1Dn9>QUL2@Zc^LNxVzI!T9J?u6Rm5@H<5*QV{dU5E zpk3BD9%41Hi>VZ8i`Y-scZNb^on3u})k!xjxP&`4(75)J#s;#)6T(@shYW>sm#ob! zue(W*?9G|8i+x?4W1&!Y!u~t7kPL;%^32+0PG&5s!55>r0>Mkxb`E6ir`>MDBqQOm z&b5~$VmE;x62{G`wA1DA=6ZW362dym(j?}156+jF)FR})M0&z$BFsd=wbRFuD-INE z+mIhG*3572l2t+~zlmHf_@Nz;$*Cz?zN@u#!XmD%T;F|Xm15_nEOW-RL_#JlbDG1* zGcB5&vah_e`})d>q)=+T%h_Y(vf8`W2L7UHP4U};x%ngub`(w*I14?-X#m#;&s#8L zuDoCph;ZQIsv0SGA`-+Qm)NZ*m2Il#xUZ>nWNcrYnj+HCnOVewRrVLU-dT*tH!U6< z9UWY}i66RhxM8r)jLC}IlUk8Japqm^cX6`kn5wltGG!k+GIMldA5!GM0B7pBVq{^v z4(v7*x9N!~9`nRiS#f>uop?`1XsROJ06BgGTLS%L5PIR4rEfX=plbaI71tr{YJfmeEKo)Vo1L!`FLx;TMeHS-TkJC@ok>sT#hX}(&Wb;@NoKHH35{Rx#=i!_$ zaUB9HgC>L~#Lq@!GuN;*LuKmn zYtV->!wvaOUEJmisEXnD$D{T2quFc=-km&~kWD)iVbg>&+-P5$QB64VSiHBPp)bY^ z6_4pPZE*VI7(JZWxe0c#L&R1_QCGx+rq`l09E-L?PQwW!>vcw3GzO2WWqO0LilRJ3 z6rZ=Fu_@QqT{^F;*rFSOK<}CaKQ$sU#+F#q7Yw>xEyb?$x=ZcZrp6ARS0N+D=8ook zer+z7g0lyQ5r{u2t%}E27F8V&%nFfIF1I$HZ|>M^%vSa=wsQ4Fu4f@W zygFVJJZ=TJ04!B*r@|0{Wnvx2-~@xOMaZ5)b0s5}tX;QnAFihs4t^oLVDJkkLatvc z-rKj99Q+feq)C66e;el#Zt%1L_;!~;-?~V;3iHCyg?R-}fH<%#MY$)BZ-mKgZ8))$7m+fo^%Txf`>a#U7qKWJ8q_(04G5;mSv+h#dZ0pSL)iXWxPj8K{?Sml z(pBv5FLqVJq0#c~-g+;+7y(<43=P>|8yb3~J(nNEQHXyqpKD(TS1!1q!mpn3@g9DW zA72Rd<#z5Ry&b?s&6lqzcNHt4a5z*cc9mCL(cHq=p+k(Fx%tp9&-uvQ8h>Cgmv3*& z=NAR=Ta?eYwdZq#0Y91AUK!uMeY~>W{>e(wbkEA;bsU_*|Bvhx?%e=_0$@hMUU7tE zZ;@MVT&I1{TFy?RPT$3O`YxeiPRkw*JZjoFrAhK`YR(6BeJAPRd)WSmi@0f0r#}GI zPbT-Haj{2^>ZoOD`~LtJeEvl>4SwwCd<%3v?$-tN1db>W?;R;SxN^pAR<5);rp!)( zqncUsr|5uRo7Ql;NgSn;Ys5S3?{idvru z;Bcu^4YC(}q5ODg$cnbKtVm_Er?(VZTk<&s<;PoF+(v6t zvXEKdj<%W4%&ce}Os6e>psnr7v9X&LRUeXPmfFxZdRPjNfX8?MN(DdnIS;t{JE2or zh{wvqyT4dkDQ)6(Mc^L8+0Ozee{W7XN#4oo9UE>0Wn+Q!>)0pq&^(rGUP|P02_n^H zv*fwsGHm6zj7Pk|pw|}&-O2CXVCbIeGd;vBj^q-x))Kjq6LM_ybzCKTI_M=5Z)9Hb zgx(G2@<{={W^hK6#CgMN=>yU^h!w>|f=a477Tjop%(5qQ zt!QaS+3F)zdB6`5P<2XeDG*%J($dw{QW(PL9>FDrLZ{wY$S)4SJvuLDEau%gGc%Rs^G6b* zlJm0D&BRAC!TS1D9qm1y81;GviiiOZ5vS}(v*@C;fG&cB1cO89#GVOyXLy4%p3uzH zBwrbQhYMnENS@s|laVNQ%$o*$cV!>g+Ob3V328azIm1k;m7fVYlLEh4OsXz+ZrM0~)#{Ys(m0< ziP<+Bh!^l1$*#=zyusE;q$B7(m}yTM0dN-b66wMCz9Wa>Cb2)kXYz?aeSP2)-e5_+f1$MXpMl{4^H4oGKlF1V0Ft6F+h+o&?b8Ykoo@5on2d6won16RwBx4ay6f(<3J zne}aL9UX0L>oeJ8Q#{s;Gb&eeEZ+3)C0k~=9B}NR8brU5jKx#@-Bdi5Y|3WVIjusG zYF_x2;)oTR-ok|MvF@ z$bRtTC5FbP(&){^iWfZ(~REo@UR?YRmr5~oXE7l)|0ISy_#olQCDrLo(Cq^~@Qa#myN$5N32-wq)SKHm*Ij z?On2DZ##Dl>lywTUg^wXK0_eXPLhfQ7+^)58|UNF9K9u2~Q?uWdm3G zEgJS;*|b5NqpP5|Nj6QV@%2O*Aj=f+;s2AZgFbSSi;i%%#joiaaHs~5>DoJoP5|KN;=23e z;L8ot3{*UvCBR!L99ZHr#M<|W;==&`eGY?W4*GQTbC76nj=N~Ve~=b){A9nX*+P#G zsU?TVMf}BaUU=cBF9RpN-H4n-WEH9+kh6M96%`=?(W3YmNQk0dF(}wXsC?uPE*&07 z(aS?Ld%J~ur?&(ToY-^xs2+_vFyBaJ5!nhsA$Al0HmSC~#8$>@TJ>Wk5x6Nd0j_Ne zml0Y@mmJhAg%XcP?qLE(xE_u@SdB}^9`zAcf_v^o;TfJuJte65Mec80D|=q$$6q-2 zLMR-5JZ)?5*px4%kozFt0q1gC%_9$-R`qY{AHWtE=(`Q&jjQpk646YR_~Ho2ghGzM z4Bo_8t?p@P8iAA{4+4rKWrW4ULyZ@CBndQl`=27M-~c$TQqM}ArQn-Z4~s`yvlsTf z;g9F)#`2*SgcVT4@4Y|ue%0sFOqK|@)vd_;ntcBr;5BkuU3aM78AGG?l!6W30E+GZKyi9qy=9HT20ES3zS1HCDctq`RO%^A$}oNe=7YJeRbmPdI&qw1%#nDf1s2ns-SGnXnF zwi(2~6NUxoXdWO(C$9!)m4)->*gVJ-Y;8wR1(os-2_24vAmjz`!h9A@m(yA*pSBR- zcNNnQ|DOJOu1vpk9}l^?8Xit8-V#dSXN01?3X3Ut5a87g5g#erk7tzGn=~HGYZzCH zyRPr5-XE>zU)gLoHy6p4VrqVF6a5^0Xzp$5w!`Fw>k+x5u&r2nbg0Q@5H47i4J%5} z%VB(O3RsaBBec{hqvxOu{P1x^BO|6+)6YT2RiK)4oxuSN$NsBx&bj`2@*6*xJ8=I6 zHbu{o5Yb>Yt5N=$IIQY)>Y>K^DB0pG_{f%MA~`ok&l!%|5Bhv$Yh)?jBPmIuKZI_A z_p+e~-a%AV4hjlM4M)jVpU-|UvNU~M8~nXV=@6Z!NywPve2ZjqE50S+7nySITlu$e zyAWUY1pN^#(vI6M>(Gw~YXmWc1$Dub z?ihNeSfS)2xpeN|(KN;)4gDmOyU`;&dq$9?Ip z9OED^9f!I1R4Uwl`*|eBekUm52)uFCDn$1p>UY(ujSJv=>>mjXw&{o@vs>Z$LD*t2 zPF{R(`E2CG9&t>L-VZ-^LZ8+C=?KR!$8#hQj|X_iQ#JI5onauSwhkhP*K=&s@%bOI zCs-^0x>RT%&RNVK`J$MzGAp-|vpfyIKsjQPJ0g_^++_;@H{7N3E?ZUo#J zXBH&uCQ6iL6^0=*vE51)R9`wkr}eD!y`cE0tOS@K#J$=g5e z`j!37d+#NuU3kTBzLq?6sQIsdgLd75_NM8Kq~gmZuM|YwS3+u(n&FFYmGH4TL4aFN zUgL+T6sZjIXO9nzY%zrMn)#Z!=aH8c$7wFh-A-@ z5i(^@k*OI#{D;c?c4%^B(mpgZGh)y1IsehIW%2!cI$9>xz88*bAzT|Hlla)?rmI7+ zXHGFl3=e~w5o4L+V_PspJ~qM=N%GElUpdFO z6pQ!7|8pbBQ#^;gQEQ1miQg)xCFg;|xJGfkfa`;}uBu*7$P#e@UWDsIxYD?0aP`Sk z(!IE^l8Mxh`?3Y~rto|bu3=nDaD5!tMqK>;1Gv6}3-zTDT)gf+T=!J3YjNk};AO{O zN#spduN!bLR%N%pOuxr}RlpOtG$;kn4*rXEy7kS_=J3w&Or#KbAvzI#Id)atAOCV9 zlQ=)|tGb)&zFz;WhGOHJOeS! Q0&8&by5MY>*0`HA9|*E=Xb(QnQV~qxag|$QT97US*cQ5oV)Dpo!ieoPf;rG;CVl#@T$W9 z+KEC%8SBD*j*_uYU9)rdWmlqX6WVX0l)j;^?V`QsqTZ@&QRi+-+m&&?`|K;x@9GHd z(=wgT9@%sD?(Hb+#d|kVx_7nt-~0AndKu>X^~3c9Afa-0hoWJO_wpBT{|V>Hey#K{ z6ZZ>$EFkYNk@?@SqP#XHCg&!VNr8l7iPP%iw7kk*0M+6d!L`V#R;{8E{*=>|Ve}tE zSvp|Mh#A+6XU0Dhok`5p%w%S=Gc7YCGov%_n;D#IGljm^!@&7pk-?P@d1jE;7F)pqM; zyTdapXV#$I)6nk4X!mBcJ0;uwJGA?w6Smv^T1B?Y=6>;;xs;L8Qar_yhm!x5d?)#z z$+wfgNxqc)QS$rA?f~5*Fli+0#DT<@ z6Za(s6RAWbMz~psJ2~+GPZ?7fFyUX&K3A0TBH7)MK2KgIl##io#XpKulul)hvO!U_ z#zL`!Ie&e<{pBI%{PlFl;`Pk=i$-+A$DBXWNLM`?p+~WvZmQki*Gml)M6n!aajv$X z(b6>5(9jhO;TP;`Xc%j1IitPfjAkk=1yLLfmZ-G(jE?n{%C3g``i5PV%K8QH123Kg!8MmMali~^p$?&s9pI-=_r zl+#2ne^O~s6q6Sk4LuS!8M0V+*<{H2dy6JRR%jG{`z`w|SJdTlF@U&40WiNTvEKrK z68@wE88O3tQ`R;N7O>wm45xMg55PjW@2z6mC;F-W*oN`3`FF?0 zH%NH)$0w>^eH*kZlq=`n5w{9csZq+x=*dSf8YL<8^-99%>6T;?jYy&iXBt5`v7Ssf zD5j9*%7)6=1vHNf#wr^&j8&XDRmL_{hL&xu!Efubp-N@hkOL1bd!PN1<@Z~xK)En6 zAJjd*joI|!H@8tM+lH1^oEF(Wzh(J+mgT1{N%Q(IakFq?O$L43^$Vs~8C@{dt9_Q+cQsA)YM4;g$|5%XR|daiL9Y{WC3wNVycGix!UdXxzU{EznbQLHK9OJl_%!r#16rgAb560 z$ty~p^%pu>K{r5nwD*^>aGVuc8K9pPv7B+h2KxcKE276g6<->*cgLBgYd+)mm;8nO z)5lnD+ov?9<{rzb9?c#PeEQMl>}RpT@P{H=+-{sRys9Sn2w!@K{bzTuGvEIq)M~w> z6>rR5`1lR&SPu@_92Q=F2$DRF)rjcX#sYW}SW&nNA4OFB8@tx5-nDD>nqBKYec^@o zL?V^W&c6Q6&e2GeuUfN9KD51CySlb^G&K(r*BNYX>Y#OBH23%7o1!A=dGO@xelmI% z!E7+o$CYgxH(ry&@0yJpw=IM>t{S_D$orzPRT~$=i&&kg9ADje_0VI-zu>&gKF(_G z#};Ay9|GNe6l-T;25pc5KvOUu1dcLVhG5_<(;bTjV|tc>AcNIC7X*=JW(s}$t-iwS zbG$hFtL{pro5#CHS*3gSSJAqI$-SsbHLsY$!ISD(-&wL;~5;v$vR9K};G{zvd3jICh{+MjAg#iaBppGQH(u-Y< zCH{sdZaqDn=#R#uTK>VX$8v!B;o4CAyki!LT( zy(r*@{+Y$xt!pYD4$J3Yb|D5ZY>P`Epp%&3`fChF=E-vp3Mc84GOd8*_$ zU0>vUuf}veZ2iXYVjyA5^ztlo=`WgYxA~&(Vp;ArtuTh+HGX4-b&YF#IcFa*G~MDD z07k$~9XG80qv#!DxLv3WB2OsOa|gw+NGpco#okxK!A!7|XX2S?PcXyc*}iE$X--@A z#$h%*Z7XbYd|adtP4nH>v`Gd0uJLi&t4iP8@5CEI1@~yh4$Aa(uvP~8DiH?+!NtQ^ zB~YVo2#5O0;Q>~n3T!R=16$sC5PuybsybC$=-?&W=qS`aI?}=3>2$8I4|I%ljEr>5 z9&kr2w!CF@V6?@4wbRskSfXPDn&!Qj%nY9c1tyhtWtmdJD2lP(BGVwm^Fyr9$FStx zg(AjMV)>W^jyx-A^A(GE@I1}O;-!3dSINuT8DF95+}^`>#OqURhu0k}+IQM_v0}*b zv5(IKM?JixS~tuh_Px<;+`c!$qS&u&k9>0v|0y>Fw|76_@jRe+1k5md^wE$z&~Df- zex8Lb01vp`&Xdox?-6V*aX;y0o+rI!&wo7kna?~Y_uA^Y?}~pG8zmMd$;Q~YnScZP^+vqqP0$dxPsj+aG7#LGrF6E=$FB2o`J!Cwg*sJm+s*d&R; zp14soki=6jZC95rPjyBDe)YVUcAgpL4dF9){d?4Ij9iwxKRo68`!Ut;PAB#lJJ`PE_owZB?2b+4eGU6!A2t$Rs1K|rB4ocy12&mf71LaF5ea?(D@VKB${8Ni!k`l)TXAZRm()%i3(Vw)siGz_;;w&*&f4UF9@Og z)GaF4=)&2rg{t|~dz|~bIX{Q<2UMS?tCWLENvHX;cup*XF1JkCf))8=DrxCtmRz=U zGIcl0AG$v3_s6dHN2C7hV}5^BLJw~npVx!N9X%+Ul{yhjj*HR_4e5)<#>U4GXRjNbP7Pd+a>(F1)dt)0Hwzm;&H;^=&gglr8U0O;MzL#EQ zP&r$HKP6u@vT>svFP5_lAdRFH>FJJoE)A0fT9Vcih5hGeeWZg{H@np7VI?l3z34DV zQtx9?adcbkl5n`EIo#D5Zth+Hv0Vp9`Fn&PB#q< zH2tHlYhG>J2ZRnqL+E;{Y2cP4@4npKonDm9E=vEh$GP_OL$xQH#>e)p={wSL*FCL5 zw0(MZn%ZOsn+BdX+(M{}7I7#F!fiZ_{wC&Mg%rJT?t9`(BB?ydu4Z?$2SGu-(lg*l zn1Zv5WeJRCA9lBn9b4*>CD^(xWcttsmyaCvuL!|9zvY26=c$v|F^FY{t;u|0J>qWI z^9!E#fW1hOM2`+Xm6~I@-x-s%H=U=DTuYe&pT+!u9D4?~v+SihVngpgJfK!mC+EE& z^P@hh-6Kys*dT*`T^$H4^P}oHqpl7U1A!7mV_z_v>Zq$F&JZxP!_$SZjpt7yrNI>) zn+O%fmx4au3XSwlm*8AefpBhFO}bHIn#(*cSIPx$D0tlCEqOgr&fJ`vF_+8j_7uFn z8q?L_aie}xH=rYe+}u8Ix6c=hgu@-CWeWO%Mb??7?lS8wtKR2rbGZVlX1ZJk8s;Jv z2qa?Bs1O$8@HV(D)1w>h9#5UylkoaLyiMSo+{J~d8-sy>Kd%e1+~lNHWhuV-324h~v11p{z~+O-iwXQtp@A297nc|2{p?$Wd(p;T?e@@XU@ z!vX(N!699>G1COe7(#F%It7QV1?(HD=*7!!H?zzrEEd>NnzlrJ?Rd;PD`J20>{FaI zfNBi>H8l4%_%&BZU(Ttd8=ut2b~5oMU^z(cbXW~0mJkLflbarDvBM%@$I?bWx;9K+ zknPru%y=wKwO{-S#CaJi4^{P!+YOIzC zLywT{ErKe%D))N5nx<2B+zcZ~^p9Uc$4 zd>963AQ842EQncnFok#)DoX^m7tT5IeMT_bmx*SB*(ma8Pap1}0cZNiBTWCGs)}dx zuiN*$mScbYfghjAzC8OEEMxy0`-K0R$FJX--20KozMK{8m!qS0L-u3Y?8kU9o3$@p zRXaSjdjnvge*t5v(CNMq0o~3;2?4)#}P(FJel*~yLoNyL7oBAtygR*a{;;*POGJn11 za%uHC=!7W^1j}qvmdIy`x-ff?-n6%c>(~!rY;FTTW9&g_#5JM8a4HoZ4Ary?F~}hQ z2Zw7yZYp!544Vyn>R(-&{TphT1t~u<4FCD2x&IVXVmUM{>@eLZN;|PwmO2q3Qz}+b z4Jv_zXb-`-cKo6AT=3#Z><`5M--)egTE2Db@{z5~{_p5On&8ahD8s0?j?fM3Zmj!| z6?1zN?rZCFsJLazh-zvdjwI-LGIpcp)@1`*;1Bqaa<#aD->3wX1b9Fz=z}yET3cEY z>2nThCAidC23s^3CxfATcnw;C?aTlJM zuAR0&{3M2<>{o`xRs3}LDHQ2og>p&f4jQ_LO*gOT5URSRW`RQd;S@q#X{Aj2v|k~& zES&!^9P=?IAVqaDbm;bJlb>!f%U(PKkzHfXQU&^P`p4P7nKiU`nDQ-UNR;{CC@^lT z=NpE}7My=LO07v)!8mXL!gxDav1z3&`}g)A&@@J1*uS?-&7bpg1HeRW`yKloTu3f4m;E+LCp5*tKbW$7ZdyDkhLsX-pmya2s09-SISJ5;boYs> zuMc{Wvr5Ev1^8_dZ+cDptt-`lY5CQw?YB%H7pnd&oQ^Ide04Ji)&|HE-0G(9m;1fY6`~VK__c^{{QVJ0L>cj zg0ObRgU{3Z$liSZd3G=!w8!Y4eKi=j)9kDE7`p>ypO$4iQMQlmv_H)sjR$8Zsf;6^ zJ%dlqPGXM=EbC|Kw^XXiSvtpA_t$6lV*wMZ1Ib2jcO7{cXzs;d06^55Q{{ zArd&1ruxifR4B7B-8+S{lB`}WM0NJ(;U!DXUX0(V@UsR!OYM9%5Vx<12Yy9uASpvH7j*u7KJjH5D|Q5_-#8w`2;*QJG!r-t<>iF&31RZ- z;8n=p1_s$1={idGIwn1kG62U9%zCbD#iU7f$E3F|gb{VLlY$m@j*hNd0N;Y?zP1CI zWnOn8QLR|a96r7^r)4pGTzl2F?cbZ`>lXlz5U@8Kk1Zz9qqF`;@HBGr^`{U6_*2Py z>ReA4Xb^u_44i3JW&f@Fr8WS|bpL*-E8MzY>MHy9i+7#r>{l>gvMBc73avoHtZvy) zNVE3eDx0`vKk>pNb15s^{`55$q{_&3m%2^^&6jivsrJq5I-Q8ASh&6m;RWlvaIG8n zTcjFc$fWh$ax0cch3~_y?VrbpdE2{U;3R8({VhcN=B+>laB?e=6rt*t+p7xHZM4si z;D*i*PDb%3SY1irMEC%pmb~lC7fm^eo@w2Bi$$vC{hSPTBRYj>X!miv<_X`H%XyB6SPkrAuEbhn_S3Z;k;rIAXJ6Brv>O&&%AFXf zUw3hR9b4*&;H=og1|x-w*X35!+$2f%=G>WGeVrYnkw|yS{s**>jzrmt+`8pXVIpq8 z7o)j?p^Mjb4CL*n-EPavhNGh$>n=_uZURB1teZ1gr^}%&wf1x@f^}Av3C!^xoG%Nd zS;~8b^+dBwnu(HYXOAIQ94O9jLpolZH^0dxt3`CW$Wjvg&<@zd?oWr5DPuZX#m#;l`R-DDKD4=A{voH(8D*JQO?C44+H!oQ{GO~EdW;%2wxNdQcH=$~7PiAHI_?dUM-9=|wR1D8T}Yp-u8w04)y?d)nctzfWsZHi8fn2NEb*7k)$ zZdY?x=egabwtPcx!3AKclAQ`e1eQto7zQU9{CtG$Nimk1O>s2q`l$@$=)uq+DNB;&sxq-tH#_#JbjncFsD?H0UkAFozx_G zH#O&jrn!^#&>pt`=|VD1YRm_q`l;-GG%okZ5goOxZ2uqNg67|)XTgshoo|7zM}A#U zPw=n;@!pZLgOoF}SxISgOqrb$NAqUQpP~bPWy-+mCUcZZ(ujB1-z8MBo(l*^gdwJ^ zgr4-2h~NZ+e8PV6gUWNIdHLiB9FjWKAbZJ2sz*;!hE-)BoV9r5RHuU}CssRks%h7;f*%(rM|QNI2fy zyfTx|pW58i(p)ScC_mZK?6z7O(oMMyZD?Egy}6b3i?i8qAlTY^#pviwi>f#2(@U*r z8$B$8N5Eq}0HuP?ea;20{z2-LVZ>t<;oV=NtWq`;T`{=FaQ3sn$=_ShPEdBDdPj#F zLD^W~{5txMJv4_Un^RJSLW(Ih`8<2>m;zfnrr;4@DCF}8BX`o(7mD0dz2^t1;&34~ z-&(3Ld|Zi*zK*G6PlbF;p+@EuPvqTTl201&HH|ZxG|n5=C?8PHLaZRpLP)CtOM&J@ zpKrwC^J-8m4l1eYSa73BGRvRHwPIu)Wvh=>I?q`W5X>O+&G$#l3#v32}E_?WW$|#k%v1O;RxlnEG>h`uCPlEeA16_y#k1*%i;bzfA zM-#dTF%}99q7!>MNDQPnZ*9R-|JS zGQ6tVKk;eO@};dpvDXk{(n;;uEOq2;I80eSHevWQ69E%f)tr)GS6Kp=adcvX1lx~={F4i9o+ut#2EwsSeRn>k$ z<@RvMcXzNh8Mzy6G_o$5kbS@~KiD-m*!4jit+YEOIRuS^B$O4H=d|!DEp#U2ObYyF z39Gu;$+B_$s?|eJNhk&JWqW_3lCW>K5HAq0(w({Q_(CnQSbNBKAlH_*g5WIdCDw!S zeM^nPO=5qNX7b5kZEf(AzEFFC?MCo2;dvPT%4zs32b4323ofM4s+L~WHmb~Uo)Z$o zJN(sAQDjMHAhrBh%ga$G#9%s~+tAwD-rm}}A(u}#BomD|qjEJSk`3?vWa~7^0mmMi zhxpghiDZV}%_I})hJ0?l(<&6nhIfBrKXP;{UfHsvFF5lr<0Ko_WL00N^7X2Fv$`{@ zHh&cnRcB`vNkZB~w5J^6*?%eSvR0v$X7wuast znj8d*rHlLfZ(F~PM1^{hp4zX4sJB#r~yk@tf~PhU@n$w)YauPlIK`W9fchC8g|&QwASv<4(SzY zY%1iS2)T_O&8v3R#baqi7rD}jc)cfz=o|!vS@0(H4U~J#hzISU0X}TF;s1?hU9GJ> zU7n;5pJu_f4AyySV$piTg|ark>NX~Q`EVrdNu?6&8hjxH3QhR)VG(QcPb6G*52?ba zHFKwRbZyDeK!nwP>C%AqRnFb4o;s>-{v#p1@cwvLXr_DzLi zqbF{~&&Tl}eY>+X_{FVsaW5_oJZXD#~Z*xEZ5`I8>pVwChn5eF#u>r98PvI3K(s5a!Xq6%8BZIl2ydn-Eic3SUpu!1^o0{@X}&=U6dB zevuf?Go@wY6dFg}IV?3=k{qiq>PU+d-myupNClg6?zQF3f+DYk5B;Bf4fK%{BtA@R zi_+9QaBv=AQ}gc}JPv@Li*)zL!I$flX{dNOOMtghIkQ7C|a!{~|Q2Fp5Tsjm;(Mutkz1`Bi(_4ZEPVPB6sz;*^OdF{zBU>RT(sD7*@12?56K-#u+8KI?Y$wAFBDDk)@4-+s#dN}rAH7*@{)Xzi-?ztDG zXLvgEl%(bt$=|q6^}I^QUpV(dC>(y2x3;x!E;eOQ`XKFqbI4Znr~{`|{hQ_oumuMC zZo~8XHTYJE8D5?FlL*L!LXN-;YGQQ0?kQ**fs_#s0*Yg0gvG-{jTb4B1RA{kFR>PI z03288XJpRM@J*{nBx5c43wquNBnvg8#Yi*43OM5T-k*8D?spkpk&3p~tStH){QnW8 z8U>@KJ5uWlsj9);M#Ri*sEvgAnA82KrddgqLUq0%9^1de0O0x>2 zkn8=a%u|?@W&Hrm2XkGgsiK)epb?vaKqh2_A+!>VMl*mObWU5&A@l$*_@R{#n83<@ zs#u>zOd~^l5M%yyh-{Tma~N~xzzZT$PC8L)eKfXa-vl zC2<+}&aKMFobR<{PynKvNvaswW)S~Q7#5%-Wq=%=yc(QUmd=}_%OF#*wH-YbR7xKb zIvfc>$P3_wT&u^99DHU^H6O z&l-x`5BmLVTWlHLV;M!^--m93_wtb#-a%AV0SXGs48_?tzu$f^wk&&08}#0Ua*$8) zG-S*%zC|{%4d0SbVw28w8+{A63-M)7@E^c3UZIT7fA;{6?C`w9HlJj1#5kjX=nAhl zJCUWAb=-D&hkj&OBZw&~sSBQT$Iv@k%4Nk9cQ%z+II^Oxy{D(GeMLAD39o=fmKm(M zlcJ1ufsbiyJrF48^9ZEQmno1MaC3`@>tdTjxzTo-@$X z-aZn>Z=@alNI2-{)8H1&2-S{}&fS{A^6>$a#J%l+a!@Gl+&UV9L*jy<-} zS2c>Lr1KD?awGJ-KiL<3%$M%kQ4ZqLF_?Q#Po>-MYKp|T+6hWH3~yS!8qvLo`dz(x z(*pP&`v($(ttKML>=t-_5VjafvKQZ5xfnjaM;w!*_d|~z*JrhVK1}!}Jja5`WRNwe&M#;>=*v651)QU z`wkvx`tomg?R@L6^X#=wR>uar`+G{P6(qTpk7h5)yoqCtnKYECO_QC1tVSAe9{71)@$M^5q zc$roEUO29W@cbBsLSvhqst&=PKFJ_4JPdAHj%AX@wqS@fHb$AE_-1{toaJBow&GUh z{g3J|ejfqO6IypdnTr-UL#)p~VAy-_`+eMvj04;hxR z-_>I6cMrR-^$dCTdw%3ic_+M&``UbG`d;@Z{HF&zfh&Wa;3Xj?bY|!$;Zwt3k8FwV zj84azVlTwU<1Z(!Oa_u)OyyGNrG8m+Q_WXvzfsp!|7OEi8@}K0X4;j0G;>MjiOfu6 zZ{v*^7173O_7Lcoe)B6YZ>pJ3dAz#rOrh=VP!k6{QH2HI{wk~jzPt)+Xf;}eb(F8E z!WMSzeg*&ck7}aChbIoq0q>~70`OoJRsmmIh3S7yN>^bW<>OV@LX7>xyUyLad+**o z5b@_Kd*R64i+_7g-+sxZyY}u$mpZ!e|1NFEHxQSi0v@2961Ya!3Ew(l;rR9)mtS=D zCFpcJ26{Qdaq&sT32Tn--E&!b$M!wjFFE_N?dPS>xhlQt+`Vftwhnv=OnqO5-glsx iJ#s{6<9@qx9?H(a*EnhT>uCaOk*F>>8)kXZ-~R<1(5%q_ literal 0 HcmV?d00001 diff --git a/static/layui_ext/dtree/font/icons.json b/static/layui_ext/dtree/font/icons.json new file mode 100755 index 00000000..20889fd7 --- /dev/null +++ b/static/layui_ext/dtree/font/icons.json @@ -0,0 +1,283 @@ +{ + "data": [{ + "cls": "dtree-icon-xiangxia1", + "uncode": "e771" + }, + { + "cls": "dtree-icon-xiangyou", + "uncode": "e78f" + }, + { + "cls": "dtree-icon-jian", + "uncode": "e656" + }, + { + "cls": "dtree-icon-jia", + "uncode": "e657" + }, + { + "cls": "dtree-icon-weibiaoti5", + "uncode": "e618" + }, + { + "cls": "dtree-icon-wenjianjiazhankai", + "uncode": "e60e" + }, + { + "cls": "dtree-icon-dian", + "uncode": "e7a5" + }, + { + "cls": "dtree-icon-yonghu", + "uncode": "e603" + }, + { + "cls": "dtree-icon-fenzhijigou", + "uncode": "e658" + }, + { + "cls": "dtree-icon-fenguangbaobiao", + "uncode": "e655" + }, + { + "cls": "dtree-icon-xinxipilu", + "uncode": "e620" + }, + { + "cls": "dtree-icon-shuye1", + "uncode": "e61e" + }, + { + "cls": "dtree-icon-caidan_xunzhang", + "uncode": "e68e" + }, + { + "cls": "dtree-icon-normal-file", + "uncode": "e60c" + }, + { + "cls": "dtree-icon-roundclose", + "uncode": "e65c" + }, + { + "cls": "dtree-icon-bianji", + "uncode": "e602" + }, + { + "cls": "dtree-icon-roundadd", + "uncode": "e6d9" + }, + { + "cls": "dtree-icon-fuxuankuangxuanzhong", + "uncode": "e652" + }, + { + "cls": "dtree-icon-fuxuankuang", + "uncode": "e6f2" + }, + { + "cls": "dtree-icon-fuxuankuang-banxuan", + "uncode": "e607" + }, + { + "cls": "dtree-icon-search_list_light", + "uncode": "e807" + }, + { + "cls": "dtree-icon-move-up", + "uncode": "ea47" + }, + { + "cls": "dtree-icon-move-down", + "uncode": "ea48" + }, + { + "cls": "dtree-icon-delete1", + "uncode": "e640" + }, + { + "cls": "dtree-icon-refresh", + "uncode": "e6a4" + }, + { + "cls": "dtree-icon-jian1", + "uncode": "e600" + }, + { + "cls": "dtree-icon-jia1", + "uncode": "e601" + }, + { + "cls": "dtree-icon-shijian", + "uncode": "e606" + }, + { + "cls": "dtree-icon-check", + "uncode": "e645" + }, + { + "cls": "dtree-icon-close", + "uncode": "e646" + }, + { + "cls": "dtree-icon-favorfill", + "uncode": "e64b" + }, + { + "cls": "dtree-icon-favor", + "uncode": "e64c" + }, + { + "cls": "dtree-icon-roundcheckfill", + "uncode": "e659" + }, + { + "cls": "dtree-icon-roundcheck", + "uncode": "e65a" + }, + { + "cls": "dtree-icon-roundclosefill", + "uncode": "e65b" + }, + { + "cls": "dtree-icon-roundrightfill", + "uncode": "e65d" + }, + { + "cls": "dtree-icon-roundright", + "uncode": "e65e" + }, + { + "cls": "dtree-icon-samefill", + "uncode": "e671" + }, + { + "cls": "dtree-icon-same", + "uncode": "e672" + }, + { + "cls": "dtree-icon-pulldown", + "uncode": "e6a0" + }, + { + "cls": "dtree-icon-pullup", + "uncode": "e6a1" + }, + { + "cls": "dtree-icon-qrcode1", + "uncode": "e6b0" + }, + { + "cls": "dtree-icon-profile1", + "uncode": "e6b7" + }, + { + "cls": "dtree-icon-home1", + "uncode": "e6b8" + }, + { + "cls": "dtree-icon-homefill", + "uncode": "e6bb" + }, + { + "cls": "dtree-icon-roundaddfill", + "uncode": "e6d8" + }, + { + "cls": "dtree-icon-wefill", + "uncode": "e6f5" + }, + { + "cls": "dtree-icon-sort", + "uncode": "e701" + }, + { + "cls": "dtree-icon-round_list_light", + "uncode": "e82b" + }, + { + "cls": "dtree-icon-search2", + "uncode": "e615" + }, + { + "cls": "dtree-icon-set-sm", + "uncode": "e621" + }, + { + "cls": "dtree-icon-close1", + "uncode": "1006" + }, + { + "cls": "dtree-icon-close-fill", + "uncode": "1007" + }, + { + "cls": "dtree-icon-chart-screen", + "uncode": "e62a" + }, + { + "cls": "dtree-icon-star", + "uncode": "e608" + }, + { + "cls": "dtree-icon-ok-circle", + "uncode": "1005" + }, + { + "cls": "dtree-icon-add-circle", + "uncode": "e61f" + }, + { + "cls": "dtree-icon-about", + "uncode": "e623" + }, + { + "cls": "dtree-icon-circle1", + "uncode": "e687" + }, + { + "cls": "dtree-icon-radio", + "uncode": "e688" + }, + { + "cls": "dtree-icon-star-fill", + "uncode": "e832" + }, + { + "cls": "dtree-icon-rate", + "uncode": "e833" + }, + { + "cls": "dtree-icon-shujudaping", + "uncode": "e742" + }, + { + "cls": "dtree-icon-share3", + "uncode": "e641" + }, + { + "cls": "dtree-icon-youjian", + "uncode": "e642" + }, + { + "cls": "dtree-icon-repair", + "uncode": "e738" + }, + { + "cls": "dtree-icon-evaluate", + "uncode": "e674" + }, + { + "cls": "dtree-icon-like", + "uncode": "e66c" + }, + { + "cls": "dtree-icon-layim-theme", + "uncode": "e61b" + }, + { + "cls": "dtree-icon-xiangmuxiaoxi", + "uncode": "e60f" + } + ] +} \ No newline at end of file diff --git "a/static/layui_ext/dtree/\345\274\200\345\217\221\350\200\205\346\227\245\345\277\227.txt" "b/static/layui_ext/dtree/\345\274\200\345\217\221\350\200\205\346\227\245\345\277\227.txt" new file mode 100755 index 00000000..21775d27 --- /dev/null +++ "b/static/layui_ext/dtree/\345\274\200\345\217\221\350\200\205\346\227\245\345\277\227.txt" @@ -0,0 +1,141 @@ +------------------------v2.4.5_finally(2018/12/21)【修复】:------------------------ +1.【修复】单页应用中,未开始toolbar导致body上的所有事件失效。 +2.【优化】更新dist文件夹中的dtree压缩 +3.【优化】toolbar模块优化:新增节点时,当开启了record参数时不会将参数新增到节点上的问题。 + + +------------------------v2.4.5_finally(2018/12/21)【优化】:------------------------ +1.【优化】menubar模块优化:点击收缩时,当前页的所有树都收缩的问题。 +2.【优化】menubar模块优化:点击删除复选框时,展开和收缩节点会报错,现在改为删除的节点如果有子集的话,子集会一并删除,但是回调函数中只会提供你勾中的数据。 +3.【优化】基础方法优化:dataInit方法回显数据时,当前选中的数据无法被获取到的问题。 +4.【优化】基础方法优化:parseData方法的basicData()方法,basicData如果未指定,前端的该项属性无数据,而不是"" +5.【优化】异步属性优化:使用data加载时,可以允许指定的data为[]。 +6.【优化】异步属性优化:使用data加载时,现在也有success和done的回调 +7.【新增】checkbar模块新增:checkbarType新增属性:"only",用于模拟单选,只能同时选择一个复选框。 +8.【新增】异步回调方法新增:新增success回调,用于数据加载完毕后的回调,在done之前执行 +9.【新增】异步属性新增:filterRequest属性,用于过滤树自动发起的请求中你不需要的参数。 +10.【新增】基础属性新增:record属性,开启数据记录模式,用于记录用户提供的JSON数据串中,指定当前节点的JSON数据(排除basicData字段和children字段) +11.【新增】基础属性新增:load属性,是否开启加载遮罩。 +12.【新增】内置函数新增:getFilterRequestParam,获取filterParam过滤后的requestParam + +------------------------v2.4.5_finally_beta(2018/12/07)【大改】:------------------------ +0.【新增】提供的内容中新增dist/dtree.js压缩js文件。 +1.【大改】css:css样式均加上dtree前缀。 +2.【大改】图标:iconfont更改为dtreefont。 +3.【大改】事件监听:图标点击事件返回的参数改为一个JSON对象,具体查看基础文档的事件监听。 +4.【大改】事件监听:节点点击事件返回的参数改为一个JSON对象,具体查看基础文档的事件监听。 +5.【大改】事件监听:节点双击事件返回的参数改为一个JSON对象,具体查看基础文档的事件监听。 +6.【大改】事件监听:复选框点击事件返回的参数改为一个JSON对象,具体查看基础文档的事件监听。 +7.【大改】事件监听:iframe加载完毕事件返回的参数改为一个JSON对象,具体查看基础文档的事件监听。 +8.【新增】基础方法新增:getChildParam,获取全部下级节点 +9.【新增】menubar模块新增:menubarTips属性,用于指定menubar的依附方式。 +10.【新增】toolbar模块新增:toolbarFun新增loadToolbarBefore函数,用于呈现右键菜单之前调用的函数。 +11.【新增】内置函数新增:initTreePlus 用于初始化菜单栏和工具栏的div。 +12.【新增】内置函数新增:openTreePlus 用于开启工具栏和菜单栏。 +13.【新增】内置函数新增:getMenubarDom 用于获取菜单栏。 +14.【新增】内置函数新增:getExtMenubarDom 用于获取扩展菜单栏。 +15.【新增】内置函数新增:getMenubarToolDom 用于获取依附在工具栏的菜单栏。 +16.【新增】内置函数新增:getExtMenubarToolDom 用于获取依附在工具栏的扩展菜单栏。 +17.【新增】内置函数新增:menubarMethod 用于menubar内置调用方法。 +17.【新增】内置函数新增:menubarListener 用于menubar内置监听。 +18.【新增】内置函数新增:setToolbarDom 用于设置工具栏按钮。 +19.【新增】内置函数新增:unbindBrowserEvent 用于解绑浏览器事件。 +19.【新增】内置属性新增:response属性中新增spread。 +20.【优化】优化了图标显示。 +21.【优化】toolbar模块:单页应用中,右键菜单的显示位置问题。 +22.【优化】menubar模块:在开启了toolbar模块时按钮点击失效的问题。 +23.【优化】iframe模块:iframeuUrl指定了"?"时,实际发送的url会出现两个"?"的问题。 +23.【优化】数据格式:默认数据格式中非必须指定isLast属性,移除level属性的作用。 +24.【修复】数据格式:在开启了list风格参数时,修复必须指定isLast返回的问题和设置initLevel无效的问题。 +25.【修复】toolbar模块:新增节点返回json格式后显示数据未定义的问题。 +26.【移除】内置函数移除:refreshTree,现放置在menubarMethod方法中。 +27.【移除】内置函数移除:openAllNode,现放置在menubarMethod方法中。 +28.【移除】内置函数移除:closeAllNode,现放置在menubarMethod方法中。 +28.【移除】内置函数移除:loadChildTree。 +29.【移除】内置函数移除:openMenubar。 +30.【移除】内置函数移除:openToolbar。 +31.【移除】内置属性移除:level。 +32.【移除】内置属性移除:response属性中移除level。 + + +------------------------v2.4.5(2018/11/30)【修复】:------------------------ +1.【修复】基础方法修复:dataInit方法的返回数据异常。 +2.【修复】基础方法修复:render函数自动识别是否重载树,代码再次修复(针对单页应用)。 +3.【新增】内置函数新增:refreshTree,用于刷新树。 +4.【新增】内置函数新增:parseData 用于解析数据。 +5.【优化】checkbar模块:checkArr属性支持传递字符串。 +6.【修复】toolbar模块:拼接新增节点内容的代码异常 。 + +------------------------v2.4.5(2018/11/25)【修复】:------------------------ +1.【修复】render函数自动识别是否重载树,代码修复。 +2.【修复】内部代码bug。 +3.【优化】修改了右键菜单弹出的动画效果。 + +------------------------v2.4.5(2018/11/25)【新增】:------------------------ +1.【优化】优化了图标显示。 +2.【新增】内置图标新增。 +3.【新增】ficon属性:用于用户自定义一级图标 +4.【新增】firstIconArray属性:用于用户自定义扩展一级图标 +5.【新增】异步加载模块添加headers属性。 +6.【新增】toolbar模块:新增属性toolbarExt,用于自定义扩展工具栏右键菜单按钮 +7.【新增】checkbar模块:新增checkbarFun中的回调方法:chooseBefore,用于复选框点击前回调 +8.【新增】内置函数新增:changeCheck,用于改变复选框状态。 +9.【优化】render函数自动识别是否重载树。 +10.【优化】内部代码优化。 +11.【更新】更新了帮助文档 + +------------------------v2.4.5(2018/11/23)【修复】:------------------------ +1.【修复】toolbar模块点击删除按钮时,控制台报错。 +2.【修复】配置了dot:false时,toolbar模块新增节点后一级图标不显示的问题。 +3.【修复】内置函数优化:initNoAllCheck,修复了显示bug +4.【新增】内置函数新增:initAllCheck,复选框选中状态初始化设置 +5.【新增】内置函数新增:checkStatus,设置复选框选中/未选中/半选 +6.【优化】toolbar模块:点击删除时,当删除了某个节点下的最后一个子节点,那该节点也会改变样式变成叶子节点。 +7.【优化】toolbar模块:addTreeNode方法优化,ajax请求不限于同步,方法无需返回 +8.【新增】内置函数新增:changeTreeNodeAdd,新增节点后改变节点内容 +9.【优化】toolbar模块:editTreeNode方法优化,ajax请求不限于同步,方法无需返回 +10.【新增】内置函数新增:changeTreeNodeEdit,编辑节点后改变节点内容 +11.【优化】toolbar模块:delTreeNode方法优化,ajax请求不限于同步,方法无需返回 +12.【新增】内置函数新增:changeTreeNodeDel,删除节点后改变节点内容 +13.【优化】toolbar模块:editTreeLoad方法优化,ajax请求不限于同步,方法无需返回 +14.【新增】内置函数新增:changeTreeNodeDone,编辑页打开后显示编辑页内容 +15.【更新】更新了帮助文档 + +------------------------v2.4.5(2018/11/22)【修复】:------------------------ +1.【修复】树重载时,使用data属性会造成数据重复加载问题。 +2.【修复】输出参数的字段,spared修改为spread(之前单词拼错了。。。。)。 +3.【新增】toolbar模块中新增属性:toolbarStyle,用于自定义toolbar的显示文字,弹框大小。 +4.【移除】基础属性中移除了addIndex属性。 +5.【新增】skin属性:用于用户自定义主题。 +6.【更新】更新了帮助文档 + +------------------------v2.4.5(2018/11/21)【修复】:------------------------ +1.【修复】使用dataFormat属性时,直接使用data属性配置节点内容失效的问题。 +2.【修复】parentId为null导致节点数据加载失效的问题。 +3.【优化】反选节点时将选中哪一级的节点展开,单选和多选均生效。 +4.【新增】dot属性,用于用户自定义一级图标中的小圆点是否显示。 +5.【更新】更新了帮助文档 + +------------------------v2.4.5(2018/11/21)【新增】:------------------------ +1.【新增】toolbar取消按钮的显示,开始绑定右键点击事件。 +2.【新增】toolbar模块新增一个属性:toolbarScroll 用于绑定树的上级div容器,让树可以显示滚动条的div容器,右键菜单绑定必填项。 +3.【新增】toolbar模块新增编辑页数据回显功能。 +4.【新增】树加载完毕后的回调函数 +5.【新增】dataStyle属性,用于用户配置layui通用的json数据风格 +6.【新增】dataFormat属性,用于用户自定义data中的数据格式(即支持传入一个大的list) +7.【更新】更新了帮助文档 + +------------------------v2.4.5(2018/11/09)【优化】:------------------------ +1.【优化】更新了图标库,精简了图标(60个) + +------------------------v2.4.5(2018/11/09)【初始】:------------------------ +1.【初始】基本树形展示,无限级,支持自定义修改树的展示图标 +2.【初始】支持异步/同步数据加载,支持静态数据加载,支持数据缓存 +3.【初始】支持自定义返回json格式,支持自定义异步/同步加载参数 +4.【初始】支持复选框,1-N级,支持复选框的四种选中形式,支持记录复选框选中/更改数据的回调 +5.【初始】支持工具栏,即可直接修改当前树节点,新增/ 编辑/删除等 +6.【初始】支持菜单栏,即可直接对树进行全部节点展开/收缩、删除全部选中节点、刷新树、搜索树等 +7.【初始】支持加载iframe,即点击树节点时,可以带上一个访问iframe的url,这个设计在左树右内容风格的页面极为有用 +8.【初始】支持数据回调,单击节点回调、双击节点回调、复选框选中回调、iframe加载完毕回调等 +9.【初始】支持数据获取,即获取当前选中节点数据,当前选中复选框节点数据等 +10.【初始】支持数据反显,即加载树之后将需要反显的节点高亮显示或选中 \ No newline at end of file From 3f1c88c94e58567d7952b6306b78df08cb0fcd2a Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 21 May 2019 10:18:04 +0800 Subject: [PATCH 041/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=BC=B9=E5=87=BA?= =?UTF-8?q?=E5=B1=82=E7=9A=84=E4=B8=80=E4=B8=AA=E6=97=B6=E9=97=B4=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E9=94=99=E8=AF=AF=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/html/login/action.html | 21 +++++++++++++------- lualib/admin/html/system/header/header.html | 2 +- lualib/admin/html/system/menu/menu.html | 2 +- lualib/admin/html/system/role/role-add.html | 6 +++--- lualib/admin/html/system/role/role-edit.html | 6 +++--- lualib/admin/html/system/role/role.html | 4 ++-- lualib/admin/html/system/user/user.html | 2 +- 7 files changed, 25 insertions(+), 18 deletions(-) diff --git a/lualib/admin/html/login/action.html b/lualib/admin/html/login/action.html index 5aa0f290..04b5d5b2 100644 --- a/lualib/admin/html/login/action.html +++ b/lualib/admin/html/login/action.html @@ -5,19 +5,26 @@ //监听提交 form.on('submit(login)', function(data){ $('#sub').hide(); - $.post('{*login_api*}', data.field, - function (data, status) { - if (data.code != 0 && data.code != 200) { - return layer.msg('请求失败:' + data.msg, {time:2000}, function () { + $.ajax({ + url: "{*login_api*}", + type: "POST", + data: data.field, + success: function (res) { + if (res.code != 0) { + return layer.msg(res.msg, {time:2000}, function () { $('#sub').show(); }); } - layer.msg('登录成功', {time:1000}, function () { + layer.msg(res.msg, {time:1000}, function () { window.location.href = data.url + '?' + 'token=' + data.token }); + }, + error: function (res) { + return layer.msg("请求失败", {time:2000}, function () { + $('#sub').show(); + }); } - ); - form.render(); + }) return false; }); }); diff --git a/lualib/admin/html/system/header/header.html b/lualib/admin/html/system/header/header.html index e748231c..95c3f0d9 100644 --- a/lualib/admin/html/system/header/header.html +++ b/lualib/admin/html/system/header/header.html @@ -62,7 +62,7 @@ }, function (data, status) { if (data.code != 0) { - return layer.msg(data.msg, {timeout:2000}); + return layer.msg(data.msg, {time:2000}); } layer.msg(data.msg, {time:1000}, function () { return window.location.reload(); diff --git a/lualib/admin/html/system/menu/menu.html b/lualib/admin/html/system/menu/menu.html index 228751af..583ee80d 100644 --- a/lualib/admin/html/system/menu/menu.html +++ b/lualib/admin/html/system/menu/menu.html @@ -111,7 +111,7 @@ $.post('{*api_url*}', field, function (data, status) { if (data.code != 0) { - return layer.msg(data.msg, {timeout:2000}); + return layer.msg(data.msg, {time:2000}); } layer.msg(data.msg, {time:1000}, function () { return window.location.reload(); diff --git a/lualib/admin/html/system/role/role-add.html b/lualib/admin/html/system/role/role-add.html index 35367da0..27a57364 100644 --- a/lualib/admin/html/system/role/role-add.html +++ b/lualib/admin/html/system/role/role-add.html @@ -98,17 +98,17 @@ }), success: function (res) { if (res.code != 0) { - return layer.msg(res.msg, {timeout: 2000}, function () { + return layer.msg(res.msg, {time: 2000}, function () { $('#submit').show(); }); } - return layer.msg(res.msg, {timeout: 1000}, function () { + return layer.msg(res.msg, {time: 1000}, function () { xadmin.close(); xadmin.father_reload(); }); }, error: function (res) { - layer.msg(res.msg, {timeout: 2000}, function () { + layer.msg(res.msg, {time: 2000}, function () { $('#submit').show(); }); } diff --git a/lualib/admin/html/system/role/role-edit.html b/lualib/admin/html/system/role/role-edit.html index bd6cef91..90de8aae 100644 --- a/lualib/admin/html/system/role/role-edit.html +++ b/lualib/admin/html/system/role/role-edit.html @@ -105,17 +105,17 @@ }), success: function (res) { if (res.code != 0) { - return layer.msg(res.msg, {timeout: 2000}, function () { + return layer.msg(res.msg, {time: 2000}, function () { $('#submit').show(); }); } - return layer.msg(res.msg, {timeout: 1000}, function () { + return layer.msg(res.msg, {time: 1000}, function () { xadmin.close(); xadmin.father_reload(); }); }, error: function (res) { - layer.msg(res.msg, {timeout: 2000}, function () { + return layer.msg(res.msg, {time: 2000}, function () { $('#submit').show(); }); } diff --git a/lualib/admin/html/system/role/role.html b/lualib/admin/html/system/role/role.html index 45c9d066..8b31d651 100644 --- a/lualib/admin/html/system/role/role.html +++ b/lualib/admin/html/system/role/role.html @@ -64,9 +64,9 @@ }, function (data, status) { if (data.code != 0) { - return layer.msg(data.msg, {timeout:2000}); + return layer.msg(data.msg, {time:2000}); } - layer.msg(data.msg, {timeout:1000}, function () { + layer.msg(data.msg, {time:1000}, function () { return window.location.reload(); }) } diff --git a/lualib/admin/html/system/user/user.html b/lualib/admin/html/system/user/user.html index a59d631d..c15ee403 100644 --- a/lualib/admin/html/system/user/user.html +++ b/lualib/admin/html/system/user/user.html @@ -65,7 +65,7 @@ }, function (data, status) { if (data.code != 0) { - return layer.msg(data.msg, {timeout:2000}); + return layer.msg(data.msg, {time:2000}); } layer.msg(data.msg, {time:1000}, function () { return window.location.reload(); From ac68ae3a26d7a5036351a154c12fac98d8342c98 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 21 May 2019 12:20:20 +0800 Subject: [PATCH 042/956] =?UTF-8?q?=E4=BC=98=E5=8C=96dashboard=E6=B5=81?= =?UTF-8?q?=E7=A8=8B,=20=E7=BC=A9=E5=87=8FSQL=E8=AF=B7=E6=B1=82=E6=AC=A1?= =?UTF-8?q?=E6=95=B0,=20=E6=9B=B4=E6=96=B0sql=E4=B8=8E=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E6=B5=81=E7=A8=8B,=20README=E5=A2=9E=E5=8A=A0benchmark?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/README.md | 29 ++++++++++++++++ lualib/admin/config.lua | 3 +- lualib/admin/db/database.sql | 54 ++++++----------------------- lualib/admin/db/header.lua | 4 +-- lualib/admin/db/token.lua | 25 +++++++++++++ lualib/admin/db/user.lua | 6 ++-- lualib/admin/db/view.lua | 2 +- lualib/admin/html/login/action.html | 2 +- lualib/admin/http/dashboard.lua | 48 ++++++++----------------- lualib/admin/http/login.lua | 6 +--- lualib/admin/token.lua | 8 ++--- lualib/admin/utils/init.lua | 3 -- 12 files changed, 92 insertions(+), 98 deletions(-) diff --git a/lualib/admin/README.md b/lualib/admin/README.md index f1a776f1..a37284f4 100644 --- a/lualib/admin/README.md +++ b/lualib/admin/README.md @@ -7,3 +7,32 @@ #### 2. dashboard为后台首页 #### 3. view用户自定义页 + + +##benchmark + +> 这里有一份对cf的admin库压测数据对比(ms取整), 压测内容包含数据库请求、渲染模板、权限判断, 接口处理. + 由于cf的admin库是单页面iframe实现, 所以也是admin库中频繁被适用到的地方. 最具测试代表性. + +测试平台: + Mac OSX + +测试硬件: + 处理器: Intel(R) Core(TM) i7-5557U CPU @ 3.10GHz + 内存: 16 GB 1867 MHz DDR3 + +测试工具: + ab + +测试方法: + ab -c 客户端数量 -n 请求次数 -C Cookie: CFTOKEN=5D464133460A1A1E5251415940231B505A5D1A70545D513A795E1A265A47501A5347 + +测试情况: + +50个客户端/1000个请求: 平均每秒能完成200个左右的请求, 平均请求延迟为170ms. 延迟浮动值为:170~400ms; + +100个客户端/1000个请求: 平均每秒能完成190个左右的请求, 平均请求延迟为550ms. 延迟浮动值为:300~5300ms; + +200个客户端/1000个请求: 平均每秒能完成190个左右的请求, 平均请求延迟为1050ms. 延迟浮动值为:300~5500ms; + +``` diff --git a/lualib/admin/config.lua b/lualib/admin/config.lua index 2693313c..dd7f3f52 100644 --- a/lualib/admin/config.lua +++ b/lualib/admin/config.lua @@ -1,10 +1,9 @@ -local locales = require "admin.locales" local config = { cdn = 'http://localhost:8080/', -- 静态文件前缀地址 -- github = 'https://github.com/candymi/core_framework', -- 跳转地址 cache = false, -- 是否缓存模板 locale = "ZH-CN", -- 当前语言 - locales = locales, -- 语言表 + locales = require "admin.locales", -- 语言表 secure = 'cfadmin', -- 生成token的secure cookie_timeout = 86400 -- Cookie超时时间 } diff --git a/lualib/admin/db/database.sql b/lualib/admin/db/database.sql index 0b1c55d2..5b74d1db 100644 --- a/lualib/admin/db/database.sql +++ b/lualib/admin/db/database.sql @@ -7,23 +7,13 @@ # # Host: 127.0.0.1 (MySQL 5.7.25) # Database: cfadmin -# Generation Time: 2019-05-16 15:18:11 +0000 +# Generation Time: 2019-05-21 02:32:16 +0000 # ************************************************************ - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - - # Dump of table cfadmin_headers # ------------------------------------------------------------ -CREATE TABLE `cfadmin_headers` ( +CREATE TABLE IF NOT EXISTS `cfadmin_headers` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', `name` varchar(255) NOT NULL COMMENT '头部名称', `url` varchar(255) NOT NULL COMMENT '头部URL', @@ -35,24 +25,10 @@ CREATE TABLE `cfadmin_headers` ( -# Dump of table cfadmin_logs -# ------------------------------------------------------------ - -CREATE TABLE `cfadmin_logs` ( - `id` bigint(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', - `uid` int(11) unsigned NOT NULL COMMENT '用户ID', - `content` text NOT NULL COMMENT '日志信息', - `create_at` int(11) unsigned NOT NULL COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `uid_index` (`uid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - - # Dump of table cfadmin_menus # ------------------------------------------------------------ -CREATE TABLE `cfadmin_menus` ( +CREATE TABLE IF NOT EXISTS `cfadmin_menus` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', `parent` int(11) unsigned NOT NULL, `name` varchar(255) NOT NULL COMMENT '菜单名称', @@ -61,7 +37,8 @@ CREATE TABLE `cfadmin_menus` ( `create_at` int(11) unsigned NOT NULL COMMENT '创建时间', `update_at` int(11) unsigned NOT NULL COMMENT '更新时间', `active` tinyint(4) unsigned NOT NULL COMMENT '删除标志', - PRIMARY KEY (`id`) + PRIMARY KEY (`id`), + KEY `com_index` (`active`,`url`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -69,7 +46,7 @@ CREATE TABLE `cfadmin_menus` ( # Dump of table cfadmin_permissions # ------------------------------------------------------------ -CREATE TABLE `cfadmin_permissions` ( +CREATE TABLE IF NOT EXISTS `cfadmin_permissions` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', `role_id` int(11) unsigned NOT NULL COMMENT '所属角色', `menu_id` int(11) unsigned NOT NULL COMMENT '所属菜单', @@ -77,8 +54,7 @@ CREATE TABLE `cfadmin_permissions` ( `update_at` int(11) unsigned NOT NULL COMMENT '修改时间', `active` tinyint(4) unsigned NOT NULL COMMENT '是否启用', PRIMARY KEY (`id`), - KEY `role_index` (`role_id`), - KEY `menu_index` (`menu_id`) + KEY `com_index` (`active`,`role_id`,`menu_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -86,7 +62,7 @@ CREATE TABLE `cfadmin_permissions` ( # Dump of table cfadmin_roles # ------------------------------------------------------------ -CREATE TABLE `cfadmin_roles` ( +CREATE TABLE IF NOT EXISTS `cfadmin_roles` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', `name` varchar(255) NOT NULL COMMENT '角色名称', `is_admin` tinyint(4) unsigned NOT NULL COMMENT '管理员标志', @@ -101,7 +77,7 @@ CREATE TABLE `cfadmin_roles` ( # Dump of table cfadmin_tokens # ------------------------------------------------------------ -CREATE TABLE `cfadmin_tokens` ( +CREATE TABLE IF NOT EXISTS `cfadmin_tokens` ( `uid` int(11) unsigned NOT NULL COMMENT '用户ID', `name` varchar(255) NOT NULL COMMENT '用户名称', `token` varchar(255) NOT NULL COMMENT '用户TOKEN', @@ -114,7 +90,7 @@ CREATE TABLE `cfadmin_tokens` ( # Dump of table cfadmin_users # ------------------------------------------------------------ -CREATE TABLE `cfadmin_users` ( +CREATE TABLE IF NOT EXISTS `cfadmin_users` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', `name` varchar(255) NOT NULL COMMENT '用户名', `username` varchar(255) NOT NULL COMMENT '用户账户', @@ -127,13 +103,3 @@ CREATE TABLE `cfadmin_users` ( `active` tinyint(4) unsigned NOT NULL COMMENT '删除标志', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - - - -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/lualib/admin/db/header.lua b/lualib/admin/db/header.lua index 131a25d4..0536044f 100644 --- a/lualib/admin/db/header.lua +++ b/lualib/admin/db/header.lua @@ -14,7 +14,7 @@ end -- 获取指定header function header.get_header (db, id) local ret, err = db:query(fmt([[ SELECT id, name, url FROM cfadmin_headers WHERE id = '%s' LIMIT 1]], id)) - if #ret == 0 then + if not ret or #ret == 0 then return end return ret[1] @@ -22,7 +22,7 @@ end -- header 总数 function header.header_count (db) - return db:query([[SELECT count(id) as count FROM cfadmin_headers WHERE active = '1']])[1]['count'] + return db:query([[SELECT count(id) AS count FROM cfadmin_headers WHERE active = '1']])[1]['count'] end -- 是否存在此header diff --git a/lualib/admin/db/token.lua b/lualib/admin/db/token.lua index 51b3264f..4d103a89 100644 --- a/lualib/admin/db/token.lua +++ b/lualib/admin/db/token.lua @@ -32,4 +32,29 @@ function token.token_exists (db, token) return ret[1] end +-- 根据token查用户信息 +function token.token_to_userinfo (db, token) + return db:query(fmt([[ + SELECT + `cfadmin_users`.id, + `cfadmin_users`.name, + `cfadmin_users`.username, + `cfadmin_users`.password, + `cfadmin_tokens`.token, + `cfadmin_users`.role, + `cfadmin_users`.name AS role_name, + `cfadmin_roles`.is_admin, + `cfadmin_users`.email, + `cfadmin_users`.phone, + `cfadmin_users`.create_at, + `cfadmin_users`.update_at + FROM + cfadmin_users, cfadmin_tokens, cfadmin_roles + WHERE + `cfadmin_tokens`.uid = `cfadmin_users`.id AND + `cfadmin_roles`.id = `cfadmin_users`.role AND + `cfadmin_tokens`.token = '%s' + LIMIT 1]], token))[1] +end + return token diff --git a/lualib/admin/db/user.lua b/lualib/admin/db/user.lua index 9e4c92fa..9b307074 100644 --- a/lualib/admin/db/user.lua +++ b/lualib/admin/db/user.lua @@ -18,7 +18,7 @@ function user.user_list (db, opt) `cfadmin_users`.id, `cfadmin_users`.name, `cfadmin_users`.username, - `cfadmin_roles`.name as role_name, + `cfadmin_roles`.name AS role_name, `cfadmin_users`.email, `cfadmin_users`.phone, `cfadmin_users`.create_at, @@ -33,7 +33,7 @@ end -- 用户总数 function user.user_count (db) - return db:query([[SELECT count(id) as count FROM cfadmin_users WHERE active = '1']])[1]['count'] + return db:query([[SELECT count(id) AS count FROM cfadmin_users WHERE active = '1']])[1]['count'] end -- 用户是否存在 @@ -59,7 +59,7 @@ function user.user_info (db, uid) `cfadmin_users`.name, `cfadmin_users`.username, `cfadmin_users`.password, - `cfadmin_roles`.name as role_name, + `cfadmin_roles`.name AS role_name, `cfadmin_roles`.is_admin, `cfadmin_users`.role, `cfadmin_users`.phone, diff --git a/lualib/admin/db/view.lua b/lualib/admin/db/view.lua index 5568f219..9e81cabb 100644 --- a/lualib/admin/db/view.lua +++ b/lualib/admin/db/view.lua @@ -3,7 +3,7 @@ local view = {} -- 获取顶部栏 function view.get_headers (db) - return db:query([[ SELECT id, name, url FROM cfadmin_headers WHERE active = '1' ORDER BY id LIMIT 8 ]]) + return db:query([[ SELECT id, name, url FROM cfadmin_headers WHERE active = '1' ORDER BY id]]) end -- 获取菜单栏 diff --git a/lualib/admin/html/login/action.html b/lualib/admin/html/login/action.html index 04b5d5b2..ab870379 100644 --- a/lualib/admin/html/login/action.html +++ b/lualib/admin/html/login/action.html @@ -16,7 +16,7 @@ }); } layer.msg(res.msg, {time:1000}, function () { - window.location.href = data.url + '?' + 'token=' + data.token + window.location.href = res.url + '?token=' + res.token }); }, error: function (res) { diff --git a/lualib/admin/http/dashboard.lua b/lualib/admin/http/dashboard.lua index 20063255..01f3ba83 100644 --- a/lualib/admin/http/dashboard.lua +++ b/lualib/admin/http/dashboard.lua @@ -4,9 +4,9 @@ local Cookie = require "admin.cookie" local config = require "admin.config" local locales = require "admin.locales" local role = require "admin.db.role" +local view = require "admin.db.view" local user = require "admin.db.user" local user_token = require "admin.db.token" -local view = require "admin.db.view" local type = type local ipairs = ipairs @@ -16,49 +16,31 @@ local user_have_permission = utils.user_have_permission local template_path = 'lualib/admin/html/dashboard/base.html' --- 管理页面验权 local function verify_permission (content, db) local args = content.args - -- 切换语言 - if type(args) == 'table' and args.lang then - local lang = args.lang - local locale = config.locales[lang] - if locale then - Cookie.setCookie('CFLANG', lang) - else - Cookie.setCookie('CFLANG', config.locale) - end - return false, config.dashboard - end - -- 登录注入Cookie - if type(args) == 'table' and args.token then - local token = args.token - if not token then -- 对一些错误传参直接重定向到登录页 - return false, config.login_render + if type(args) == 'table' then + local lang, token = args.lang, args.token + if lang then -- 切换语言 + Cookie.setCookie('CFLANG', config.locales[lang] and lang or config.locale) + return false, config.dashboard end - -- 开启验证Token - local exists = user_token.token_exists(db, token) - if not exists then -- Token不存在需要重新登录 - return false, config.login_render + if token then -- 登录授权 + local exists = user_token.token_exists(db, token) + if not exists then -- Token不存在需要重新登录 + return false, config.login_render + end + Cookie.setCookie("CFTOKEN", token) + return false, config.dashboard end - -- 如果是第一次带上Token访问后, admin会给予一个重写URL后的url. - -- 同时admin会在这里将Token写入到Cookie中去, 用户无需感知. - Cookie.setCookie("CFTOKEN", token) - return false, utils.get_path(content) end local token = Cookie.getCookie('CFTOKEN') if not token then -- 未授权的访问 return false, config.login_render end - local exists = user_token.token_exists(db, token) - if not exists then -- Token不存在需要重新登录 - return false, config.login_render - end - local info = user.user_info(db, exists.uid) + local info = user_token.token_to_userinfo(db, token) if not info then return false, config.login_render end - info.token = exists.token info.is_admin = info.is_admin == 1 info.roles = role.role_permissions(db, info.role) return true, info @@ -138,13 +120,13 @@ function dashboard.render(content) menus = get_menus(db, {is_admin = user.is_admin, roles = user.roles}), headers = get_headers(db), username = user.name, + is_admin = user.is_admin, logout = config.login_render, user = config.system_user_render, menu = config.system_menu_render, header = config.system_header_render, role = config.system_role_render, profile = config.profile_render, - is_admin = user.is_admin, locale = get_locale(Cookie.getCookie("CFLANG")) } end diff --git a/lualib/admin/http/login.lua b/lualib/admin/http/login.lua index 911eeea5..68abf419 100644 --- a/lualib/admin/http/login.lua +++ b/lualib/admin/http/login.lua @@ -55,11 +55,7 @@ function login.response (content) if not ok then return json_encode({code = 500, msg = err}) end - return json_encode({ - code = 200, - token = TOKEN, - url = config.dashboard, - }) + return json_encode({code = 0, msg = "登录成功", token = TOKEN, url = config.dashboard }) end return login diff --git a/lualib/admin/token.lua b/lualib/admin/token.lua index 9103a152..4057fe1c 100644 --- a/lualib/admin/token.lua +++ b/lualib/admin/token.lua @@ -28,9 +28,9 @@ function token.generate (uid) end -- 解析token -function token.parser (token) - local str = token.decode(token) - return match(str, '([%d]+):([%d]+)') -end +-- function token.parser (token) +-- local str = token.decode(token) +-- return match(str, '([%d]+):([%d%.]+)') +-- end return token diff --git a/lualib/admin/utils/init.lua b/lualib/admin/utils/init.lua index 4bd8f58d..c67e2d55 100644 --- a/lualib/admin/utils/init.lua +++ b/lualib/admin/utils/init.lua @@ -1,13 +1,10 @@ -local crypt = require "crypt" local template = require "template" local config = require "admin.config" local type = type local assert = assert local ipairs = ipairs -local tonumber = tonumber -local io_open = io.open local split = string.sub local find = string.find local concat = table.concat From 4840f06294f2b8fdaf1be7da954a21930e2caf63 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 21 May 2019 12:40:22 +0800 Subject: [PATCH 043/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0admin=E5=BA=93?= =?UTF-8?q?=E6=90=AD=E5=BB=BA=E6=95=88=E6=9E=9C=E9=A2=84=E8=A7=88=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index b971ec8a..697a3dc2 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,24 @@ > [我有一些cf使用上的问题?](https://github.com/CandyMi/core_framework/wiki/QA) +## 看一下cf的通用后台开发模板预览图吧! + +

                  + +

                  + +

                  + +

                  + +

                  + +

                  + +

                  + +

                  + ## 联系作者 > [issues](https://github.com/CandyMi/core_framework/issues) From 9596b6de331d7d1ef26b50eef36088d7ae9f3711 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 21 May 2019 16:16:43 +0800 Subject: [PATCH 044/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dhttpd.Route=E7=9A=84?= =?UTF-8?q?=E5=8F=82=E6=95=B0bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Router.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index 99d7ed9e..445e315b 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -40,7 +40,7 @@ local typ = { -- 分割路径后进行hex, 得到key后一次查表即可完成 local function hex_route(route) local tab = {} - for r in splite(route, '/([^/?]+)') do + for r in splite(route, '/([^/%?]+)') do tab[#tab + 1] = r end return hexencode(concat(tab)) @@ -61,7 +61,7 @@ local function registery_router (route, class, route_type) end local function find_route (path) - local hex = hex_route(path) + local hex = hex_route(split(path, 1, (find(path, '?') or 0) - 1)) local t = routes[hex] if t then return t.class, t.type From 849f53893721c2a8d1a5fb91646c65db26587c8c Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 21 May 2019 17:21:37 +0800 Subject: [PATCH 045/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9xml=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E4=B8=BA2=E4=B8=ADheader=E7=B1=BB=E5=9E=8B=E8=AF=86=E5=88=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Cookie.lua | 3 +++ lualib/protocol/http.lua | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lualib/httpd/Cookie.lua b/lualib/httpd/Cookie.lua index 5eec6a06..c7a296b7 100644 --- a/lualib/httpd/Cookie.lua +++ b/lualib/httpd/Cookie.lua @@ -64,6 +64,9 @@ end function Cookie.getCookie (name) local co = co_self() local cs = Cookie.client[co] + if not cs then + return + end local value = cs[name] if not value then return diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index 28f90be4..11e0d2b1 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -256,7 +256,8 @@ local function PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEA end end local FILE_ENCODE = 'multipart/form-data' - local XML_ENCODE = 'application/xml' + local XML_ENCODE_1 = 'text/xml' + local XML_ENCODE_2 = 'application/xml' local JSON_ENCODE = 'application/json' local URL_ENCODE = 'application/x-www-form-urlencoded' local format = match(HEADER['Content-Type'], '(.-/[^;]*)') @@ -276,7 +277,7 @@ local function PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEA elseif format == JSON_ENCODE then content['json'] = true content['body'] = BODY - elseif format == XML_ENCODE then + elseif format == XML_ENCODE_1 or format == XML_ENCODE_2 then content['xml'] = true content['body'] = BODY elseif format == URL_ENCODE then From 753a11b45ba71d52a0eb078f8fea870d8a5cee94 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 21 May 2019 17:22:29 +0800 Subject: [PATCH 046/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E5=AD=97=E7=AC=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/locales/EN-US.lua | 2 +- lualib/admin/locales/ZH-CN.lua | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lualib/admin/locales/EN-US.lua b/lualib/admin/locales/EN-US.lua index 9ae8282f..5d1e0072 100644 --- a/lualib/admin/locales/EN-US.lua +++ b/lualib/admin/locales/EN-US.lua @@ -22,7 +22,7 @@ return { ['dashboard.header.profile.update_userinfo'] = 'Update Profile', ['dashboard.header.profile.form.name'] = 'Name', ['dashboard.header.profile.form.name.notice'] = 'User Display Name', - ['dashboard.header.profile.form.username'] = 'User Name', + ['dashboard.header.profile.form.username'] = 'User Account', ['dashboard.header.profile.form.username.notice'] = 'User Account', ['dashboard.header.profile.form.email'] = 'Email', ['dashboard.header.profile.form.phone'] = 'Phone', diff --git a/lualib/admin/locales/ZH-CN.lua b/lualib/admin/locales/ZH-CN.lua index 88c4f90b..4e34c427 100644 --- a/lualib/admin/locales/ZH-CN.lua +++ b/lualib/admin/locales/ZH-CN.lua @@ -20,9 +20,9 @@ return { ['dashboard.header.langs.simplified-chinese'] = '简体-中文', ['dashboard.header.profile.update_userinfo'] = '更新用户信息', - ['dashboard.header.profile.form.name'] = '用户名称', + ['dashboard.header.profile.form.name'] = '名称', ['dashboard.header.profile.form.name.notice'] = '用户显示名称', - ['dashboard.header.profile.form.username'] = '用户账户', + ['dashboard.header.profile.form.username'] = '账户', ['dashboard.header.profile.form.username.notice'] = '用户登录账户', ['dashboard.header.profile.form.email'] = '邮箱', ['dashboard.header.profile.form.phone'] = '手机', From d177625c701bc8250ba32a442a5f58ffe1a06c7c Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 21 May 2019 17:55:45 +0800 Subject: [PATCH 047/956] =?UTF-8?q?=E4=B8=BAjson=E4=BF=9D=E6=8A=A4?= =?UTF-8?q?=E8=B0=83=E7=94=A8encode=E4=B8=8Edecode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/json.lua | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/lualib/json.lua b/lualib/json.lua index a81e105a..3906e4b8 100644 --- a/lualib/json.lua +++ b/lualib/json.lua @@ -1,5 +1,9 @@ local cjson = require "cjson" +local cjson_encode = cjson.encode +local cjson_decode = cjson.decode +local cjson_array_mt = cjson.array_mt + -- this lib fork from resty cjson, only modified some compatible codes -- more details please check it. cjson.decode_array_with_array_mt(true) @@ -7,8 +11,6 @@ cjson.decode_array_with_array_mt(true) local CJSON = { null = null, _VERSION = cjson._VERSION, - encode = cjson.encode, - decode = cjson.decode, array_mt = cjson.array_mt, empty_array = cjson.empty_array, empty_array_mt = cjson.empty_array_mt, @@ -23,4 +25,18 @@ function CJSON.sparse_array_to_null(array) return setmetatable(array, cjson.array_mt) end -return CJSON \ No newline at end of file +function CJSON.encode (...) + local ok, data = pcall(cjson_encode, ...) + if ok then + return data + end +end + +function CJSON.decode (...) + local ok, data = pcall(cjson_decode, ...) + if ok then + return data + end +end + +return CJSON From 4d79093249fb20a40396e40286a3fdffefd4cf42 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 21 May 2019 18:04:18 +0800 Subject: [PATCH 048/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9cjson=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E5=85=81=E8=AE=B8=E7=A8=80=E7=96=8F=E6=95=B0=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/json.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lualib/json.lua b/lualib/json.lua index 3906e4b8..940f5de5 100644 --- a/lualib/json.lua +++ b/lualib/json.lua @@ -6,8 +6,12 @@ local cjson_array_mt = cjson.array_mt -- this lib fork from resty cjson, only modified some compatible codes -- more details please check it. + cjson.decode_array_with_array_mt(true) +-- 默认允许稀疏数组 +cjson.encode_sparse_array(true) + local CJSON = { null = null, _VERSION = cjson._VERSION, @@ -15,6 +19,7 @@ local CJSON = { empty_array = cjson.empty_array, empty_array_mt = cjson.empty_array_mt, encode_max_depth = cjson.encode_max_depth, + encode_sparse_array = cjson.encode_sparse_array, decode_max_depth = cjson.decode_max_depth, decode_array_with_array_mt = cjson.decode_array_with_array_mt, encode_empty_table_as_object = cjson.encode_empty_table_as_object, @@ -25,6 +30,7 @@ function CJSON.sparse_array_to_null(array) return setmetatable(array, cjson.array_mt) end +-- json序列化 function CJSON.encode (...) local ok, data = pcall(cjson_encode, ...) if ok then @@ -32,6 +38,7 @@ function CJSON.encode (...) end end +-- json反序列化 function CJSON.decode (...) local ok, data = pcall(cjson_decode, ...) if ok then From 0d9d8970f64cdae77cf3cd9af19098ca3cf84cd5 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 21 May 2019 18:48:10 +0800 Subject: [PATCH 049/956] =?UTF-8?q?=E8=B0=83=E6=95=B4view=E7=9A=84?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E9=AA=8C=E6=9D=83,=20=E4=B8=BAadmin=E5=BA=93?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0httpctx=E4=BE=BF=E4=BA=8E=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/httpctx.lua | 71 +++++++++++++++++++++++++++++++++++++ lualib/admin/utils/init.lua | 7 +--- lualib/admin/view.lua | 59 +++++++++++++++++++++--------- script/test_cfadmin.lua | 13 ++++++- 4 files changed, 127 insertions(+), 23 deletions(-) create mode 100644 lualib/admin/httpctx.lua diff --git a/lualib/admin/httpctx.lua b/lualib/admin/httpctx.lua new file mode 100644 index 00000000..95a864b6 --- /dev/null +++ b/lualib/admin/httpctx.lua @@ -0,0 +1,71 @@ +local class = require "class" +local Cookie = require "admin.cookie" + +local xml2lua = require "xml2lua" + +local json = require "json" + +local json_encode = json.encode +local json_decode = json.decode + +local type = type +local split = string.sub +local find = string.find + +local ctx = class("ctx") + +function ctx:ctor (opt) + self._content = opt.content +end + +-- 获取请求path +function ctx:get_path () + return split(self._content.path, 1, (find(self._content.path, '?') or 0) - 1) +end + +-- 获取原始path +function ctx:get_raw_path () + return self._content.path +end + +-- 获取请求头部 +function ctx:get_headers () + return self._content.headers +end + +-- 获取请求方法 +function ctx:get_method () + return self._content.method +end + +-- 获取Cookie +function ctx:get_cookie (name) + return Cookie.getCookie(name) +end + +-- 获取请求参数表 +function ctx:get_args () + local args = self._content.args + if type(args) == 'table' then + return args + end + local body = self._content.body + if body then + local xml = self._content.xml + local json = self._content.json + if xml then + args = xml2lua.parser(body) + if type(args) == 'table' then + return args + end + end + if json then + args = json_decode(body) + if type(args) == 'table' then + return args + end + end + end +end + +return ctx diff --git a/lualib/admin/utils/init.lua b/lualib/admin/utils/init.lua index c67e2d55..ba416a85 100644 --- a/lualib/admin/utils/init.lua +++ b/lualib/admin/utils/init.lua @@ -39,12 +39,7 @@ end -- 获取页面url function utils.get_path (content) - local path = content['path'] - local _, Pos = find(path, '?') - if Pos then - path = split(path, 1, Pos - 1) - end - return path + return split(content['path'], 1, (find(content['path'], '?') or 0) - 1) end -- 获取语言 diff --git a/lualib/admin/view.lua b/lualib/admin/view.lua index 6b0ef13d..107a5041 100644 --- a/lualib/admin/view.lua +++ b/lualib/admin/view.lua @@ -1,11 +1,16 @@ local template = require "template" local utils = require "admin.utils" +local Cookie = require "admin.cookie" local config = require "admin.config" local user = require "admin.db.user" +local httpctx = require "admin.httpctx" local user_token = require "admin.db.token" local permission = require "admin.db.permission" +local type = type +local assert = assert + local get_path = utils.get_path -- 用户自定义view页面需要验权 @@ -14,27 +19,49 @@ local function verify_permission (content, db) if not token then return false, config.login_render end - local exists = user_token.token_exists(db, token) - if not exists then -- Token不存在需要重新登录 - return false, config.login_render - end - local info = user.user_info(db, exists.uid) - if not info or info.is_admin ~= 1 or not permission.user_have_menu_permission(db, info.id, get_path(content)) - return false, config.login_render + local info = user_token.token_to_userinfo(db, token) + if info and (info.is_admin == 1 or permission.user_have_menu_permission(db, info.id, get_path(content))) then + return true end - return true + return false, config.login_render end local view = {} --- 渲染模板 -function view.render (content, path) - local db = config.db - local ok, page = verify_permission(content, db) - if not ok then - return utils.redirect(page) - end - return template.compile(path) +-- 页面路由 +function view.use (path, cls) + assert(type(path) == 'string', 'view use path failed.') + assert(type(cls) == 'function' or type(cls) == 'table', 'view use path failed.') + local db, app = config.db, config.app + return app:use(path, function (content) + local ok, url = verify_permission(content, db) + if not ok then + return utils.redirect(url) + end + local ok, html + if type(cls) == 'function' then + ok, html = pcall(cls, {ctx = httpctx:new{content = content, db = db}}) + else + ok, html = pcall(cls:new({ctx = httpctx:new{content = content, db = db}}), content['method']:lower()) + end + return html + end) +end + +-- 接口路由 +function view.api (path, cls) + assert(type(path) == 'string', 'view api path failed.') + assert(type(cls) == 'function' or type(cls) == 'table', 'view use path failed.') + local db, app = config.db, config.app + return app:api(path, function (content) + local ok, res + if type(cls) == 'function' then + ok, res = pcall(cls, {ctx = httpctx:new{content = content, db = db}}) + else + ok, res = pcall(cls:new({ctx = httpctx:new{content = content, db = db}}), content['method']:lower()) + end + return res + end) end return view diff --git a/script/test_cfadmin.lua b/script/test_cfadmin.lua index 62d5098a..692c698e 100644 --- a/script/test_cfadmin.lua +++ b/script/test_cfadmin.lua @@ -43,7 +43,18 @@ cfadmin.init_db() -- cfadmin.init_home(location or domain + path) -- cfadmin.init_home('https://www.baidu.com') - +local view = require "admin.view" +-- 参数: +-- 1. ctx是一个http req 对象, 目前内置包括: get_method, get_args, get_path, get_raw_path, get_headers, get_cookie +-- 2. db初始化后的db对象, 方便用户直接使用. + +view.use('/test1', function (ctx, db) + return "hello world" +end) + +view.api('/test2', function (ctx, db) + return '{"code":0,"msg":"hello world"}' +end) -- 这里是设置语言的地方 -- 语言表在admin/locales内, 可参照key -> value进行填写. From 8f5afa9293ae84b47f125643f278cac3a8269a0f Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 22 May 2019 22:25:00 +0800 Subject: [PATCH 050/956] =?UTF-8?q?admin=E5=A2=9E=E5=8A=A0=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E7=9A=84=E7=94=A8=E6=88=B7=E5=90=8D=E4=B8=8E=E5=90=8D?= =?UTF-8?q?=E7=A7=B0=E9=AA=8C=E8=AF=81,=20menu=E5=A2=9E=E5=8A=A0=E5=90=8D?= =?UTF-8?q?=E7=A7=B0=E9=AA=8C=E8=AF=81;view=E4=BF=AE=E5=A4=8Dclass?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/db/menu.lua | 8 ++++++++ lualib/admin/db/user.lua | 12 ++++++++---- lualib/admin/http/system/menu.lua | 3 +++ lualib/admin/http/system/user.lua | 2 +- lualib/admin/view.lua | 8 +++++--- 5 files changed, 25 insertions(+), 8 deletions(-) diff --git a/lualib/admin/db/menu.lua b/lualib/admin/db/menu.lua index 7840f7e9..2191f939 100644 --- a/lualib/admin/db/menu.lua +++ b/lualib/admin/db/menu.lua @@ -12,6 +12,14 @@ function menu.menu_list (db, opt) return db:query(fmt([[SELECT id, parent, name, url, icon, create_at, update_at FROM cfadmin_menus WHERE active = '1' LIMIT %s, %s]], limit * (page - 1), limit)) end +function menu.menu_name_exists (db, name) + local ret = db:query(fmt([[SELECT name FROM cfadmin_menus WHERE name = '%s' AND active = '1']], name)) + if ret and #ret > 0 then + return true + end + return false +end + -- 菜单信息 function menu.menu_info (db, id) return db:query(fmt([[SELECT id, name, url, icon FROM cfadmin_menus WHERE id = '%s' AND active = '1']], id))[1] diff --git a/lualib/admin/db/user.lua b/lualib/admin/db/user.lua index 9b307074..c8848f8a 100644 --- a/lualib/admin/db/user.lua +++ b/lualib/admin/db/user.lua @@ -3,10 +3,6 @@ local tostring = tostring local os_time = os.time local fmt = string.format -local json = require "json" -local json_encode = json.encode -local json_decode = json.decode - local user = {} -- 用户列表 @@ -51,6 +47,14 @@ function user.user_exists (db, username, uid) tostring(username), toint(uid)))[1] end +function user.user_name_or_username_exists (db, name, username) + local ret, err = db:query(fmt([[SELECT name, username FROM cfadmin_users WHERE name = '%s' OR username = '%s' AND active = '1']], name, username)) + if ret and #ret > 0 then + return true + end + return false +end + -- 用户信息 function user.user_info (db, uid) local ret, err = db:query(fmt([[ diff --git a/lualib/admin/http/system/menu.lua b/lualib/admin/http/system/menu.lua index 890a9f3d..dd9b3a02 100644 --- a/lualib/admin/http/system/menu.lua +++ b/lualib/admin/http/system/menu.lua @@ -198,6 +198,9 @@ function system.menu_response (content) args.name = url_decode(args.name) args.url = url_decode(args.url) or "NULL" args.icon = url_decode(args.icon) + if menu.menu_name_exists then + return json_encode({code = 401, msg = '菜单名已存在'}) + end menu.menu_add(db, args) return json_encode({code = 0, msg = 'Success'}) end diff --git a/lualib/admin/http/system/user.lua b/lualib/admin/http/system/user.lua index c536d802..b4991786 100644 --- a/lualib/admin/http/system/user.lua +++ b/lualib/admin/http/system/user.lua @@ -147,7 +147,7 @@ function system.user_response (content) end args.name = url.decode(args.name) args.email = url.decode(args.email) - local exists = user.user_exists(db, args.username) + local exists = user.user_name_or_username_exists(db, args.name, args.username) if exists then return json_encode({code = 400, data = null, msg = '4. 用户已存在'}) end diff --git a/lualib/admin/view.lua b/lualib/admin/view.lua index 107a5041..f7bd8dfd 100644 --- a/lualib/admin/view.lua +++ b/lualib/admin/view.lua @@ -42,7 +42,8 @@ function view.use (path, cls) if type(cls) == 'function' then ok, html = pcall(cls, {ctx = httpctx:new{content = content, db = db}}) else - ok, html = pcall(cls:new({ctx = httpctx:new{content = content, db = db}}), content['method']:lower()) + local c = cls:new({ctx = httpctx:new{content = content, db = db}}) + ok, html = pcall(c, c, content['method']:lower()) end return html end) @@ -51,14 +52,15 @@ end -- 接口路由 function view.api (path, cls) assert(type(path) == 'string', 'view api path failed.') - assert(type(cls) == 'function' or type(cls) == 'table', 'view use path failed.') + assert(type(cls) == 'function' or type(cls) == 'table', 'view api path failed.') local db, app = config.db, config.app return app:api(path, function (content) local ok, res if type(cls) == 'function' then ok, res = pcall(cls, {ctx = httpctx:new{content = content, db = db}}) else - ok, res = pcall(cls:new({ctx = httpctx:new{content = content, db = db}}), content['method']:lower()) + local c = cls:new({ctx = httpctx:new{content = content, db = db}}) + ok, res = pcall(c, c, content['method']:lower()) end return res end) From a3095b268a1310d2380a25e65fc92116dfc0535a Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 23 May 2019 17:42:16 +0800 Subject: [PATCH 051/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0before=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E6=95=B0=E6=8D=AE=E8=B7=AF=E7=94=B1,=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E7=94=A8=E6=88=B7=E5=90=8D=E9=AA=8C=E8=AF=81=E5=92=8C?= =?UTF-8?q?=E5=85=B6=E5=AE=83=E7=9A=84=E4=B8=80=E4=BA=9Bbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/config.lua | 2 +- lualib/admin/db/menu.lua | 1 + lualib/admin/db/role.lua | 5 ++ lualib/admin/db/user.lua | 3 +- lualib/admin/html/profile/base.html | 61 ++++++++++++------- .../admin/html/system/header/header-add.html | 24 +++++--- .../admin/html/system/header/header-edit.html | 22 ++++--- lualib/admin/html/system/header/header.html | 29 +++++---- lualib/admin/html/system/menu/menu-add.html | 32 ++++++---- lualib/admin/html/system/menu/menu-edit.html | 30 +++++---- lualib/admin/html/system/menu/menu.html | 19 +++--- lualib/admin/html/system/role/role-add.html | 3 +- lualib/admin/html/system/role/role-edit.html | 3 +- lualib/admin/html/system/role/role.html | 24 ++++---- lualib/admin/html/system/user/user-add.html | 24 +++++--- lualib/admin/html/system/user/user-edit.html | 32 ++++++---- lualib/admin/html/system/user/user.html | 38 ++++++++---- lualib/admin/http/system/menu.lua | 2 +- lualib/admin/http/system/role.lua | 2 +- lualib/admin/init.lua | 33 ++++++++-- lualib/admin/view.lua | 35 ++++++----- lualib/protocol/http.lua | 1 + 22 files changed, 269 insertions(+), 156 deletions(-) diff --git a/lualib/admin/config.lua b/lualib/admin/config.lua index dd7f3f52..a328f266 100644 --- a/lualib/admin/config.lua +++ b/lualib/admin/config.lua @@ -1,5 +1,5 @@ local config = { - cdn = 'http://localhost:8080/', -- 静态文件前缀地址 + cdn = '/', -- 静态文件前缀地址 -- github = 'https://github.com/candymi/core_framework', -- 跳转地址 cache = false, -- 是否缓存模板 locale = "ZH-CN", -- 当前语言 diff --git a/lualib/admin/db/menu.lua b/lualib/admin/db/menu.lua index 2191f939..3251c3b8 100644 --- a/lualib/admin/db/menu.lua +++ b/lualib/admin/db/menu.lua @@ -12,6 +12,7 @@ function menu.menu_list (db, opt) return db:query(fmt([[SELECT id, parent, name, url, icon, create_at, update_at FROM cfadmin_menus WHERE active = '1' LIMIT %s, %s]], limit * (page - 1), limit)) end +-- 菜单名已存在 function menu.menu_name_exists (db, name) local ret = db:query(fmt([[SELECT name FROM cfadmin_menus WHERE name = '%s' AND active = '1']], name)) if ret and #ret > 0 then diff --git a/lualib/admin/db/role.lua b/lualib/admin/db/role.lua index d71de0ab..3dd9504a 100644 --- a/lualib/admin/db/role.lua +++ b/lualib/admin/db/role.lua @@ -22,6 +22,11 @@ function role.role_list (db, opt) return roles end +-- 计算角色数量 +function role.role_count (db) + return db:query([[SELECT count(id) AS count FROM cfadmin_roles WHERE active = '1']])[1]['count'] +end + -- 角色对应权限 function role.role_permissions (db, id) return db:query(fmt([[SELECT role_id, menu_id FROM cfadmin_permissions WHERE role_id = '%s' AND active = '1']], id)) diff --git a/lualib/admin/db/user.lua b/lualib/admin/db/user.lua index c8848f8a..58806054 100644 --- a/lualib/admin/db/user.lua +++ b/lualib/admin/db/user.lua @@ -47,8 +47,9 @@ function user.user_exists (db, username, uid) tostring(username), toint(uid)))[1] end +-- 用户名或者登录名是否存在 function user.user_name_or_username_exists (db, name, username) - local ret, err = db:query(fmt([[SELECT name, username FROM cfadmin_users WHERE name = '%s' OR username = '%s' AND active = '1']], name, username)) + local ret, err = db:query(fmt([[SELECT name, username FROM cfadmin_users WHERE active = '1' AND (name = '%s' OR username = '%s')]], name, username)) if ret and #ret > 0 then return true end diff --git a/lualib/admin/html/profile/base.html b/lualib/admin/html/profile/base.html index 6e778f44..2b795352 100644 --- a/lualib/admin/html/profile/base.html +++ b/lualib/admin/html/profile/base.html @@ -124,34 +124,49 @@ // 更新用户密码 form.on('submit(pwsubmit)', function(data) { $('#pwsubmit').hide(); - $.post('{*api_url*}', data.field, function (data, status) { - if (data.code != 0 ){ - return layer.msg(data.msg, {time:2000}, function () { - $('#pwsubmit').show(); - }); - }; - layer.msg(data.msg, {time:1000}, function () { - xadmin.close(); - xadmin.father_reload(); - }) - }); + $.ajax({ + url: "{*api_url*}", type:"POST", headers:{token:"{*user.token*}"}, data: data.field, + error: function (res) { + return layer.msg("请求失败", {time:2000}, function () { + $('#pwsubmit').show(); + }); + }, + success: function (res) { + if (res.code != 0 ){ + return layer.msg(res.msg, {time:2000}, function () { + $('#pwsubmit').show(); + }); + }; + layer.msg(res.msg, {time:1000}, function () { + xadmin.close(); + xadmin.father_reload(); + }) + }, + }); return false; }); - // 更新用户信息 form.on('submit(profilesubmit)', function(data) { $('#profilesubmit').hide(); - $.post('{*api_url*}', data.field, function (data, status) { - if (data.code != 0 ){ - return layer.msg(data.msg, {time:2000}, function () { - $('#profilesubmit').show(); - }); - }; - layer.msg(data.msg, {time:1000}, function () { - xadmin.close(); - xadmin.father_reload(); - }) - }); + $.ajax({ + url: "{*api_url*}", type:"POST", headers:{token:"{*user.token*}"}, data: data.field, + error: function (res) { + return layer.msg("请求失败", {time:2000}, function () { + $('#profilesubmit').show(); + }); + }, + success: function (res) { + if (res.code != 0 ){ + return layer.msg(res.msg, {time:2000}, function () { + $('#profilesubmit').show(); + }); + }; + layer.msg(res.msg, {time:1000}, function () { + xadmin.close(); + xadmin.father_reload(); + }) + }, + }); return false; }); }); diff --git a/lualib/admin/html/system/header/header-add.html b/lualib/admin/html/system/header/header-add.html index ca1a3ec9..86f75f94 100644 --- a/lualib/admin/html/system/header/header-add.html +++ b/lualib/admin/html/system/header/header-add.html @@ -67,16 +67,24 @@ var {form, layer} = layui; form.on('submit(add)',function(data) { $('#submit').hide(); - $.post("{*api_url*}", data.field, function (data, status) { - if (data.code != 0) { - return layer.msg(data.msg, {time:2000}, function () { + $.ajax({ + url: "{*api_url*}", type: "POST", headers: {token:"{*token*}"}, data: data.field, + error: function (res) { + return layer.msg('请求失败', {time:2000}, function () { $('#submit').show(); }); - } - layer.msg(data.msg, {time:500}, function () { - xadmin.close(); - xadmin.father_reload(); - }); + }, + success: function (res) { + if (res.code != 0) { + return layer.msg(res.msg, {time:2000}, function () { + $('#submit').show(); + }); + } + return layer.msg(res.msg, {time:1000}, function () { + xadmin.close(); + xadmin.father_reload(); + }); + }, }); return false; }); diff --git a/lualib/admin/html/system/header/header-edit.html b/lualib/admin/html/system/header/header-edit.html index 8ac229a7..8dd2bbd7 100644 --- a/lualib/admin/html/system/header/header-edit.html +++ b/lualib/admin/html/system/header/header-edit.html @@ -72,16 +72,24 @@ var {form, layer} = layui; form.on('submit(edit)',function(data) { $('#submit').hide(); - $.post("{*api_url*}", data.field, function (data, status) { - if (data.code != 0) { - return layer.msg(data.msg, {time:2000}, function () { + $.ajax({ + url: "{*api_url*}", type: "POST", headers: {token:"{*token*}"}, data: data.field, + error: function (res) { + return layer.msg("请求失败", {time:2000}, function () { $('#submit').show(); }); + }, + success: function (res) { + if (res.code != 0) { + return layer.msg(res.msg, {time:2000}, function () { + $('#submit').show(); + }); + } + return layer.msg(res.msg, {time:1000}, function () { + xadmin.close(); + xadmin.father_reload(); + }); } - layer.msg(data.msg, {time:500}, function () { - xadmin.close(); - xadmin.father_reload(); - }); }); return false; }); diff --git a/lualib/admin/html/system/header/header.html b/lualib/admin/html/system/header/header.html index 95c3f0d9..ecac20cf 100644 --- a/lualib/admin/html/system/header/header.html +++ b/lualib/admin/html/system/header/header.html @@ -17,7 +17,7 @@ - +
                  @@ -51,24 +51,23 @@ table.on('tool(table)', function(obj){ var data = obj.data; if(obj.event === 'delete'){ - layer.confirm("", { - title:"", - content:"{*locale['dashboard.menu.header_manage.table.options.delete.confirm']*}" - }, function(index){ - $.post('{*api_url*}', { - action: "delete", - headerid: data.id, - token: "{*token*}", + layer.confirm("", {title:"", content:"{*locale['dashboard.menu.header_manage.table.options.delete.confirm']*}"}, + function(index){ + $.ajax({ + url: "{*api_url*}", type: "POST", headers: {token:"{*token*}"}, + data: { action: "delete", token: "{*token*}", headerid: data.id}, + error: function (res) { + return layer.msg('请求失败',{time:2000}); }, - function (data, status) { - if (data.code != 0) { - return layer.msg(data.msg, {time:2000}); + success: function (res) { + if (res.code != 0) { + return layer.msg(res.msg, {time:2000}); } - layer.msg(data.msg, {time:1000}, function () { + return layer.msg(res.msg, {time:1000}, function () { return window.location.reload(); - }) + }); } - ); + }); return false; }); } else if(obj.event === 'edit'){ diff --git a/lualib/admin/html/system/menu/menu-add.html b/lualib/admin/html/system/menu/menu-add.html index 4e08fa52..574d166e 100644 --- a/lualib/admin/html/system/menu/menu-add.html +++ b/lualib/admin/html/system/menu/menu-add.html @@ -84,18 +84,26 @@ //监听提交 form.on('submit(add)', function(data) { $('#submit').hide(); - $.post('{*api_url*}', data.field, function (data, status) { - if (data.code != 0 ){ - return layer.msg(data.msg , {time:2000}, function () { - $('#submit').show(); - }); - }; - layer.msg(data.msg, {time:1000}, function () { - xadmin.close(); - xadmin.father_reload(); - }) - }) - return false; + $.ajax({ + url: "{*api_url*}", type: "POST", headers: {token:"{*token*}"}, data: data.field, + error: function (res) { + return layer.msg('请求失败', {time:2000}, function () { + $('#submit').show(); + }); + }, + success: function (res) { + if (res.code != 0) { + return layer.msg(res.msg, {time:2000}, function () { + $('#submit').show(); + }); + } + return layer.msg(res.msg, {time:1000}, function () { + xadmin.close(); + xadmin.father_reload(); + }); + }, + }); + return false; }); }); diff --git a/lualib/admin/html/system/menu/menu-edit.html b/lualib/admin/html/system/menu/menu-edit.html index df50e536..b2fa7d7a 100644 --- a/lualib/admin/html/system/menu/menu-edit.html +++ b/lualib/admin/html/system/menu/menu-edit.html @@ -84,17 +84,25 @@ //监听提交 form.on('submit(edit)', function(data) { $('#submit').hide(); - $.post('{*api_url*}', data.field, function (data, status) { - if (data.code != 0 ){ - return layer.msg(data.msg , {time:2000}, function () { - $('#submit').show(); - }); - }; - layer.msg(data.msg, {time:1000}, function () { - xadmin.close(); - xadmin.father_reload(); - }) - }) + $.ajax({ + url: "{*api_url*}", type: "POST", headers: {token:"{*token*}"}, data: data.field, + error: function (res) { + return layer.msg('请求失败', {time:2000}, function () { + $('#submit').show(); + }); + }, + success: function (res) { + if (res.code != 0) { + return layer.msg(res.msg, {time:2000}, function () { + $('#submit').show(); + }); + } + return layer.msg(res.msg, {time:1000}, function () { + xadmin.close(); + xadmin.father_reload(); + }); + }, + }); return false; }); diff --git a/lualib/admin/html/system/menu/menu.html b/lualib/admin/html/system/menu/menu.html index 583ee80d..f9e5e72d 100644 --- a/lualib/admin/html/system/menu/menu.html +++ b/lualib/admin/html/system/menu/menu.html @@ -108,15 +108,20 @@ /*用户-删除*/ function member_del(obj, field){ layer.confirm("", {title:"", content:"{*locale['dashboard.menu.menu_manage.table.options.confirm']*}"}, function(index){ - $.post('{*api_url*}', field, - function (data, status) { - if (data.code != 0) { - return layer.msg(data.msg, {time:2000}); + $.ajax({ + url: "{*api_url*}", type: "POST", headers: {token:"{*token*}"}, data: field, + error: function (res) { + return layer.msg('请求失败', {time:2000}); + }, + success: function (res) { + if (res.code != 0) { + return layer.msg(res.msg, {time:2000}); } - layer.msg(data.msg, {time:1000}, function () { - return window.location.reload(); + return layer.msg(res.msg, {time:1000}, function () { + return window.location.reload(); }); - }); + }, + }) }); } diff --git a/lualib/admin/html/system/role/role-add.html b/lualib/admin/html/system/role/role-add.html index 27a57364..5d795b08 100644 --- a/lualib/admin/html/system/role/role-add.html +++ b/lualib/admin/html/system/role/role-add.html @@ -66,7 +66,7 @@ var {dtree, form, layer} = layui; var tree = dtree.render({ - elem: '#dataTree', dataStyle: "layuiStyle", dataFormat: "list", response: {message:"msg", statusCode:0}, // 固定参数1 + elem: '#dataTree', dataStyle: "layuiStyle", dataFormat: "list", response: {message:"msg", statusCode:0}, headers:{token:'{*token*}'}, // 固定参数1 checkbar: true, checkbarLoad: "node", request: {token:"{*token*}", action:"get_tree_list"}, url: '{*api_url*}', // 固定参数2 initLevel: 1, // 控制折叠菜单权限列表 }) @@ -89,6 +89,7 @@ url: "{*api_url*}", type: "POST", dataType:"json", + headers:{token:'{*token*}'}, contentType: "application/json", data: JSON.stringify({ name : data.field.name, diff --git a/lualib/admin/html/system/role/role-edit.html b/lualib/admin/html/system/role/role-edit.html index 90de8aae..628afce3 100644 --- a/lualib/admin/html/system/role/role-edit.html +++ b/lualib/admin/html/system/role/role-edit.html @@ -66,7 +66,7 @@ var {dtree, form, layer} = layui; var tree = dtree.render({ - elem: '#dataTree', dataStyle: "layuiStyle", dataFormat: "list", response: {message:"msg", statusCode:0}, // 固定参数1 + elem: '#dataTree', dataStyle: "layuiStyle", dataFormat: "list", response: {message:"msg", statusCode:0}, headers:{token:'{*token*}'},// 固定参数1 checkbar: true, checkbarLoad: "node", request: {token:"{*token*}", action:"get_veri_tree", id: "{*id*}"}, url: '{*api_url*}', // 固定参数2 initLevel: 1, // 控制折叠菜单权限列表 }) @@ -95,6 +95,7 @@ url: "{*api_url*}", type: "POST", dataType:"json", + headers:{token:'{*token*}'}, contentType: "application/json", data: JSON.stringify({ id: "{*id*}", diff --git a/lualib/admin/html/system/role/role.html b/lualib/admin/html/system/role/role.html index 8b31d651..a63f9f0d 100644 --- a/lualib/admin/html/system/role/role.html +++ b/lualib/admin/html/system/role/role.html @@ -17,7 +17,7 @@ -
                  #
                  +
                  @@ -57,20 +57,20 @@ title:"{*locale['dashboard.menu.role_manage.table.options.delete']*}", content:"{*locale['dashboard.menu.role_manage.table.options.confirm']*}", }, function(index){ - $.post('{*api_url*}', { - action: "delete", - id: data.id, - token: "{*token*}", + $.ajax({ + url: "{*api_url*}", type: "POST", headers: {token:"{*token*}"}, data: { action: "delete", id: data.id, token: "{*token*}"}, + error: function (res) { + return layer.msg('请求失败', {time:2000}); }, - function (data, status) { - if (data.code != 0) { - return layer.msg(data.msg, {time:2000}); + success: function (res) { + if (res.code != 0) { + return layer.msg(res.msg, {time:2000}); } - layer.msg(data.msg, {time:1000}, function () { + return layer.msg(res.msg, {time:1000}, function () { return window.location.reload(); - }) - } - ); + }); + }, + }) return false; }); } else if(obj.event === 'edit'){ diff --git a/lualib/admin/html/system/user/user-add.html b/lualib/admin/html/system/user/user-add.html index a7bda97e..c3bcb0ac 100644 --- a/lualib/admin/html/system/user/user-add.html +++ b/lualib/admin/html/system/user/user-add.html @@ -109,16 +109,24 @@ //监听提交 form.on('submit(add)', function(data) { $('#submit').hide(); - $.post('{*api_url*}', data.field, function (data, status) { - if (data.code != 0 ){ - return layer.msg(data.msg , {time:2000}, function () { + $.ajax({ + url: "{*api_url*}", type: "POST", headers: {token:"{*token*}"}, data: data.field, + error: function (res) { + return layer.msg('请求失败', {time:2000}, function () { $('#submit').show(); }); - }; - layer.msg('添加成功:'+data.msg, {time:1000}, function () { - xadmin.close(); - xadmin.father_reload(); - }) + }, + success: function (res) { + if (res.code != 0) { + return layer.msg(res.msg, {time:2000}, function () { + $('#submit').show(); + }); + } + return layer.msg(res.msg, {time:1000}, function () { + xadmin.close(); + xadmin.father_reload(); + }); + }, }) return false; }); diff --git a/lualib/admin/html/system/user/user-edit.html b/lualib/admin/html/system/user/user-edit.html index e424e772..0e84aaeb 100644 --- a/lualib/admin/html/system/user/user-edit.html +++ b/lualib/admin/html/system/user/user-edit.html @@ -118,18 +118,26 @@ //监听提交 form.on('submit(edit)', function(data) { $('#submit').hide(); - $.post('{*api_url*}', data.field, function (data, status) { - if (data.code != 0 ){ - return layer.msg(data.msg , {time:2000}, function () { - $('#submit').show(); - }); - }; - layer.msg(data.msg, {time:1000}, function () { - xadmin.close(); - xadmin.father_reload(); - }) - }) - return false; + $.ajax({ + url: "{*api_url*}", type: "POST", headers: {token:"{*token*}"}, data: data.field, + error: function (res) { + return layer.msg('请求失败', {time:2000}, function () { + $('#submit').show(); + }); + }, + success: function (res) { + if (res.code != 0) { + return layer.msg(res.msg, {time:2000}, function () { + $('#submit').show(); + }); + } + return layer.msg(res.msg, {time:1000}, function () { + xadmin.close(); + xadmin.father_reload(); + }); + }, + }) + return false; }); }); diff --git a/lualib/admin/html/system/user/user.html b/lualib/admin/html/system/user/user.html index c15ee403..aa501e6d 100644 --- a/lualib/admin/html/system/user/user.html +++ b/lualib/admin/html/system/user/user.html @@ -17,7 +17,7 @@ -
                  #
                  +
                  @@ -58,20 +58,34 @@ if(obj.event === 'delete'){ layer.confirm("{*locale['dashboard.menu.user_manage.table.options.delete.confirm']*}", {title:""}, function(index){ - $.post('{*api_url*}', { - action: "delete", - uid: data.id, - token: "{*token*}", + // $.post('{*api_url*}', { + // action: "delete", + // uid: data.id, + // token: "{*token*}", + // }, + // function (data, status) { + // if (data.code != 0) { + // return layer.msg(data.msg, {time:2000}); + // } + // layer.msg(data.msg, {time:1000}, function () { + // return window.location.reload(); + // }) + // } + // ); + $.ajax({ + url: "{*api_url*}", type: "POST", headers: {token:"{*token*}"}, data: { action: "delete", id: data.id, token: "{*token*}"}, + error: function (res) { + return layer.msg('请求失败', {time:2000}); }, - function (data, status) { - if (data.code != 0) { - return layer.msg(data.msg, {time:2000}); + success: function (res) { + if (res.code != 0) { + return layer.msg(res.msg, {time:2000}); } - layer.msg(data.msg, {time:1000}, function () { + return layer.msg(res.msg, {time:1000}, function () { return window.location.reload(); - }) - } - ); + }); + }, + }) return false; }); } else if(obj.event === 'edit'){ diff --git a/lualib/admin/http/system/menu.lua b/lualib/admin/http/system/menu.lua index dd9b3a02..5a732dd2 100644 --- a/lualib/admin/http/system/menu.lua +++ b/lualib/admin/http/system/menu.lua @@ -198,7 +198,7 @@ function system.menu_response (content) args.name = url_decode(args.name) args.url = url_decode(args.url) or "NULL" args.icon = url_decode(args.icon) - if menu.menu_name_exists then + if menu.menu_name_exists(db, args.name) then return json_encode({code = 401, msg = '菜单名已存在'}) end menu.menu_add(db, args) diff --git a/lualib/admin/http/system/role.lua b/lualib/admin/http/system/role.lua index fb9ace2b..48f22ba6 100644 --- a/lualib/admin/http/system/role.lua +++ b/lualib/admin/http/system/role.lua @@ -139,7 +139,7 @@ function system.role_response (content) return json_encode({code = 400, data = null, msg = '4. 用户权限不足'}) end if args.action == 'list' then - return json_encode({code = 0, data = role.role_list(db, args)}) + return json_encode({code = 0, count = role.role_count(db), data = role.role_list(db, args)}) end if args.action == 'add' then if not args.name or not args.permissions then diff --git a/lualib/admin/init.lua b/lualib/admin/init.lua index 6fab288d..e51c96ae 100644 --- a/lualib/admin/init.lua +++ b/lualib/admin/init.lua @@ -1,12 +1,18 @@ local template = require "template" +local utils = require "admin.utils" local config = require "admin.config" local locales = require "admin.locales" local admin_db = require "admin.db" +local user_token = require "admin.db.token" local login = require "admin.http.login" local system = require "admin.http.system" local profile = require "admin.http.profile" local dashboard = require "admin.http.dashboard" +local find = string.find + +local get_path = utils.get_path + local admin = {} -- 开启全局模板缓存 @@ -52,6 +58,23 @@ function admin.init_page (app, db) config.home = config.home or '/welcome.html' + -- 注册一些admin规则用于安全验证. + -- 接口路由需要进行验权, 页面路由由Cookie验权. + app:before(function (content) + local path = get_path(content) + if find(path, '^/api/admin/.+') then -- 用于所有/api接口需要传递token header进行验权. + local token = content['headers']['token'] + if not token then + return 401 + end + local exists = user_token.token_exists(db, token) + if not exists then + return 403 + end + end + return 200 + end) + -- 注册登录页相关路由 config.login_api = '/api/login' config.login_render = '/admin' @@ -63,7 +86,7 @@ function admin.init_page (app, db) app:use(config.dashboard, dashboard.render) -- 用户管理路由 - config.system_user_api = '/api/system/user' + config.system_user_api = '/api/admin/system/user' config.system_user_render = '/admin/system/user' config.system_user_add_render = '/admin/system/user/add' config.system_user_edit_render = '/admin/system/user/edit' @@ -73,7 +96,7 @@ function admin.init_page (app, db) app:use(config.system_user_edit_render, system.user_edit_render) -- 菜单管理 - config.system_menu_api = '/api/system/menu' + config.system_menu_api = '/api/admin/system/menu' config.system_menu_render = '/admin/system/menu' config.system_menu_add_render = '/admin/system/menu/add' config.system_menu_edit_render = '/admin/system/menu/edit' @@ -83,7 +106,7 @@ function admin.init_page (app, db) app:use(config.system_menu_edit_render, system.menu_edit_render) -- 导航管理 - config.system_header_api = '/api/system/header' + config.system_header_api = '/api/admin/system/header' config.system_header_render = '/admin/system/header' config.system_header_add_render = '/admin/system/header/add' config.system_header_edit_render = '/admin/system/header/edit' @@ -93,7 +116,7 @@ function admin.init_page (app, db) app:use(config.system_header_edit_render, system.header_edit_render) -- 权限管理 - config.system_role_api = '/api/system/role' + config.system_role_api = '/api/admin/system/role' config.system_role_render = '/admin/system/role' config.system_role_add_render = '/admin/system/role/add' config.system_role_edit_render = '/admin/system/role/edit' @@ -104,7 +127,7 @@ function admin.init_page (app, db) -- profile路由 - config.profile_api = '/api/profile' + config.profile_api = '/api/admin/profile' config.profile_render = '/admin/profile' app:api(config.profile_api, profile.response) app:use(config.profile_render, profile.render) diff --git a/lualib/admin/view.lua b/lualib/admin/view.lua index f7bd8dfd..e3508f27 100644 --- a/lualib/admin/view.lua +++ b/lualib/admin/view.lua @@ -9,6 +9,7 @@ local permission = require "admin.db.permission" local type = type +local pcall = pcall local assert = assert local get_path = utils.get_path @@ -29,41 +30,39 @@ end local view = {} -- 页面路由 -function view.use (path, cls) +function view.use (path, f) assert(type(path) == 'string', 'view use path failed.') - assert(type(cls) == 'function' or type(cls) == 'table', 'view use path failed.') + assert(type(f) == 'function', 'view use handle failed.') local db, app = config.db, config.app return app:use(path, function (content) local ok, url = verify_permission(content, db) if not ok then return utils.redirect(url) end - local ok, html - if type(cls) == 'function' then - ok, html = pcall(cls, {ctx = httpctx:new{content = content, db = db}}) - else - local c = cls:new({ctx = httpctx:new{content = content, db = db}}) - ok, html = pcall(c, c, content['method']:lower()) - end + local ok, html = pcall(f, httpctx:new{content = content}, db) return html end) end -- 接口路由 -function view.api (path, cls) +function view.api (path, f) assert(type(path) == 'string', 'view api path failed.') - assert(type(cls) == 'function' or type(cls) == 'table', 'view api path failed.') + assert(type(f) == 'function', 'view api handle failed.') local db, app = config.db, config.app return app:api(path, function (content) - local ok, res - if type(cls) == 'function' then - ok, res = pcall(cls, {ctx = httpctx:new{content = content, db = db}}) - else - local c = cls:new({ctx = httpctx:new{content = content, db = db}}) - ok, res = pcall(c, c, content['method']:lower()) - end + local ok, res = pcall(f, httpctx:new{content = content}, db) return res end) end +-- 获取当前用户语言表 +function view.get_locale () + return utils.get_locale(Cookie.getCookie("CFLANG")) +end + +-- 获取静态文件前缀 +function view.get_cdn () + return config.cdn +end + return view diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index 11e0d2b1..69f9c558 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -454,6 +454,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) }, CRLF)..CRLF2) return sock:close() end + content['ROUTE'] = HTTP_PROTOCOL[typ] content['method'], content['path'], content['headers'] = METHOD, PATH, HEADER -- before 函数只影响接口与view if before_func and (typ == HTTP_PROTOCOL.API or typ == HTTP_PROTOCOL.USE) then From a4e23c3f42f607599f0d7ce52953c0f90b4ed931 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 23 May 2019 18:04:01 +0800 Subject: [PATCH 052/956] =?UTF-8?q?=E8=B0=83=E6=95=B4test=5Fadmin=E7=A4=BA?= =?UTF-8?q?=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_cfadmin.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/script/test_cfadmin.lua b/script/test_cfadmin.lua index 692c698e..24aa4add 100644 --- a/script/test_cfadmin.lua +++ b/script/test_cfadmin.lua @@ -1,5 +1,4 @@ local httpd = require "httpd" -local http = require "httpd.http" local DB = require "DB" --[[ @@ -16,6 +15,7 @@ local db = DB:new({ password = '123456789', charset = 'utf8', database = 'cfadmin', + max = 100, }) db:connect() @@ -48,11 +48,12 @@ local view = require "admin.view" -- 1. ctx是一个http req 对象, 目前内置包括: get_method, get_args, get_path, get_raw_path, get_headers, get_cookie -- 2. db初始化后的db对象, 方便用户直接使用. -view.use('/test1', function (ctx, db) +view.use('/admin/test1', function (ctx, db) + print(view.get_locale(), view.get_cdn()) return "hello world" end) -view.api('/test2', function (ctx, db) +view.api('/api/admin/test2', function (ctx, db) return '{"code":0,"msg":"hello world"}' end) @@ -76,7 +77,7 @@ end) -- 这个方法可以用来设置静态文件域名与前缀. -- 如果静态文件在其它域名或者无法访问, 可以使用这个参数修改.(域名后必须加上'/') --- cfadmin.static('http://10.0.0.16:8080/') +-- cfadmin.static('/') -- 设置cfadmin的区域语言, 默认为: ZH-CN -- cfadmin.set_locale('EN-US') From 7cb0f294f0d82b038c0872fa445a6973f06bcd9b Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 23 May 2019 23:20:15 +0800 Subject: [PATCH 053/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dcookie=E5=A4=A7?= =?UTF-8?q?=E5=B0=8F=E5=86=99=E5=AF=BC=E8=87=B4=E7=9A=84=E5=BC=95=E7=94=A8?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/http/system/header.lua | 2 +- lualib/admin/http/system/menu.lua | 2 +- lualib/admin/http/system/role.lua | 2 +- lualib/admin/http/system/user.lua | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lualib/admin/http/system/header.lua b/lualib/admin/http/system/header.lua index 1443a623..05c63692 100644 --- a/lualib/admin/http/system/header.lua +++ b/lualib/admin/http/system/header.lua @@ -5,7 +5,7 @@ local user = require "admin.db.user" local user_token = require "admin.db.token" local role = require "admin.db.role" local header = require "admin.db.header" -local Cookie = require "admin.Cookie" +local Cookie = require "admin.cookie" local json = require "json" local json_encode = json.encode diff --git a/lualib/admin/http/system/menu.lua b/lualib/admin/http/system/menu.lua index 5a732dd2..f08050fc 100644 --- a/lualib/admin/http/system/menu.lua +++ b/lualib/admin/http/system/menu.lua @@ -6,7 +6,7 @@ local user_token = require "admin.db.token" local menu = require "admin.db.menu" local role = require "admin.db.role" local view = require "admin.db.view" -local Cookie = require "admin.Cookie" +local Cookie = require "admin.cookie" local json = require "json" local json_encode = json.encode diff --git a/lualib/admin/http/system/role.lua b/lualib/admin/http/system/role.lua index 48f22ba6..14c36239 100644 --- a/lualib/admin/http/system/role.lua +++ b/lualib/admin/http/system/role.lua @@ -5,7 +5,7 @@ local user = require "admin.db.user" local user_token = require "admin.db.token" local menu = require "admin.db.menu" local role = require "admin.db.role" -local Cookie = require "admin.Cookie" +local Cookie = require "admin.cookie" local json = require "json" local json_encode = json.encode diff --git a/lualib/admin/http/system/user.lua b/lualib/admin/http/system/user.lua index b4991786..9578b1fa 100644 --- a/lualib/admin/http/system/user.lua +++ b/lualib/admin/http/system/user.lua @@ -4,7 +4,7 @@ local utils = require "admin.utils" local user = require "admin.db.user" local user_token = require "admin.db.token" local role = require "admin.db.role" -local Cookie = require "admin.Cookie" +local Cookie = require "admin.cookie" local crypt = require "crypt" local json = require "json" From 1bc7a78fff6b2d935c127281ed447487872ec11e Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 23 May 2019 23:53:28 +0800 Subject: [PATCH 054/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=80=E4=BA=9Bbef?= =?UTF-8?q?ore=E7=9A=84=E4=B8=80=E6=AC=A1=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/init.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lualib/admin/init.lua b/lualib/admin/init.lua index e51c96ae..f7f99136 100644 --- a/lualib/admin/init.lua +++ b/lualib/admin/init.lua @@ -1,4 +1,5 @@ local template = require "template" +local http = require "httpd.http" local utils = require "admin.utils" local config = require "admin.config" local locales = require "admin.locales" @@ -65,14 +66,14 @@ function admin.init_page (app, db) if find(path, '^/api/admin/.+') then -- 用于所有/api接口需要传递token header进行验权. local token = content['headers']['token'] if not token then - return 401 + return http.throw(401, "Can't Find Token") end local exists = user_token.token_exists(db, token) if not exists then - return 403 + return http.throw(403, "Token Was not Exists") end end - return 200 + return http.ok() end) -- 注册登录页相关路由 From 53fa6d55b1f181e5fd09c0c9f18a8e3541ba45d5 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 24 May 2019 00:26:25 +0800 Subject: [PATCH 055/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A01=E5=88=86=E9=92=9F?= =?UTF-8?q?=E5=BF=AB=E9=80=9F=E4=BD=93=E9=AA=8Ccf-admin=E5=90=8E=E5=8F=B0W?= =?UTF-8?q?iki=E6=95=99=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 697a3dc2..f2be6a9e 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,8 @@

                  +> [如何快速体验cfadmin后台?](https://github.com/CandyMi/core_framework/wiki/cfadmin) + ## 联系作者 > [issues](https://github.com/CandyMi/core_framework/issues) From 9d7226cbdd8e33c717c9a1fe5c9c9bee63a7f105 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 24 May 2019 00:49:58 +0800 Subject: [PATCH 056/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0main.lua?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/main.lua | 126 +++++++++++++++++++++++++++--------------------- 1 file changed, 71 insertions(+), 55 deletions(-) diff --git a/script/main.lua b/script/main.lua index 5499cafd..a991ee97 100644 --- a/script/main.lua +++ b/script/main.lua @@ -1,76 +1,92 @@ local httpd = require "httpd" -local http = require "httpd.http" -local httpc = require "httpc" - - +local DB = require "DB" + +--[[ +请按照以下步奏初始化后台: + 1. 创建一个数据库(名字任意); + 2. 请手动打开lualib/db/database.sql文件, 复制里面的SQL语句在GUI工具中执行一次; + 3. 执行完成之后, 将您填写的数据库替换database字段, 并且charset需要设置一致. +]] + +local db = DB:new({ + host = '10.0.0.16', + port = 3306, + username = 'root', + password = '123456789', + charset = 'utf8', + database = 'cfadmin', + max = 100, +}) + +db:connect() + +-- 导入httpd对象 local app = httpd:new("App") +-- httpd启用Cookie扩展 +app:enable_cookie() +-- httpd设置Cookie加密的密匙 +app:cookie_secure("https://github.com/CandyMi/core_framework") +-- app:cookie_secure("candymi") +-- 导入cf内置的admin库 +local cfadmin = require "admin" --- 每次http请求在处理函数之前将调用此方法, 第三方中间件可以在此针对回调函数设计. --- 需要注意的是: 如果路由不存在, 则不会经过此回调! --- 以下为使用示例: --- 如果未使用before回调进行中间件设计或进行header验证, 请不要注册before回调 --- 只要注册了before回调, 即使不做任何操作(返回非法值(nil))也会返回http 401 code. -app:before(function (content) - if true then - return http.ok() - end - -- if true then - -- return http.redirect('https://github.com/CandyMi/core_framework') - -- end - -- if true then - -- return http.throw(431, '

                  This is 413 Error, too long request header

                  ') - -- end -end) - --- 单个连接最大保持时间 -app:timeout(5) +-- 注册后台页面路由 +cfadmin.init_page(app, db) --- 最大URI长度 -app:max_path_size(1024) --- 最大Header长度 -app:max_header_size(65535) +-- 这个函数仅在第一次初始化数据的时候适用 +-- 初始化完成之后, 请不要再运行. +cfadmin.init_db() --- 最大Body长度 -app:max_body_size(1024 * 1024) +-- 这里设置首页的显示的页面 +-- cfadmin.init_home(location or domain + path) +-- cfadmin.init_home('https://www.baidu.com') --- 可自定义Server Name -app:server_name('Candy Server/1.0') +local view = require "admin.view" +-- 参数: +-- 1. ctx是一个http req 对象, 目前内置包括: get_method, get_args, get_path, get_raw_path, get_headers, get_cookie +-- 2. db初始化后的db对象, 方便用户直接使用. --- 注册接口 -app:api("/api", require "api") - -app:api("/app", function (opt) - local code, resp = httpc.get('http://t.weather.sojson.com/api/weather/city/101030100') - if code ~= 200 then - print(code, resp) - return '{"code":500,"message":"请求失败."}' - end - return resp +view.use('/admin/test1', function (ctx, db) + print(view.get_locale(), view.get_cdn()) + return "hello world" end) --- 注册普通路由(html/text) -app:use("/view", function (opt) - return "

                  This is text/html content-type

                  Server: cf/0.1" +view.api('/api/admin/test2', function (ctx, db) + return '{"code":0,"msg":"hello world"}' end) --- 批量路由注册 -app:group(app.API, '/admin', require "admin") +-- 这里是设置语言的地方 +-- 语言表在admin/locales内, 可参照key -> value进行填写. +-- 传入一个数组表: 索引1是key, 索引2为显示内容. + +-- cfadmin.add_locale_item('ZH-CN', { +-- {'login.form.title', '这是登录页Title'}, +-- {'dashboard.header.logo', '仪表盘 Logo'} +-- }) + +-- cfadmin.add_locale_item('EN-US', { +-- {'login.form.title', 'This is Login Page Title'}, +-- {'dashboard.header.logo', 'dashboard Logo'} +-- }) --- 批量路由注册 -app:group(app.USE, '/login', require "admin") +-- 开启页面缓存能显著提升页面渲染性能. 生产环境下建议开启. +-- 也因为cf缓存模板页面内容, 所以开发模式下不建议开启. +-- cfadmin.cached() --- 注册websocket路由 -app:ws("/ws", require "ws") +-- 这个方法可以用来设置静态文件域名与前缀. +-- 如果静态文件在其它域名或者无法访问, 可以使用这个参数修改.(域名后必须加上'/') +-- cfadmin.static('/') --- 注册静态文件目录 -app:static('static', 10) +-- 设置cfadmin的区域语言, 默认为: ZH-CN +-- cfadmin.set_locale('EN-US') --- 需要记录日志, 并且指定日志存放路径 --- app:log("./http.log") +-- 设置客户端静态文件ttl值内无需再次请求, 减少服务端消耗 +-- app:static('static', 30) +app:static('static') --- http监听端口 +-- httpd监听端口 app:listen("0.0.0.0", 8080) -- 运行 From ff314fffc91a80b22c5cead868c0885b63b9ffb1 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 24 May 2019 02:57:05 +0800 Subject: [PATCH 057/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0DB=E4=B8=8ECache,=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=B8=80=E4=BA=9B=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/Cache/init.lua | 6 ++++-- lualib/DB/init.lua | 13 ++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lualib/Cache/init.lua b/lualib/Cache/init.lua index 4506d272..4fdad1c1 100644 --- a/lualib/Cache/init.lua +++ b/lualib/Cache/init.lua @@ -55,9 +55,11 @@ local function CREATE_CACHE(opt) rds:close() timer.sleep(3) end - local ok, ret = rds:cmd("CONFIG", "GET", "TIMEOUT") - if not INITIALIZATION and ret[2] ~= '0' then + if not INITIALIZATION then + local ok, ret = rds:cmd("CONFIG", "GET", "TIMEOUT") + if ret[2] ~= '0' then rds:cmd("CONFIG SET", "TIMEOUT", "0") + end end return rds end diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index c92e9cb4..c57b4d7f 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -64,7 +64,9 @@ local GROUPBY = "GROUP BY" local COMMA = ", " -- 空闲连接时间 -local WAIT_TIMEOUT = 31104000 +local WAIT_TIMEOUT = 31536000 + +local WAIT_TIMEOUT_NOT_SET = true -- 数据库连接创建函数 local function DB_CREATE (opt) @@ -81,10 +83,11 @@ local function DB_CREATE (opt) times = times + 1 timer.sleep(3) end - db:query(fmt('SET wait_timeout=%s', tostring(WAIT_TIMEOUT))) - db:query(fmt('SET interactive_timeout=%s', tostring(WAIT_TIMEOUT))) - db:query(fmt('SET wait_timeout=%s', tostring(WAIT_TIMEOUT))) - -- Log:DEBUG(db:query("show session variables where variable_name ='wait_timeout' or variable_name = 'interactive_timeout'")) + if WAIT_TIMEOUT_NOT_SET then -- 设置连接超时时间 + WAIT_TIMEOUT_NOT_SET = false + db:query(fmt('SET GLOBAL wait_timeout=%s', WAIT_TIMEOUT)) + db:query(fmt('SET GLOBAL interactive_timeout=%s', WAIT_TIMEOUT)) + end return db end From 32562c49441c075c2731a313a0cea95414e75d4c Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 24 May 2019 05:33:18 +0800 Subject: [PATCH 058/956] =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E4=B8=8E=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 73 ++++++++++++++++++++++++++++++++++++++-------- lualib/DB/init.lua | 16 +++++----- 2 files changed, 69 insertions(+), 20 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 41cc62b1..cf19eb6c 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -5,26 +5,75 @@ #include #include "../../src/core.h" +#define SERVER 0 +#define CLIENT 1 + static inline -void SETSOCKETOPT(int sockfd){ +void SETSOCKETOPT(int sockfd, int mode){ int Enable = 1; + int ret = 0; + /* 设置非阻塞 */ non_blocking(sockfd); /* 地址/端口重用 */ - setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &Enable, sizeof(Enable)); + ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &Enable, sizeof(Enable)); + if (ret){ + LOG("ERROR", "SO_REUSEADDR 设置失败."); + return exit(-1); + } /* 关闭小包延迟合并算法 */ - setsockopt(sockfd, SOL_SOCKET, TCP_NODELAY, &Enable, sizeof(Enable)); - - /* TCP Fast Open*/ - /* - cfadmin Sever一般隐藏在代理层之后. . - setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN, &Enable, sizeof(Enable)); - */ - + ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &Enable, sizeof(Enable)); + if (ret){ + LOG("ERROR", "TCP_NODELAY 设置失败."); + return exit(-1); + } + + /* 开启 TCP keepalive */ + ret = setsockopt(sockfd, IPPROTO_TCP, SO_KEEPALIVE, &Enable , sizeof(Enable)); + if (ret){ + LOG("ERROR", "SO_KEEPALIVE 设置失败."); + return exit(-1); + } + +#if defined(linux) + if (!mode) { + /* 开启延迟Accept, 没数据来之前不回调accept */ + ret = setsockopt(sockfd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &Enable, sizeof(Enable)); + if (ret){ + LOG("ERROR", "TCP_DEFER_ACCEPT 设置失败."); + return exit(-1); + } + } + + /* 设置 TCP keepalive 空闲时间 */ + int keepidle = 30; + ret = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle , sizeof(keepidle)); + if (ret){ + LOG("ERROR", "TCP_KEEPIDLE 设置失败."); + return exit(-1); + } + +#endif + + /* 设置 TCP keepalive 探测总次数 */ + int keepcount = 3; + ret = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, &keepcount , sizeof(keepcount)); + if (ret){ + LOG("ERROR", "TCP_KEEPCNT 设置失败."); + return exit(-1); + } + + /* 设置 TCP keepalive 每次探测间隔时间 */ + int keepinterval = 5; + ret = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, &keepinterval , sizeof(keepinterval)); + if (ret){ + LOG("ERROR", "TCP_KEEPINTVL 设置失败."); + return exit(-1); + } } @@ -37,7 +86,7 @@ create_server_fd(int port, int backlog){ if (0 >= sockfd) return -1; /* socket option set */ - SETSOCKETOPT(sockfd); + SETSOCKETOPT(sockfd, SERVER); struct sockaddr_in6 SA; SA.sin6_family = AF_INET6; @@ -66,7 +115,7 @@ create_client_fd(const char *ipaddr, int port){ if (0 >= sockfd) return -1; /* socket option set */ - SETSOCKETOPT(sockfd); + SETSOCKETOPT(sockfd, CLIENT); struct sockaddr_in6 SA; SA.sin6_family = AF_INET6; diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index c57b4d7f..38462379 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -453,14 +453,14 @@ function DB:query(query) while 1 do db = pop_db(self) if db then - ret, err = db:query(query) - if db.state then - break - end - Log:ERROR(err) - db:close() - self.current = self.current - 1 - db, ret, err = nil, nil, nil + ret, err = db:query(query) + if db.state then + break + end + -- Log:ERROR(err) + db:close() + self.current = self.current - 1 + db, ret, err = nil, nil, nil end end local co = pop_wait(self) From 03fd32b88357d0609a8e22d4f63cce418936eb68 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 24 May 2019 06:06:04 +0800 Subject: [PATCH 059/956] =?UTF-8?q?=E5=8E=BB=E6=8E=89=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E4=BF=A1=E5=8F=B7=E6=8D=95=E8=8E=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/core.c b/src/core.c index 89211fa8..830e3f89 100644 --- a/src/core.c +++ b/src/core.c @@ -168,21 +168,20 @@ init_lua_libs(lua_State *L){ lua_settop(L, 0); /* 注入lua搜索域 */ - lua_getglobal(L, "package"); + lua_getglobal(L, "package"); - lua_pushliteral(L, "lualib/?.lua;lualib/?/init.lua;script/?.lua;script/?/init.lua;"); - lua_setfield(L, 1, "path"); + lua_pushliteral(L, "lualib/?.lua;lualib/?/init.lua;script/?.lua;script/?/init.lua;"); + lua_setfield(L, 1, "path"); - lua_pushliteral(L, "luaclib/?.so;luaclib/?/init.so;"); - lua_setfield(L, 1, "cpath"); + lua_pushliteral(L, "luaclib/?.so;luaclib/?/init.so;"); + lua_setfield(L, 1, "cpath"); - lua_settop(L, 0); + lua_settop(L, 0); } static lua_State *L; core_signal sighup; core_signal sigpipe; -core_signal sigquit; core_signal sigtstp; void @@ -196,10 +195,6 @@ signal_init(){ ev_signal_init(&sigpipe, SIG_CB, SIGPIPE); ev_signal_start(CORE_LOOP_ &sigpipe); - /* 忽略Ctrl-/操作信号 */ - ev_signal_init(&sigquit, SIG_CB, SIGQUIT); - ev_signal_start(CORE_LOOP_ &sigquit); - /* 忽略Ctrl-Z操作信号 */ ev_signal_init(&sigtstp, SIG_CB, SIGTSTP); ev_signal_start(CORE_LOOP_ &sigtstp); From 31c8fe9074cb08a040d8a6614489b493e089cc0f Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 24 May 2019 06:24:50 +0800 Subject: [PATCH 060/956] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E9=80=A0=E6=88=90=E7=9A=84Warning?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Makefile b/src/Makefile index 906dca5e..e804a6cf 100644 --- a/src/Makefile +++ b/src/Makefile @@ -13,17 +13,17 @@ LIBS += -L/usr/local/lib INCLUDES += -I/usr/local/include # 使用jemalloc内存分配器请启用这段 -# CFLAGS += -Wall -Os -fPIC --shared -DJEMALLOC +# CFLAGS += -Wall -Os -fPIC --shared -DJEMALLOC -fno-strict-aliasing # DLL += -ljemalloc -lev -llua # MACRO += -w -Os -L./ -L../ -DJEMALLOC # 使用tcmalloc内存分配器请启用这段 -# CFLAGS += -Wall -Os -fPIC --shared -DTCMALLOC +# CFLAGS += -Wall -Os -fPIC --shared -DTCMALLOC -fno-strict-aliasing # DLL += -ltcmalloc -lev -llua # MACRO += -w -Os -L./ -L../ -DTCMALLOC # 默认情况下使用系统内存分配器 -CFLAGS += -Wall -Os -fPIC --shared +CFLAGS += -Wall -Os -fPIC --shared -fno-strict-aliasing DLL += -lev -llua MACRO += -w -Os From 6f7ba59a686aa5eb0d79421f75cbf4fd7d6ae68b Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 24 May 2019 08:50:31 +0800 Subject: [PATCH 061/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=87=A0=E4=B8=AA?= =?UTF-8?q?=E4=BF=A1=E5=8F=B7=E5=A4=84=E7=90=86=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 72 +++++++++++++++++++++++++++++++++++++++++++++----- src/core_sys.h | 2 +- 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/src/core.c b/src/core.c index 830e3f89..a557298b 100644 --- a/src/core.c +++ b/src/core.c @@ -110,12 +110,56 @@ core_start(core_loop *loop, int mode){ } -static void -SIG_CB(core_loop *loop, core_signal *signal, int revents){ - // LOG("WARN", "RECV SIGNAL..."); + +const char *signame[]= { + "INVALID", + "SIGHUP", + "SIGINT", + "SIGQUIT", + "SIGILL", + "SIGTRAP", + "SIGABRT", + "SIGBUS", + "SIGFPE", + "SIGKILL", + "SIGUSR1", + "SIGSEGV", + "SIGUSR2", + "SIGPIPE", + "SIGALRM", + "SIGTERM", + "SIGSTKFLT", + "SIGCHLD", + "SIGCONT", + "SIGSTOP", + "SIGTSTP", + "SIGTTIN", + "SIGTTOU", + "SIGURG", + "SIGXCPU", + "SIGXFSZ", + "SIGVTALRM", + "SIGPROF", + "SIGWINCH", + "SIGPOLL", + "SIGPWR", + "SIGSYS", + NULL +}; + +#define signum_to_string(number) (signame[number]) + +static void // 忽略信号 +SIG_IGNORE(core_loop *loop, core_signal *signal, int revents){ return ; } +static void // 退出信号 +SIG_EXIT(core_loop *loop, core_signal *signal, int revents){ + LOG("ERROR", signum_to_string(signal->signum)); + return exit(-1); +} + static void ERROR_CB(const char *msg){ LOG("ERROR", msg); @@ -184,21 +228,37 @@ core_signal sighup; core_signal sigpipe; core_signal sigtstp; +core_signal sigint; +core_signal sigterm; +core_signal sigquit; + void signal_init(){ /* 忽略父进程退出的信号 */ - ev_signal_init(&sighup, SIG_CB, SIGHUP); + ev_signal_init(&sighup, SIG_IGNORE, SIGHUP); ev_signal_start(CORE_LOOP_ &sighup); /* 忽略管道信号 */ - ev_signal_init(&sigpipe, SIG_CB, SIGPIPE); + ev_signal_init(&sigpipe, SIG_IGNORE, SIGPIPE); ev_signal_start(CORE_LOOP_ &sigpipe); /* 忽略Ctrl-Z操作信号 */ - ev_signal_init(&sigtstp, SIG_CB, SIGTSTP); + ev_signal_init(&sigtstp, SIG_IGNORE, SIGTSTP); ev_signal_start(CORE_LOOP_ &sigtstp); + /* TERM信号 显示退出 */ + ev_signal_init(&sigterm, SIG_EXIT, SIGTERM); + ev_signal_start(CORE_LOOP_ &sigterm); + + /* INT信号 显示退出 */ + ev_signal_init(&sigint, SIG_EXIT, SIGINT); + ev_signal_start(CORE_LOOP_ &sigint); + + /* QUIT信号 显示退出 */ + ev_signal_init(&sigquit, SIG_EXIT, SIGQUIT); + ev_signal_start(CORE_LOOP_ &sigquit); + } void diff --git a/src/core_sys.h b/src/core_sys.h index 3521d3af..49096435 100644 --- a/src/core_sys.h +++ b/src/core_sys.h @@ -52,4 +52,4 @@ int ipv4(const char *IP); /* 检查是否为有效ipv6地址 */ int ipv6(const char *IP); -#endif \ No newline at end of file +#endif From 001f2ec983192ac4bdd7cc67d77b1313138a5c46 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 24 May 2019 09:03:06 +0800 Subject: [PATCH 062/956] =?UTF-8?q?=E5=8F=96=E6=B6=88=E6=89=93=E5=8D=B0?= =?UTF-8?q?=E6=97=A0=E7=94=A8=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core.c b/src/core.c index a557298b..7bb5fc53 100644 --- a/src/core.c +++ b/src/core.c @@ -110,7 +110,6 @@ core_start(core_loop *loop, int mode){ } - const char *signame[]= { "INVALID", "SIGHUP", @@ -156,7 +155,7 @@ SIG_IGNORE(core_loop *loop, core_signal *signal, int revents){ static void // 退出信号 SIG_EXIT(core_loop *loop, core_signal *signal, int revents){ - LOG("ERROR", signum_to_string(signal->signum)); + // LOG("ERROR", signum_to_string(signal->signum)); return exit(-1); } @@ -224,10 +223,13 @@ init_lua_libs(lua_State *L){ } static lua_State *L; + +/* 注册需要忽略的信号 */ core_signal sighup; core_signal sigpipe; core_signal sigtstp; +/* 注册需要退出的信号(docker需要) */ core_signal sigint; core_signal sigterm; core_signal sigquit; From aeda93fd3183711c4ea51c31926d914f88a1b690 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 24 May 2019 09:04:12 +0800 Subject: [PATCH 063/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dcfadmin=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E5=9C=A8=E5=AE=B9=E5=99=A8=E5=86=85=E5=93=8D=E5=BA=94?= =?UTF-8?q?ctrl+c=E3=80=81ctrl+\=E7=AD=89=E9=80=80=E5=87=BA=E4=BF=A1?= =?UTF-8?q?=E5=8F=B7=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 90dfe8be..5ee3c335 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -20,4 +20,4 @@ RUN yum groupinstall "development tools" -y && yum install autoconf git readline # 使用者可在启动容器时使用-v命令将您的代码目录直接挂载到/app/script目录进行调试操作 WORKDIR /app -CMD ["./cfadmin"] \ No newline at end of file +ENTRYPOINT ["./cfadmin"] From 6ef2fcfbed8681a074d98f19b39e48bb8f282529 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 24 May 2019 10:13:50 +0800 Subject: [PATCH 064/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AADB?= =?UTF-8?q?=E4=B8=8ECache=E7=9A=84=E8=BF=9E=E6=8E=A5=E6=B1=A0=E8=A1=8C?= =?UTF-8?q?=E4=B8=BA,=E8=B0=83=E6=95=B4test=5FDB.lua=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/Cache/init.lua | 13 +++++++------ lualib/DB/init.lua | 14 +++++++++----- script/test_DB.lua | 2 ++ 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/lualib/Cache/init.lua b/lualib/Cache/init.lua index 4fdad1c1..130c9b03 100644 --- a/lualib/Cache/init.lua +++ b/lualib/Cache/init.lua @@ -12,6 +12,7 @@ local ipairs = ipairs local setmetatable = setmetatable local table = table +local insert = table.insert local remove = table.remove local upper = string.upper local lower = string.lower @@ -66,12 +67,7 @@ end -- 加入到连接池内 local function add_cache(self, cache) - self.cache_pool[#self.cache_pool+1] = cache -end - --- 加入到协程池内 -local function add_wait(self, co) - self.co_pool[#self.co_pool+1] = co + insert(self.cache_pool, 1, cache) end -- 从连接池内取出一个cache对象 @@ -87,6 +83,11 @@ local function pop_cache(self) return co_wait() end +-- 加入到协程池内 +local function add_wait(self, co) + insert(self.co_pool, 1, co) +end + -- 弹出一个等待协程 local function pop_wait(self) return remove(self.co_pool) diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index 38462379..8cc6d50c 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -92,7 +92,7 @@ local function DB_CREATE (opt) end local function add_wait(self, co) - self.co_pool[#self.co_pool + 1] = co + insert(self.co_pool, 1, co) end local function pop_wait(self) @@ -100,7 +100,7 @@ local function pop_wait(self) end local function add_db(self, db) - self.db_pool[#self.db_pool + 1] = db + insert(self.db_pool, 1, db) end -- 负责创建连接/加入等待队列 @@ -121,7 +121,11 @@ end -- 将['field', '=', 'a'] 格式化为 field = a -- 将['field', 'NOT', 'IN', '(1, 2, 3)'] 格式化为 field NOT IN (1, 2, 3) local function format(t) - return fmt(rep("'%s' ", #t), unpack(t)) + local tab = {} + for index=1, #t, 1 do + tab[#tab+1] = "%s" + end + return fmt(concat(tab, ' '), unpack(t)) end local function format_value1(t) @@ -223,7 +227,7 @@ local function where(query, conditions) insert(CONDITIONS, format({condition[1], con2, LEFT..format_value2(condition[3], c)..RIGHT})) elseif con2 == IS then insert(CONDITIONS, concat(condition, " ")) - elseif find(condition[3], condition[1]) or find(condition[3], '`.') then + elseif find(condition[3], condition[1]) or find(condition[3], '`') then insert(CONDITIONS, concat(condition, " ")) else insert(CONDITIONS, format_value3(condition)) @@ -448,7 +452,7 @@ function DB:query(query) return nil, "DB尚未初始化" end assert(type(query) == 'string' and query ~= '' , "原始SQL类型错误(query):"..tostring(query)) - -- Log:DEBUG(query) + Log:DEBUG(query) local db, ret, err while 1 do db = pop_db(self) diff --git a/script/test_DB.lua b/script/test_DB.lua index a011acfa..bd2295a3 100644 --- a/script/test_DB.lua +++ b/script/test_DB.lua @@ -83,6 +83,8 @@ cf.fork(function ( ... ) {"user", "BETWEEN", {1, 100}}, "AND", {"user", "NOT", "BETWEEN", {1, 100}}, + "AND", + {"`user`.id", "=", '`user`.id'}, }) :groupby('id') -- groupby({"name", "user"}) :orderby("id") -- orderby({"name", "user"}) From 58235fb2b8a6a76d22d6ebc25565d17e62093d1f Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 24 May 2019 10:15:52 +0800 Subject: [PATCH 065/956] =?UTF-8?q?=E6=B3=A8=E9=87=8ADB=E7=9A=84DEBUG=20Lo?= =?UTF-8?q?g?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/DB/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index 8cc6d50c..aedfac98 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -452,7 +452,7 @@ function DB:query(query) return nil, "DB尚未初始化" end assert(type(query) == 'string' and query ~= '' , "原始SQL类型错误(query):"..tostring(query)) - Log:DEBUG(query) + -- Log:DEBUG(query) local db, ret, err while 1 do db = pop_db(self) From 2db5c2e216ff4b818399ce8740628a9661c3c93a Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 24 May 2019 11:28:25 +0800 Subject: [PATCH 066/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dconfig.github?= =?UTF-8?q?=E6=97=A0=E6=95=88=E7=9A=84=E9=97=AE=E9=A2=98,=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9Eview.template=E6=B8=B2=E6=9F=93=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=E6=96=B9=E6=B3=95hook,=20=E5=88=A0=E9=99=A4=E6=97=A0=E7=94=A8?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=A4=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/config.lua | 2 +- lualib/admin/html/view/base.html | 0 lualib/admin/view.lua | 2 ++ 3 files changed, 3 insertions(+), 1 deletion(-) delete mode 100644 lualib/admin/html/view/base.html diff --git a/lualib/admin/config.lua b/lualib/admin/config.lua index a328f266..60ba26a5 100644 --- a/lualib/admin/config.lua +++ b/lualib/admin/config.lua @@ -1,6 +1,6 @@ local config = { cdn = '/', -- 静态文件前缀地址 - -- github = 'https://github.com/candymi/core_framework', -- 跳转地址 + github = 'https://github.com/candymi/core_framework', -- 跳转地址 cache = false, -- 是否缓存模板 locale = "ZH-CN", -- 当前语言 locales = require "admin.locales", -- 语言表 diff --git a/lualib/admin/html/view/base.html b/lualib/admin/html/view/base.html deleted file mode 100644 index e69de29b..00000000 diff --git a/lualib/admin/view.lua b/lualib/admin/view.lua index e3508f27..0ed30141 100644 --- a/lualib/admin/view.lua +++ b/lualib/admin/view.lua @@ -65,4 +65,6 @@ function view.get_cdn () return config.cdn end +view.template = template + return view From 694f93f50df7e1a658381b2ba6ccc48a01d6a469 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 24 May 2019 11:39:07 +0800 Subject: [PATCH 067/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9view.template?= =?UTF-8?q?=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/view.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/admin/view.lua b/lualib/admin/view.lua index 0ed30141..fd78aae5 100644 --- a/lualib/admin/view.lua +++ b/lualib/admin/view.lua @@ -65,6 +65,6 @@ function view.get_cdn () return config.cdn end -view.template = template +view.template = template.compile return view From 74f18b211c4a7532a1be1e10322b2c3b6d7ef073 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 24 May 2019 23:04:43 +0800 Subject: [PATCH 068/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8DCache=E7=9A=84?= =?UTF-8?q?=E5=88=A4=E6=96=AD=E5=88=9D=E5=A7=8B=E5=8C=96=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E8=A1=A8=E8=BE=BE=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/Cache/init.lua | 2 +- script/main.lua | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lualib/Cache/init.lua b/lualib/Cache/init.lua index 130c9b03..b3f4e47c 100644 --- a/lualib/Cache/init.lua +++ b/lualib/Cache/init.lua @@ -56,7 +56,7 @@ local function CREATE_CACHE(opt) rds:close() timer.sleep(3) end - if not INITIALIZATION then + if not opt.INITIALIZATION then local ok, ret = rds:cmd("CONFIG", "GET", "TIMEOUT") if ret[2] ~= '0' then rds:cmd("CONFIG SET", "TIMEOUT", "0") diff --git a/script/main.lua b/script/main.lua index a991ee97..242affbf 100644 --- a/script/main.lua +++ b/script/main.lua @@ -49,7 +49,6 @@ local view = require "admin.view" -- 2. db初始化后的db对象, 方便用户直接使用. view.use('/admin/test1', function (ctx, db) - print(view.get_locale(), view.get_cdn()) return "hello world" end) From 25acb60708fcfc5cbc0781f308670fa9df97c6bb Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 25 May 2019 04:02:29 +0800 Subject: [PATCH 069/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0Cache(redis)=E7=9A=84?= =?UTF-8?q?pipeline=E5=91=BD=E4=BB=A4=E6=94=AF=E6=8C=81,=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0test=5FCache.lua=E4=BD=BF=E7=94=A8=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/Cache/init.lua | 2 +- lualib/protocol/redis.lua | 22 ++++++++++++++++++++++ script/test_Cache.lua | 8 ++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/lualib/Cache/init.lua b/lualib/Cache/init.lua index b3f4e47c..b5f3cfa3 100644 --- a/lualib/Cache/init.lua +++ b/lualib/Cache/init.lua @@ -22,7 +22,7 @@ local Log = log:new({ dump = true, path = 'Cache'}) -- 注册命令 local commands = { - 'sismember', 'exists' + 'sismember', 'exists', 'pipeline' } local function in_command(cmd) diff --git a/lualib/protocol/redis.lua b/lualib/protocol/redis.lua index 17d2710f..78630035 100644 --- a/lualib/protocol/redis.lua +++ b/lualib/protocol/redis.lua @@ -4,6 +4,7 @@ local Co = require "internal.Co" local tcp = require "internal.TCP" local concat = table.concat +local unpack = table.unpack local Log = log:new({ dump = true, path = 'protocol-redis' }) local co_spwan = Co.spwan @@ -204,6 +205,27 @@ function redis:cmd(...) return read_response(sock) end +-- 管道命令 +function redis:pipeline(opt) + local cmds = {} + if opt and #opt > 0 then + for _, cmd in ipairs(opt) do + cmds[#cmds+1] = CMD(unpack(cmd)) + end + end + local max_read_times = #cmds + if max_read_times > 0 then + local rets = {} + local sock = self.sock + sock:send(concat(cmds)) + for i = 1, max_read_times do + rets[#rets+1] = {read_response(sock)} + end + return true, rets + end + return nil +end + function redis:close() if self.sock then self.sock:close() diff --git a/script/test_Cache.lua b/script/test_Cache.lua index 2c259445..647b46bb 100644 --- a/script/test_Cache.lua +++ b/script/test_Cache.lua @@ -129,4 +129,12 @@ Co.spwan(function ( ... ) -- 其它一些特殊方法支持 -- type, move, rename, keys, randomkey等等 print(Cache:count()) + + -- 管道命令支持 + local ok, ret = Cache:pipeline { + {"HMSET", "USER_INFO", "name", "Candy", "email", '869646063@qq.com', 'phone', '13000000000'}, + {"HGET", "USER_INFO", "email"}, + {"HGET", "USER_INFO", "phone"}, + } + print(ok); var_dump(ret) end) From 1edf06c958b58d4ab4123c1b7d0b33be4cf07b59 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 25 May 2019 06:57:26 +0800 Subject: [PATCH 070/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8DDB=E7=9A=84=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E5=85=A8=E5=B1=80=E5=87=BD=E6=95=B0=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/DB/init.lua | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index aedfac98..3fc68afa 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -66,8 +66,6 @@ local COMMA = ", " -- 空闲连接时间 local WAIT_TIMEOUT = 31536000 -local WAIT_TIMEOUT_NOT_SET = true - -- 数据库连接创建函数 local function DB_CREATE (opt) local times = 1 @@ -83,8 +81,7 @@ local function DB_CREATE (opt) times = times + 1 timer.sleep(3) end - if WAIT_TIMEOUT_NOT_SET then -- 设置连接超时时间 - WAIT_TIMEOUT_NOT_SET = false + if not opt.INITIALIZATION then -- 设置连接超时时间 db:query(fmt('SET GLOBAL wait_timeout=%s', WAIT_TIMEOUT)) db:query(fmt('SET GLOBAL interactive_timeout=%s', WAIT_TIMEOUT)) end From debcc546dec9b64db3dfa3d543cc5036e6e74388 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 25 May 2019 20:53:31 +0800 Subject: [PATCH 071/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dhttpserver=E5=85=B3?= =?UTF-8?q?=E9=97=AD=E8=BF=9E=E6=8E=A5=E5=90=8E=E7=9A=84=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E8=A1=8C=E4=B8=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/init.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lualib/httpc/init.lua b/lualib/httpc/init.lua index 6b8981ee..51f6dc72 100644 --- a/lualib/httpc/init.lua +++ b/lualib/httpc/init.lua @@ -63,6 +63,9 @@ local function httpc_response(IO, SSL) end local Content_Length = toint(HEADER['Content-Length'] or HEADER['content-length']) local chunked = HEADER['Transfer-Encoding'] + if not chunked and not Content_Length then + Content_Length = 0 + end if Content_Length then if (#DATA - posB) == Content_Length then IO:close() @@ -432,4 +435,4 @@ function httpc.file(domain, HEADER, FILES, TIMEOUT) return httpc_response(IO, PROTOCOL) end -return httpc \ No newline at end of file +return httpc From 072402e569195e283878df55f1dc82d5c6b92b6e Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 26 May 2019 12:20:55 +0800 Subject: [PATCH 072/956] =?UTF-8?q?=E5=88=A0=E9=99=A4=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E8=BF=87=E6=97=B6=E7=9A=84=E4=BB=A3=E7=A0=81=E4=B8=8E=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E4=BB=A3=E7=A0=81=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http.lua | 3 +-- lualib/utils/init.lua | 8 +------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index 69f9c558..d0726027 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -547,9 +547,8 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) local ok, msg = pcall(Switch_Protocol, http, cls, sock, HEADER, METHOD, VERSION, PATH, HEADER['X-Real-IP'] or ipaddr, start) if not ok then Log:ERROR(msg) - return sock:close() end - return + return sock:close() else local file_type local path = PATH diff --git a/lualib/utils/init.lua b/lualib/utils/init.lua index 0857f3ee..81b7a99d 100644 --- a/lualib/utils/init.lua +++ b/lualib/utils/init.lua @@ -47,24 +47,18 @@ end -- local co = require "internal.Co" -- local tcp = require "internal.TCP" -- local Timer = require "internal.Timer" --- local DB = require "DB" --- local Cache = require "Cache" -- co.spwan(function ( ... ) -- while 1 do -- local self = co.self() --- local ti = Timer.timeout(0.1, function() +-- local ti = Timer.timeout(1, function() -- local co_count, task_count = co.count() -- local tcp_count = tcp.count() -- local time_count = Timer.count() --- local db_count = DB.count() --- local cache_count = Cache.count() -- print("=======================") -- print("co 数量为:", co_count) -- print("tcp 数量为:", tcp_count) -- print("task 数量为:", task_count) -- print("timer 数量为:", time_count) --- print("db 数量为:", db_count) --- print("cache 数量为:", cache_count) -- print("当前内存为:", collectgarbage('count')) -- print("=======================") -- co.wakeup(self) From f2f4cb133402415e406d9a25b2d9dd85ea50a5c0 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 26 May 2019 19:18:57 +0800 Subject: [PATCH 073/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0websocket=20client;?= =?UTF-8?q?=E4=BC=98=E5=8C=96websocket-server;=E4=BC=98=E5=8C=96=E5=AE=9A?= =?UTF-8?q?=E6=97=B6=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/websocket/client.lua | 266 +++++++++++++++++++++++++ lualib/protocol/websocket/protocol.lua | 28 ++- lualib/protocol/websocket/server.lua | 252 +++++++++++------------ 3 files changed, 400 insertions(+), 146 deletions(-) create mode 100644 lualib/protocol/websocket/client.lua diff --git a/lualib/protocol/websocket/client.lua b/lualib/protocol/websocket/client.lua new file mode 100644 index 00000000..f0b5af74 --- /dev/null +++ b/lualib/protocol/websocket/client.lua @@ -0,0 +1,266 @@ +local class = require "class" +local log = require "logging" + +local tcp = require "internal.TCP" + +local wbproto = require "protocol.websocket.protocol" +local _recv_frame = wbproto.recv_frame +local _send_frame = wbproto.send_frame + +local httpparser = require "httpparser" +local RESPONSE_PROTOCOL_PARSER = httpparser.parser_response_protocol +local RESPONSE_HEADER_PARSER = httpparser.parser_response_header + +local crypt = require "crypt" +local sha1 = crypt.sha1 +local base64encode = crypt.base64encode + +local type = type +local setmetatable = setmetatable + +local random = math.random +local toint = math.tointeger +local os_date = os.date +local concat = table.concat + +local char = string.char +local byte = string.byte +local find = string.find +local fmt = string.format +local match = string.match + +local CRLF = '\x0d\x0a' +local CRLF2 = '\x0d\x0a\x0d\x0a' + + +local function rshift(a, b) + return a >> b +end + +local function band (a, b) + return a & b +end + +local function sock_read (self, byte) + local sock = self.sock + if self.ssl then + return sock:ssl_recv(byte) + end + return sock:recv(byte) +end + +local function sock_send (self, data) + local sock = self.sock + if self.ssl then + return sock:ssl_send(data) + end + return sock:send(data) +end + +local function sock_connect (self, domain, port) + local sock = self.sock + if self.ssl then + return sock:ssl_connect(domain, port) + end + return sock:connect(domain, port) +end + +local function sock_close (self) + self.sock:close() + self.sock = nil +end + +local function check_response (self, secure) + local buffers = {} + while 1 do + local data = sock_read(self, 1024) + if not data then + return nil, '服务端断开了连接' + end + buffers[#buffers + 1] = data + local buffer = concat(buffers) + if find(buffer, CRLF2) then + local version, code, msg = RESPONSE_PROTOCOL_PARSER(buffer) + if code ~= 101 then + sock_close(self) + return nil, '协议升级失败' + end + local headers = RESPONSE_HEADER_PARSER(buffer) + if not headers then + sock_close(self) + return nil, '错误: ws升级协议' + end + local sec_key = headers['Sec-WebSocket-Accept'] + local connection = headers['Connection'] + if connection ~= 'Upgrade' then + sock_close(self) + return nil, '错误: 不支持的ws协议版本' + end + if sec_key ~= secure then + sock_close(self) + return nil, '错误: Sec-WebSocket-Accept验证失败' + end + return true + end + end +end + +-- HTTP[s] Over WebSocket Upgrade +local function do_handshake (self) + + local ok, err = sock_connect(self, self.domain, self.port) + if not ok then + sock_close(self) + return nil, err + end + + local GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' + local key = char( + byte('c'), byte('f'), byte('a'), byte('d'), byte('m'), byte('i'), byte('n'), + random(256) - 1, random(256) - 1, random(256) - 1, + random(256) - 1, random(256) - 1, random(256) - 1, + random(256) - 1, random(256) - 1, random(256) - 1 + ) + + local sec_key = base64encode(key) + local req = { + fmt('GET %s HTTP/1.1', self.path), + fmt('Data: %s', os_date("Date: %a, %d %b %Y %X GMT")), + fmt('host: %s:%s', self.domain, self.port), + fmt('Sec-WebSocket-Key: %s', sec_key), + 'Sec-WebSocket-Version: 13', + 'Upgrade: websocket', + 'Connection: Upgrade', + CRLF + } + local ok, err = sock_send(self, concat(req, CRLF)) + if not ok then + sock_close(self) + return ok, err + end + + return check_response(self, base64encode(sha1(sec_key..GUID))) +end + +local function url_split (self) + local scheme, domain_port, path = match(self.url, '^(ws[s]?)://([^/]+)(.*)') + if not scheme or not domain_port then + return nil, "连接失败: 无效的url参数" + end + + if not path or path == '' then + return nil, "连接失败: wss无path需要以'/'结尾" + end + + local domain, port + if find(domain_port, ':') then + local _, Bracket_Pos = find(domain_port, '[%[%]]') + if Bracket_Pos then + domain, port = match(domain_port, '%[(.+)%][:]?(%d*)') + else + domain, port = match(domain_port, '([^:]+):(%d*)') + end + if not domain then + return nil, "无效或者非法的主机名: "..domain_port + end + port = toint(port) + if not port then + port = 80 + end + else + domain, port = domain_port, 80 + end + + -- 判断是否需要ssl socket + if scheme == 'wss' then + self.ssl = true + end + self.domain = domain + self.port = port + self.path = path + return true +end + +local websocket = class("websocket-client") + +function websocket:ctor (opt) + self.ssl = nil + self.url = opt.url + self.sock = tcp:new() + self.sock._timeout = opt.timeout + self.send_masked = opt.send_masked + self.max_payload_len = opt.max_payload_len or 65535 +end + +-- 设置超时 +function websocket:set_timeout (timeout) + self.sock._timeout = timeout +end + +function websocket:connect () + if self.state then + return nil, '已连接' + end + -- 切割URL + local ok, err = url_split(self) + if not ok then + return nil, err + end + -- Websocket握手流程 + local ok, err = do_handshake(self) + if not ok then + return nil, err + end + self.state = true + return true, err +end + +-- 发送 text/binary +function websocket:send (data, is_binary) + if not self.state then + return nil, '未连接' + end + return _send_frame(self.sock, true, binary and 0x2 or 0x1, data, self.max_payload_len, self.send_masked) +end + +-- 接受数据 +function websocket:recv() + if not self.state then + return nil, '未连接' + end + local data, typ, err = _recv_frame(self.sock, self.max_payload_len, self.send_masked) + if typ == 'close' or not typ then + self.state = nil + if type == 'close' then + return nil, 'server was closed session' + end + return nil, data or err + end + return data, typ +end + +-- 发送ping +function websocket:ping( data) + if not self.state then + return nil, '未连接' + end + return _send_frame(self.sock, true, 0x9, data, self.max_payload_len, self.send_masked) +end + +-- 发送pong +function websocket:pong(data) + if not self.state then + return nil, '未连接' + end + return _send_frame(self.sock, true, 0xA, data, self.max_payload_len, self.send_masked) +end + +-- 清理连接 +function websocket:close () + if self.sock then + self.sock:close() + self.sock = nil + end +end + +return websocket diff --git a/lualib/protocol/websocket/protocol.lua b/lualib/protocol/websocket/protocol.lua index e5cae73a..809f8bf3 100644 --- a/lualib/protocol/websocket/protocol.lua +++ b/lualib/protocol/websocket/protocol.lua @@ -28,9 +28,23 @@ local types = { [0xa] = "pong", } +local function sock_recv (sock, byte) + if sock.ssl then + return sock:ssl_recv(byte) + end + return sock:recv(byte) +end + +local function sock_send (sock, data) + if sock.ssl then + return sock:ssl_send(data) + end + return sock:send(data) +end + function _M.recv_frame(sock, max_payload_len, force_masking) - local data, err = sock:recv(2) + local data, err = sock_recv(sock, 2) if not data then return nil, nil, err end @@ -62,7 +76,7 @@ function _M.recv_frame(sock, max_payload_len, force_masking) local payload_len = snd & 0x7f if payload_len == 126 then - local data, err = sock:recv(2) + local data, err = sock_recv(sock, 2) if not data then return nil, nil, "failed to receive the 2 byte payload length: " .. (err or "unknown") @@ -70,7 +84,7 @@ function _M.recv_frame(sock, max_payload_len, force_masking) payload_len = (byte(data, 1) >> 8) | byte(data, 2) elseif payload_len == 127 then - local data, err = sock:recv(8) + local data, err = sock_recv(sock, 8) if not data then return nil, nil, "failed to receive the 8 byte payload length: " .. (err or "unknown") @@ -120,7 +134,7 @@ function _M.recv_frame(sock, max_payload_len, force_masking) local data if rest > 0 then - data, err = sock:recv(rest) + data, err = sock_recv(sock, rest) if not data then return nil, nil, "failed to read masking-len and payload: " .. (err or "unknown") @@ -214,7 +228,7 @@ local function build_frame(fin, opcode, payload_len, payload, masking) snd = 127 -- XXX we only support 31-bit length here - extra_len_bytes = char(0, 0, 0, 0, + extra_len_bytes = char(0, 0, 0, 0, (payload_len >> 24) & 0xff, (payload_len >> 16) & 0xff, (payload_len >> 8) & 0xff, @@ -272,7 +286,7 @@ function _M.send_frame(sock, fin, opcode, payload, max_payload_len, masking) if not frame then return nil, "failed to build frame: " .. err end - return sock:send(frame) + return sock_send(sock, frame) end -return _M \ No newline at end of file +return _M diff --git a/lualib/protocol/websocket/server.lua b/lualib/protocol/websocket/server.lua index 1a54f5dc..9232799b 100644 --- a/lualib/protocol/websocket/server.lua +++ b/lualib/protocol/websocket/server.lua @@ -1,12 +1,13 @@ -local log = require "logging" local class = require "class" -local co = require "internal.Co" + local wbproto = require "protocol.websocket.protocol" local _recv_frame = wbproto.recv_frame local _send_frame = wbproto.send_frame +local log = require "logging" local Log = log:new({ dump = true, path = 'protocol-websocket-server'}) +local co = require "internal.Co" local co_self = co.self local co_wait = co.wait local co_spwan = co.spwan @@ -21,159 +22,132 @@ local setmetatable = setmetatable local tostring = tostring local char = string.char +-- 将回调函数写入到队列内 +local function add_to_queue(queue, f) + queue[#queue + 1] = f +end + +-- 唤醒write queue +local function wakeup(co, ...) + return co and co_wakeup(co, ...) +end local websocket = class("websocket-server") function websocket:ctor(opt) - self.cls = opt.cls - self.sock = opt.sock - self.sock._timeout = nil -end - --- 将回调函数写入到队列内 -local function add_to_queue(queue, func) - queue[#queue + 1] = func + self._VERSION = '0.07' + self.cls = opt.cls + self.sock = opt.sock + self.co = co_self() + self.write_co = nil + self.closed = nil + self.sock._timeout = nil + self.queue = {} + self.write_state = 'work' + co_spwan(function () + self.write_co = co_self() + while 1 do + self.write_state = 'work' + for _, f in ipairs(self.queue) do + local ok, err = pcall(f) + if not ok then + Log:ERROR(err) + end + end + if self.closed then + self.write_state = 'quit' + return + end + self.queue = {} + self.write_state = 'wait' + local continue = co_wait() + if not continue then + self.write_state = 'quit' + return + end + end + end) end --- 一次将多条回调函数写入到队列内 -local function more_add_to_queue(queue, list) - for _, func in ipairs(list) do - add_to_queue(queue, func) +-- send_text、send_binary +function websocket:send (data, binary) + if self.closed then + return + end + if data and type(data) == 'string' then + add_to_queue(self.queue, function () + return _send_frame(self.sock, true, binary and 0x2 or 0x1, data, self.max_payload_len, self.send_masked) + end) + if self.write_state == 'wait' then + return wakeup(self.write_co, true) end + end end --- 唤醒write queue -local function wakeup(co) - return co and co_wakeup(co) +-- 发送close帧 +function websocket:close (data) + if self.closed then + return + end + self.closed = true + if type(data) == 'string' and data ~= '' then + add_to_queue(self.queue, function () + return _send_frame(self.sock, true, 0x8, char(((1000 >> 8) & 0xff), (1000 & 0xff))..data, self.max_payload_len, self.send_masked) + end) + end + if self.write_state == 'wait' then + wakeup(self.write_co) + end + wakeup(self.co) end +-- Websocket Server 事件循环 function websocket:start() - local cls - local sock = self.sock - local current_co = co_self() - local write_list = {} - local write_co = co_spwan(function (...) - while 1 do - for _, f in ipairs(write_list) do - local ok, err = pcall(f) - if not ok then - Log:ERROR(err) - end - end - write_list = {} - co_wait() - if #write_list == 0 then - -- print("写入协程退出了") - return - end + local sock = self.sock + local cls = self.cls:new { ws = self } + local on_open = cls.on_open + local on_message = cls.on_message + local on_error = cls.on_error + local on_close = cls.on_close + local ok, err = pcall(on_open, cls) + if not ok then + self.sock = nil + return Log:ERROR(err) + end + self.cls = nil + self.sock._timeout = cls.timeout + self.send_masked = cls.send_masked + self.max_payload_len = cls.max_payload_len or 65535 + while 1 do + local data, typ, err = _recv_frame(sock, self.max_payload_len, self.send_masked) + if (not data and not typ) or typ == 'close' then + self.closed = true + if self.write_state == 'wait' then + wakeup(self.write_co) + end + if err and err ~= 'read timeout' then + local ok, err = pcall(on_error, cls, err) + if not ok then + Log:ERROR(err) end - end) - local ws = { - CLOSE = false, - send = function (self, data, binary) - if self.CLOSE then return end - if data and type(data) == 'string' then - local code = 0x1 - if binary then - code = 0x2 - end - add_to_queue(write_list, function () - return _send_frame( - sock, - true, - code, - data, - cls.max_payload_len or 65535, - cls.send_masked or false - ) - end) - return wakeup(write_co) - end - end, - close = function (self, data) - if self.CLOSE then return end - self.CLOSE = true - more_add_to_queue(write_list, { - function() - return _send_frame( - sock, - true, - 0x8, - char(((1000 >> 8) & 0xff),(1000 & 0xff))..(data or ""), - cls.max_payload_len or 65535, - cls.send_masked or false - ) - end, - function() return sock:close() end, - }) - return wakeup(current_co), wakeup(write_co) - end, - -- ping = function (self, data) - -- if self.CLOSE then return end - -- add_to_queue(write_list, function() - -- _send_frame(sock, true, 0x9, data, cls.max_payload_len or 65535, cls.send_masked or false) - -- end) - -- return wakeup(write_co) - -- end, - -- pong = function (self, data) - -- if self.CLOSE then return end - -- add_to_queue(write_list, function() - -- _send_frame(sock, true, 0xa, data, cls.max_payload_len or 65535, cls.send_masked or false) - -- end) - -- return wakeup(write_co) - -- end, - } - cls = self.cls:new { ws = ws } - local on_open = cls.on_open - local on_message = cls.on_message - local on_error = cls.on_error - local on_close = cls.on_close - local ok, err = pcall(on_open, cls) - if not ok then + end + local ok, err = pcall(on_close, cls, data) + if not ok then Log:ERROR(err) - return sock:close() + end + self.sock = nil + return end - while 1 do - local data, typ, err = _recv_frame(sock, cls.max_payload_len, true) - if (not data and not typ) or typ == 'close' then - -- 客户端主动关闭: ws.CLOSE == flase - -- 服务端主动关闭: ws.CLOSE == true - if not ws.CLOSE then - ws.CLOSE = true - write_list = {} - sock:close() - end - if err then - local ok, err = pcall(on_error, cls, err) - if not ok then - Log:ERROR(err) - end - end - local ok, err = pcall(on_close, cls, data) - if not ok then - Log:ERROR(err) - end - -- print("读取协程退出了") - return wakeup(write_co) - end - if typ == 'ping' then - add_to_queue(write_list, function() - _send_frame( - sock, - true, - 0xa, - data, - cls.max_payload_len or 65535, - cls.send_masked or false - ) - end) - elseif typ == 'text' or typ == 'binary' then - co_spwan(on_message, cls, data, typ) - else - -- 目前将设计精简为: 除了需要回应的ping协议, 其他协议均不会触发任何Server端回调响应; - -- 如需特殊需求, 请自行在业务逻辑中解决(或使用定时器进行循环探测); - end + if typ == 'ping' then + add_to_queue(self.queue, function () return _send_frame(sock, true, 0xA, data or '', self.max_payload_len, self.send_masked) end) + if self.write_state == 'wait' then + wakeup(self.write_co, true) + end + end + if typ == 'text' or typ == 'binary' then + co_spwan(on_message, cls, data, typ == 'binary') end + end end return websocket From aafc49fe5baaa37db6223644e1f880a392226656 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 26 May 2019 19:19:57 +0800 Subject: [PATCH 074/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=AE=9A=E6=97=B6?= =?UTF-8?q?=E5=99=A8=E7=9A=84=E5=81=9C=E6=AD=A2=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/Timer.lua | 40 +++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/lualib/internal/Timer.lua b/lualib/internal/Timer.lua index 61a337a9..ad8b567a 100644 --- a/lualib/internal/Timer.lua +++ b/lualib/internal/Timer.lua @@ -62,17 +62,20 @@ function Timer.timeout(timeout, cb) Timer[timer] = nil end timer.co = co_new(function (...) - Timer_release(t) - local ok, err = pcall(cb) - if not ok then - Log:ERROR('timeout error:', err) - end - if timer.STOP then + if timer.STOP then return - end - Timer[timer] = nil - timer.STOP = true - timer.co = nil + end + Timer_release(t) + local ok, err = pcall(cb) + if not ok then + Log:ERROR('timeout error:', err) + end + if timer.STOP then + return + end + Timer[timer] = nil + timer.STOP = true + timer.co = nil end) Timer[timer] = timer ti_start(t, timeout, timer.co) @@ -104,14 +107,14 @@ function Timer.at(repeats, cb) timer.co = co_new(function () local co_wait = coroutine.yield while 1 do - if timer.STOP then - return - end - co_spwan(cb) - if timer.STOP then + if timer.STOP then return - end - co_wait() + end + co_spwan(cb) + if timer.STOP then + return + end + co_wait() end end) Timer[timer] = timer @@ -133,7 +136,8 @@ function Timer.sleep(repeats) timer.co = co_new(function (...) local current_co = timer.current_co Timer[timer] = nil - timer.current_co, timer.co = nil + timer.current_co = nil + timer.co = nil Timer_release(t) return co_wakeup(current_co) end) From acb508e8806374a18cec488f4fd87fcd1ef0bdab Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 26 May 2019 19:21:05 +0800 Subject: [PATCH 075/956] =?UTF-8?q?=E8=B0=83=E6=95=B4LOG=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core.c b/src/core.c index 7bb5fc53..8a672c60 100644 --- a/src/core.c +++ b/src/core.c @@ -150,6 +150,7 @@ const char *signame[]= { static void // 忽略信号 SIG_IGNORE(core_loop *loop, core_signal *signal, int revents){ + // LOG("ERROR", signum_to_string(signal->signum)); return ; } @@ -176,7 +177,7 @@ EV_ALLOC(void *ptr, long nsize){ for (;;) { void *newptr = xrealloc(ptr, nsize); if (newptr) return newptr; - LOG("WARN", "Allocate Failt, sleep sometime.."); + LOG("WARN", "Allocate failed, Sleep sometime.."); sleep(1); } } @@ -190,7 +191,7 @@ L_ALLOC(void *ud, void *ptr, size_t osize, size_t nsize){ for (;;) { void *newptr = xrealloc(ptr, nsize); if (newptr) return newptr; - LOG("WARN", "Allocate Failt, sleep sometime.."); + LOG("WARN", "Allocate failed, Sleep sometime.."); sleep(1); } } From de9334c152de8457b4a0edd30f353d7302b8eea8 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 26 May 2019 19:24:36 +0800 Subject: [PATCH 076/956] =?UTF-8?q?=E9=87=8D=E6=96=B0=E7=BC=96=E5=86=99ws?= =?UTF-8?q?=E7=A4=BA=E4=BE=8B=E5=B9=B6=E5=A2=9E=E5=8A=A0test=5Fwsclient.lu?= =?UTF-8?q?a=E7=A4=BA=E4=BE=8B.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/main.lua | 2 ++ script/test_wsclient.lua | 23 +++++++++++++++++++++++ script/ws.lua | 24 ++++++------------------ 3 files changed, 31 insertions(+), 18 deletions(-) create mode 100644 script/test_wsclient.lua diff --git a/script/main.lua b/script/main.lua index 242affbf..a5cd2141 100644 --- a/script/main.lua +++ b/script/main.lua @@ -28,6 +28,8 @@ app:enable_cookie() app:cookie_secure("https://github.com/CandyMi/core_framework") -- app:cookie_secure("candymi") +app:ws('/ws', require "ws") + -- 导入cf内置的admin库 local cfadmin = require "admin" diff --git a/script/test_wsclient.lua b/script/test_wsclient.lua new file mode 100644 index 00000000..ea488b2c --- /dev/null +++ b/script/test_wsclient.lua @@ -0,0 +1,23 @@ +local wc = require "protocol.websocket.client" + +-- local w = wc:new {url = "wss://[::1]/ws"} +-- local w = wc:new {url = "wss://[::1]:8080/ws"} +local w = wc:new {url = "ws://localhost:8080/ws"} +local ok, ret = w:connect() +if not ok then + return print(ok, ret) +end + +w:ping('ping') + +print(w:recv()) + +while 1 do + local data, typ = w:recv() + print(data, typ) + if not data then + break + end +end + +w:close() diff --git a/script/ws.lua b/script/ws.lua index d5e8a3c0..cf523ccd 100644 --- a/script/ws.lua +++ b/script/ws.lua @@ -1,16 +1,14 @@ local class = require "class" local cf = require "cf" local json = require "json" -local MQ = require "MQ" local websocket = class("websocket") function websocket:ctor(opt) self.ws = opt.ws -- websocket对象 self.send_masked = false -- 掩码(默认为false, 不建议修改或者使用) self.max_payload_len = 65535 -- 最大有效载荷长度(默认为65535, 不建议修改或者使用) - self.timeout = 15 + self.timeout = 15 -- 默认为一直等待, 非number类型会导致异常. self.count = 0 - self.mq = MQ:new({host = 'localhost', port = 6379, type = 'redis'}) end function websocket:on_open() @@ -19,19 +17,12 @@ function websocket:on_open() self.count = self.count + 1 self.ws:send(tostring(self.count)) end) - self.mq:on('/test/*', function (msg) -- 消息队列 - if not msg then - self.ws:send('{"code":500,"message":"无法连接到mq(reds)"}') - return self.ws:close() - end - self.ws:send(json.encode(msg)) - end) end function websocket:on_message(data, typ) print('on_message', self.ws, data) self.ws:send('welcome') - self.ws:close(data) + -- self.ws:close(data) end function websocket:on_error(error) @@ -40,13 +31,10 @@ end function websocket:on_close(data) print('on_close', self.ws, data) - if self.mq then -- 清理消息队列 - self.mq:close() - self.mq = nil - end - if self.timer then -- 清理定时器 - self.timer:stop() - self.timer = nil + if self.timer then -- 清理定时器 + print("清理定时器") + self.timer:stop() + self.timer = nil end end From 5326e99d4245715718062aec11e82ee3891ba3ee Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 26 May 2019 20:37:13 +0800 Subject: [PATCH 077/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0websocket=20client?= =?UTF-8?q?=E4=B8=8Eserver,=20=E8=A1=A5=E5=85=85Wiki=E5=AE=8C=E6=95=B4?= =?UTF-8?q?=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/websocket/server.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/protocol/websocket/server.lua b/lualib/protocol/websocket/server.lua index 9232799b..734cfcc3 100644 --- a/lualib/protocol/websocket/server.lua +++ b/lualib/protocol/websocket/server.lua @@ -131,7 +131,7 @@ function websocket:start() Log:ERROR(err) end end - local ok, err = pcall(on_close, cls, data) + local ok, err = pcall(on_close, cls, data or err) if not ok then Log:ERROR(err) end From 577088f5ed4f92b2d78d612a56bd1e02b8527e00 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 27 May 2019 01:24:14 +0800 Subject: [PATCH 078/956] =?UTF-8?q?=E4=BC=98=E5=8C=96httpc=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=80=A7=E8=83=BD,=20=E8=B0=83=E6=95=B4=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=8F=AF=E8=AF=BB=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/init.lua | 282 ++++++++++++++++++++---------------------- 1 file changed, 137 insertions(+), 145 deletions(-) diff --git a/lualib/httpc/init.lua b/lualib/httpc/init.lua index 51f6dc72..09e91466 100644 --- a/lualib/httpc/init.lua +++ b/lualib/httpc/init.lua @@ -32,8 +32,60 @@ local __TIMEOUT__ = 15 local httpc = {} -local function httpc_response(IO, SSL) - if not IO then +local function sock_recv (sock, PROTOCOL, byte) + if PROTOCOL == 'https' then + local data, len = sock:ssl_recv(byte) + if data then + return data, len + end + end + if PROTOCOL == 'http' then + local data, len = sock:recv(byte) + if data then + return data, len + end + end + return nil, '服务端断开了连接' +end + +local function sock_connect(sock, PROTOCOL, DOAMIN, PORT) + if PROTOCOL == 'https' then + local ok, err = sock:ssl_connect(DOAMIN, PORT) + if ok then + return true + end + end + if PROTOCOL == 'http' then + local ok, err = sock:connect(DOAMIN, PORT) + if ok then + return true + end + end + sock:close() + return nil, 'httpc连接失败.' +end + +local function sock_send(sock, PROTOCOL, DATA) + if PROTOCOL == 'http' then + local ok = sock:send(DATA) + if ok then + return true + end + end + + if PROTOCOL == 'https' then + local ok = sock:send(DATA) + if ok then + return true + end + end + sock:close() + return nil, "httpc发送请求失败" +end + + +local function httpc_response(sock, SSL) + if not sock then return nil, "Can't used this method before other httpc method.." end local CODE, HEADER, BODY @@ -41,14 +93,9 @@ local function httpc_response(IO, SSL) local content = {} local times = 0 while 1 do - local data, len - if SSL == "http" then - data, len = IO:recv(1024) - else - data, len = IO:ssl_recv(1024) - end + local data, len = sock_recv(sock, SSL, 1024) if not data then - IO:close() + sock:close() return nil, "A peer of remote server close this connection." end insert(content, data) @@ -58,7 +105,7 @@ local function httpc_response(IO, SSL) CODE = RESPONSE_PROTOCOL_PARSER(split(DATA, 1, posB)) HEADER = RESPONSE_HEADER_PARSER(split(DATA, 1, posB)) if not CODE or not HEADER then - IO:close() + sock:close() return nil, "can't resolvable protocol." end local Content_Length = toint(HEADER['Content-Length'] or HEADER['content-length']) @@ -68,25 +115,20 @@ local function httpc_response(IO, SSL) end if Content_Length then if (#DATA - posB) == Content_Length then - IO:close() + sock:close() return CODE, split(DATA, posB+1, #DATA) end local content = {split(DATA, posB+1, #DATA)} while 1 do - local data, len - if SSL == "http" then - data, len = IO:recv(1024) - else - data, len = IO:ssl_recv(1024) - end + local data, len = sock_recv(sock, SSL, 1024) if not data then - IO:close() + sock:close() return CODE, SSL.."[Content_Length] A peer of remote server close this connection." end insert(content, data) local DATA = concat(content) if Content_Length == #DATA then - IO:close() + sock:close() return CODE, DATA end end @@ -101,7 +143,7 @@ local function httpc_response(IO, SSL) local len = toint(fmt("0x%s", hex)) if len and len == #block then if len == 0 and block == '' then - IO:close() + sock:close() return CODE, concat(body) end insert(body, block) @@ -111,14 +153,9 @@ local function httpc_response(IO, SSL) insert(content, DATA) end while 1 do - local data, len - if SSL == "http" then - data, len = IO:recv(1024) - else - data, len = IO:ssl_recv(1024) - end + local data, len = sock_recv(sock, SSL, 1024) if not data then - IO:close() + sock:close() return CODE, SSL.."[chunked] A peer of remote server close this connection A." end insert(content, data) @@ -129,7 +166,7 @@ local function httpc_response(IO, SSL) local len = toint(fmt("0x%s", hex)) if len and len == #block then if len == 0 and block == '' then - IO:close() + sock:close() return CODE, concat(body) end insert(body, block) @@ -142,107 +179,71 @@ local function httpc_response(IO, SSL) end end -local function IO_CONNECT(IO, PROTOCOL, DOAMIN, PORT) - local PORT = tonumber(PORT) - if PROTOCOL == "http" then - if not PORT or PORT > 65536 or PORT < 1 then - PORT = 80 - end - local ok, err = IO:connect(DOAMIN, PORT) - if not ok then - IO:close() - return false, 'httpc 连接失败: '.. DOAMIN ..',' .. PORT - end - return true - end - if PROTOCOL == "https" then - if not PORT or PORT > 65536 or PORT < 1 then - PORT = 443 - end - local ok = IO:ssl_connect(DOAMIN, PORT) - if not ok then - IO:close() - return false, 'httpc ssl连接失败: '.. DOAMIN ..',' .. PORT - end - return true +-- 分割httpc domain +local function splite_protocol(domain) + if type(domain) ~= 'string' then + return nil, '1. 非法的域名' end - IO:close() - return nil, "IO_CONNECT error! unknow PROTOCOL: "..tostring(PROTOCOL) -end -local function IO_SEND(IO, PROTOCOL, DATA) - if PROTOCOL == "http" then - local ok = IO:send(DATA) - if not ok then - IO:close() - return nil, "httpc request get method error" - end - return true - end - if PROTOCOL == "https" then - local ok = IO:ssl_send(DATA) - if not ok then - IO:close() - return nil, "httpc ssl request get method error" - end - return true + local protocol, domain_port, path = match(domain, '^(http[s]?)://([^/]+)(.*)') + if not protocol or not domain_port or not path then + return nil, '2. 错误的url' end - IO:close() - return nil, "IO_SEND error! unknow PROTOCOL: "..tostring(PROTOCOL) -end - -local function splite_protocol(domain) - local PROTOCOL, DOMAIN, PATH = match(domain, '(http[s]?)://([^/]+)([/]?.*)') - if not PROTOCOL or PROTOCOL == '' or not DOMAIN or DOMAIN == '' then - return nil, "Invaild protocol" + if not path or path == '' then + return nil, "3. http无path需要以'/'结尾." end - if not PATH or PATH == '' then - PATH = '/' - end - local times = 0 - for colon in splite(DOMAIN, ":") do - times = times + 1 - end - local PORT - if times == 1 then - DOMAIN, PORT = match(DOMAIN, "(.+):([%d]+)") - elseif times > 1 then - local domain, port = match(DOMAIN, "%[(.+)%][:]?([%d]*)") - if domain and port then - DOMAIN, PORT = domain, port - end + + local domain, port + if find(domain_port, ':') then + local _, Bracket_Pos = find(domain_port, '[%[%]]') + if Bracket_Pos then + domain, port = match(domain_port, '%[(.+)%][:]?(%d*)') + else + domain, port = match(domain_port, '([^:]+):(%d*)') + end + if not domain then + return nil, "4. 无效或者非法的主机名: "..domain_port + end + port = toint(port) + if not port then + port = 80 + end + else + domain, port = domain_port, 80 end - return PROTOCOL, DOMAIN, PORT, PATH + return { + protocol = protocol, + domain = domain, + port = port, + path = path, + } end -- HTTP GET function httpc.get(domain, HEADER, ARGS, TIMEOUT) - local PROTOCOL, DOMAIN, PORT, PATH = splite_protocol(domain) - local port - if type(PORT) == 'number' and (port ~= 80 or port ~= 443) then - port = ":"..PORT - else - port = "" + local opt, err = splite_protocol(domain) + if not opt then + return nil, err end local request = { - fmt("GET %s HTTP/1.1", PATH), - fmt("Host: %s", DOMAIN..':'..port), + fmt("GET %s HTTP/1.1", opt.path), + fmt("Host: %s", opt.domain..':'..opt.port), 'Accept: */*', 'Accept-Encoding: identity', fmt("Connection: keep-alive"), fmt("User-Agent: %s", SERVER), } - if ARGS and type(ARGS) == "table" then + if type(ARGS) == "table" then local args = {} for _, arg in ipairs(ARGS) do assert(#arg == 2, "args need key[1]->value[2] (2 values)") insert(args, arg[1]..'='..arg[2]) end - request[1] = fmt("GET %s HTTP/1.1", PATH..'?'..concat(args, "&")) + request[1] = fmt("GET %s HTTP/1.1", opt.path..'?'..concat(args, "&")) end if HEADER and type(HEADER) == "table" then for _, header in ipairs(HEADER) do @@ -254,39 +255,36 @@ function httpc.get(domain, HEADER, ARGS, TIMEOUT) insert(request, CRLF) local REQ = concat(request, CRLF) - local IO = tcp:new():timeout(TIMEOUT or __TIMEOUT__) - local ok, err = IO_CONNECT(IO, PROTOCOL, DOMAIN, PORT) + local sock = tcp:new():timeout(TIMEOUT or __TIMEOUT__) + local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) if not ok then return ok, err end - local ok, err = IO_SEND(IO, PROTOCOL, REQ) + local ok, err = sock_send(sock, opt.protocol, REQ) if not ok then return ok, err end - return httpc_response(IO, PROTOCOL) + return httpc_response(sock, opt.protocol) end -- HTTP POST function httpc.post(domain, HEADER, BODY, TIMEOUT) - local PROTOCOL, DOMAIN, PORT, PATH = splite_protocol(domain) - local port - if type(PORT) == 'number' and (port ~= 80 or port ~= 443) then - port = ":"..PORT - else - port = "" + local opt, err = splite_protocol(domain) + if not opt then + return nil, err end local request = { - fmt("POST %s HTTP/1.1\r\n", PATH), - fmt("Host: %s\r\n", DOMAIN..':'..port), + fmt("POST %s HTTP/1.1\r\n", opt.path), + fmt("Host: %s\r\n", opt.domain..':'..opt.port), 'Accept: */*\r\n', 'Accept-Encoding: identity\r\n', fmt("Connection: keep-alive\r\n"), fmt("User-Agent: %s\r\n", SERVER), 'Content-Type: application/x-www-form-urlencoded\r\n', } - if HEADER and type(HEADER) == "table" then + if type(HEADER) == "table" then for _, header in ipairs(HEADER) do assert(string.lower(header[1]) ~= 'content-length', "please don't give Content-Length") assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") @@ -311,33 +309,30 @@ function httpc.post(domain, HEADER, BODY, TIMEOUT) local REQ = concat(request) - local IO = tcp:new():timeout(TIMEOUT or __TIMEOUT__) - local ok, err = IO_CONNECT(IO, PROTOCOL, DOMAIN, PORT) + local sock = tcp:new():timeout(TIMEOUT or __TIMEOUT__) + local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) if not ok then return ok, err end - local ok, err = IO_SEND(IO, PROTOCOL, REQ) + local ok, err = sock_send(sock, opt.protocol, REQ) if not ok then return ok, err end - return httpc_response(IO, PROTOCOL) + return httpc_response(sock, opt.protocol) end function httpc.json(domain, HEADER, JSON, TIMEOUT) - local PROTOCOL, DOMAIN, PORT, PATH = splite_protocol(domain) - local port - if type(PORT) == 'number' and (port ~= 80 or port ~= 443) then - port = ":"..PORT - else - port = "" + local opt, err = splite_protocol(domain) + if not opt then + return nil, err end assert(type(JSON) == "string", "Please passed A vaild json string.") local request = { - fmt("POST %s HTTP/1.1\r\n", PATH), - fmt("Host: %s\r\n", DOMAIN..':'..port), + fmt("POST %s HTTP/1.1\r\n", opt.path), + fmt("Host: %s\r\n", opt.domain..':'..opt.port), 'Accept: */*\r\n', 'Accept-Encoding: identity\r\n', fmt("Connection: keep-alive\r\n"), @@ -358,31 +353,28 @@ function httpc.json(domain, HEADER, JSON, TIMEOUT) local REQ = concat(request) - local IO = tcp:new():timeout(TIMEOUT or __TIMEOUT__) - local ok, err = IO_CONNECT(IO, PROTOCOL, DOMAIN, PORT) + local sock = tcp:new():timeout(TIMEOUT or __TIMEOUT__) + local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) if not ok then return ok, err end - local ok, err = IO_SEND(IO, PROTOCOL, REQ) + local ok, err = sock_send(sock, opt.protocol, REQ) if not ok then return ok, err end - return httpc_response(IO, PROTOCOL) + return httpc_response(sock, opt.protocol) end function httpc.file(domain, HEADER, FILES, TIMEOUT) - local PROTOCOL, DOMAIN, PORT, PATH = splite_protocol(domain) - local port - if type(PORT) == 'number' and (port ~= 80 or port ~= 443) then - port = ":"..PORT - else - port = "" + local opt, err = splite_protocol(domain) + if not opt then + return nil, err end local request = { - fmt("POST %s HTTP/1.1\r\n", PATH), - fmt("Host: %s\r\n", DOMAIN..':'..port), + fmt("POST %s HTTP/1.1\r\n", opt.path), + fmt("Host: %s\r\n", opt.domain..':'..opt.port), 'Accept: */*\r\n', 'Accept-Encoding: identity\r\n', fmt("Connection: keep-alive\r\n"), @@ -423,16 +415,16 @@ function httpc.file(domain, HEADER, FILES, TIMEOUT) local REQ = concat(request) - local IO = tcp:new():timeout(TIMEOUT or __TIMEOUT__) - local ok, err = IO_CONNECT(IO, PROTOCOL, DOMAIN, PORT) + local sock = tcp:new():timeout(TIMEOUT or __TIMEOUT__) + local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) if not ok then return ok, err end - local ok, err = IO_SEND(IO, PROTOCOL, REQ) + local ok, err = sock_send(sock, opt.protocol, REQ) if not ok then return ok, err end - return httpc_response(IO, PROTOCOL) + return httpc_response(sock, opt.protocol) end return httpc From e14ba94e56bc1074446505745a6690aed759b4a7 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 27 May 2019 01:24:50 +0800 Subject: [PATCH 079/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/main.lua | 14 +++++++++-- script/test_Cache.lua | 5 ++-- script/test_MQ.lua | 12 +++++----- script/test_cfadmin.lua | 15 ++++++++++-- script/test_dns.lua | 16 ++++++------- script/test_httpc.lua | 52 ++++++++++++++++++++--------------------- 6 files changed, 67 insertions(+), 47 deletions(-) diff --git a/script/main.lua b/script/main.lua index a5cd2141..731a876e 100644 --- a/script/main.lua +++ b/script/main.lua @@ -28,8 +28,6 @@ app:enable_cookie() app:cookie_secure("https://github.com/CandyMi/core_framework") -- app:cookie_secure("candymi") -app:ws('/ws', require "ws") - -- 导入cf内置的admin库 local cfadmin = require "admin" @@ -58,6 +56,18 @@ view.api('/api/admin/test2', function (ctx, db) return '{"code":0,"msg":"hello world"}' end) +app:ws('/ws', require "ws") + +app:api('/api', function (content) + require('logging'):new():DEBUG(content.args or content.body) + return '{"code":200}' +end) + +app:use('/view', function (content) + require('logging'):new():DEBUG(content.files) + return '

                  cfadmin v0.3

                  ' +end) + -- 这里是设置语言的地方 -- 语言表在admin/locales内, 可参照key -> value进行填写. -- 传入一个数组表: 索引1是key, 索引2为显示内容. diff --git a/script/test_Cache.lua b/script/test_Cache.lua index 647b46bb..690f7daf 100644 --- a/script/test_Cache.lua +++ b/script/test_Cache.lua @@ -1,7 +1,6 @@ -- 测试redis local Cache = require "Cache" -local Co = require "internal.Co" -local timer = require "internal.Timer" +local cf = require "cf" require "utils" local opt = { @@ -12,7 +11,7 @@ local opt = { max = 1, } -Co.spwan(function ( ... ) +cf.fork(function ( ... ) local Cache = Cache:new(opt) local ok, err = Cache:connect() if not ok then diff --git a/script/test_MQ.lua b/script/test_MQ.lua index d25bbfc9..3b54e29b 100644 --- a/script/test_MQ.lua +++ b/script/test_MQ.lua @@ -1,5 +1,5 @@ -- 基于redis的订阅与发布消息队列(同步处理) -local Timer = require "internal.Timer" +local cf = require "cf" local MQ = require "MQ" require "utils" @@ -13,16 +13,16 @@ print(rds:on('/test/*', function (msg) var_dump(msg) end)) -Timer.at(1, function ( ... ) +cf.at(1, function ( ... ) rds:emit("/test/admin", '{"code":100}') end) -Timer.sleep(10) +cf.sleep(10) print("停止消息队列监听与投递.") rds:close() -- 基于mqtt的订阅与发布消息队列(同步处理) -local Timer = require "internal.Timer" +local cf = require "cf" local MQ = require "MQ" require "utils" @@ -32,7 +32,7 @@ local mqtt = MQ:new { type = 'mqtt' } -Timer.at(0.1, function ( ... ) +cf.at(0.1, function ( ... ) mqtt:emit("/test/admin", '{"code":100}') end) @@ -40,6 +40,6 @@ print(mqtt:on('/test/*', function (msg) var_dump(msg) end)) -Timer.sleep(10) +cf.sleep(10) print("停止消息队列监听与投递.") mqtt:close() diff --git a/script/test_cfadmin.lua b/script/test_cfadmin.lua index 24aa4add..731a876e 100644 --- a/script/test_cfadmin.lua +++ b/script/test_cfadmin.lua @@ -9,7 +9,7 @@ local DB = require "DB" ]] local db = DB:new({ - host = 'localhost', + host = '10.0.0.16', port = 3306, username = 'root', password = '123456789', @@ -49,7 +49,6 @@ local view = require "admin.view" -- 2. db初始化后的db对象, 方便用户直接使用. view.use('/admin/test1', function (ctx, db) - print(view.get_locale(), view.get_cdn()) return "hello world" end) @@ -57,6 +56,18 @@ view.api('/api/admin/test2', function (ctx, db) return '{"code":0,"msg":"hello world"}' end) +app:ws('/ws', require "ws") + +app:api('/api', function (content) + require('logging'):new():DEBUG(content.args or content.body) + return '{"code":200}' +end) + +app:use('/view', function (content) + require('logging'):new():DEBUG(content.files) + return '

                  cfadmin v0.3

                  ' +end) + -- 这里是设置语言的地方 -- 语言表在admin/locales内, 可参照key -> value进行填写. -- 传入一个数组表: 索引1是key, 索引2为显示内容. diff --git a/script/test_dns.lua b/script/test_dns.lua index b0895f08..f36f3263 100644 --- a/script/test_dns.lua +++ b/script/test_dns.lua @@ -1,37 +1,37 @@ local dns = require "protocol.dns" -local co = require "internal.Co" -co.spwan(function ( ... ) +local cf = require "cf" +cf.fork(function ( ... ) print("申请解析 1") local ok, ip = dns.resolve('www.baidu.com') print("解析完成 1", ok, ip) end) -co.spwan(function ( ... ) +cf.fork(function ( ... ) print("申请解析 2") local ok, ip = dns.resolve('www.baidu.com') print("解析完成 2", ok, ip) end) -co.spwan(function ( ... ) +cf.fork(function ( ... ) print("申请解析 3") local ok, ip = dns.resolve('www.baidu.com') print("解析完成 3", ok, ip) end) -co.spwan(function ( ... ) +cf.fork(function ( ... ) print("申请解析 11") local ok, ip = dns.resolve('www.qq.com') print("解析完成 11", ok, ip) end) -co.spwan(function ( ... ) +cf.fork(function ( ... ) print("申请解析 12") local ok, ip = dns.resolve('www.qq.com') print("解析完成 12", ok, ip) end) -co.spwan(function ( ... ) +cf.fork(function ( ... ) print("申请解析 13") local ok, ip = dns.resolve('www.qq.com') print("解析完成 13", ok, ip) -end) \ No newline at end of file +end) diff --git a/script/test_httpc.lua b/script/test_httpc.lua index 02cee87b..591cc1f2 100644 --- a/script/test_httpc.lua +++ b/script/test_httpc.lua @@ -1,34 +1,34 @@ -local Co = require "internal.Co" -local timer = require "internal.Timer" +local cf = require "cf" local httpc = require "httpc" local json = require "json" -local ti = timer.timeout(2, function ( ... ) - Co.spwan(function ( ... ) - -- GET请求: 在参数固定的情况下可以直接写在url内 - local code, body = httpc.get("http://localhost:8080/api?page=1&limit=10", {{"Auth", "admin"}}) - print(code, body) +local ti = cf.timeout(2, function ( ... ) + cf.fork(function ( ... ) + -- GET请求: 在参数固定的情况下可以直接写在url内 + local code, body = httpc.get("http://localhost:8080/api?page=1&limit=10", {{"Auth", "admin"}}) + print(code, body) - -- GET请求: 在参数为动态的情况下K呀提供请求数组由框架拼接 - local code, body = httpc.get("http://[::1]:8080/api", {{"Auth", "admin"}}, {{'page', 1}, {'limit', 10}}) - print(code, body) + -- GET请求: 在参数为动态的情况下可以提供请求数组由httpc库进行拼接 + local code, body = httpc.get("http://[::1]:8080/api", {{"Auth", "admin"}}, {{'page', 1}, {'limit', 10}}) + print(code, body) - -- POST HEADER 为数组, BODY为数组 - local code, body = httpc.post("http://[::ffff:127.0.0.1]:8080/api", {{"Auth", "admin"}}, {{'page', 1}, {'limit', 10}}) - print(code, body) + -- POST HEADER 为数组, BODY为数组 + local code, body = httpc.post("http://[::ffff:127.0.0.1]:8080/api", {{"Auth", "admin"}}, {{'page', 1}, {'limit', 10}}) + print(code, body) - -- POST HEADER 为数组, BODY为字符串 - local code, body = httpc.post("http://127.0.0.1:8080/api", {{"Auth", "admin"}}, "page=1&limit=10") - print(code, body) + -- POST HEADER 为数组, BODY为字符串 + local code, body = httpc.post("http://127.0.0.1:8080/api", {{"Auth", "admin"}}, "page=1&limit=10") + print(code, body) - -- http json请求示例 - local code, body = httpc.json("http://localhost:8080/api", {{"Auth", "admin"}}, json.encode({page=1, limit=10})) - print(code, body) - -- http 上传文件示例 - local code, body = httpc.file('http://localhost:8080/view', nil, { - {name='1', filename='1.jpg', file='1', type='abc'}, - {name='2', filename='2.jpg', file='2', type='abc'}, - }) - print(code, body) - end) + -- http json请求示例 + local code, body = httpc.json("http://localhost:8080/api", {{"Auth", "admin"}}, json.encode({page=1, limit=10})) + print(code, body) + + -- http 上传文件示例 + local code, body = httpc.file('http://localhost:8080/view', nil, { + {name='1', filename='1.jpg', file='1', type='abc'}, + {name='2', filename='2.jpg', file='2', type='abc'}, + }) + print(code, body) + end) end) From b702ca28739a9d82a60f4283254509bfd33d42c6 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 27 May 2019 05:24:07 +0800 Subject: [PATCH 080/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index cf19eb6c..aa9c3959 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -16,7 +16,7 @@ void SETSOCKETOPT(int sockfd, int mode){ int ret = 0; /* 设置非阻塞 */ - non_blocking(sockfd); + non_blocking(sockfd); /* 地址/端口重用 */ ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &Enable, sizeof(Enable)); @@ -407,18 +407,15 @@ tcp_sslconnect(lua_State *L){ if (!ssl) return 0; int status = SSL_do_handshake(ssl); - if (1 == status) { + if (status >= 1) { lua_pushboolean(L, 1); return 1; } - if (SSL_ERROR_WANT_READ == SSL_get_error(ssl, status)) { + int ERR = SSL_get_error(ssl, status); + // printf("status = %d, ERR = %d, SSL_ERROR_WANT_READ == %d, SSL_ERROR_WANT_WRITE == %d\n", status, ERR, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE); + if (SSL_ERROR_WANT_READ == ERR || SSL_ERROR_WANT_WRITE == ERR) { lua_pushnil(L); - lua_pushinteger(L, EV_READ); - return 2; - } - if (SSL_ERROR_WANT_WRITE == SSL_get_error(ssl, status)){ - lua_pushnil(L); - lua_pushinteger(L, EV_WRITE); + lua_pushinteger(L, ERR - 1); return 2; } return 0; @@ -530,6 +527,7 @@ luaopen_tcp(lua_State *L){ /* 添加SSL支持 */ SSL_library_init(); SSL_load_error_strings(); + // ERR_load_crypto_strings(); // CRYPTO_set_mem_functions(xmalloc, xrealloc, xfree); // OpenSSL_add_ssl_algorithms(); /* 添加SSL支持 */ From ca034dfaed62e3517adcb3f8e9b7f53faef3953a Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 27 May 2019 05:25:09 +0800 Subject: [PATCH 081/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AAhtt?= =?UTF-8?q?pc=E5=88=A4=E6=96=AD=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/init.lua | 23 +++++++++++++------ lualib/internal/TCP.lua | 49 +++++++++++++++++++++-------------------- 2 files changed, 41 insertions(+), 31 deletions(-) diff --git a/lualib/httpc/init.lua b/lualib/httpc/init.lua index 09e91466..04d7da90 100644 --- a/lualib/httpc/init.lua +++ b/lualib/httpc/init.lua @@ -61,25 +61,23 @@ local function sock_connect(sock, PROTOCOL, DOAMIN, PORT) return true end end - sock:close() return nil, 'httpc连接失败.' end local function sock_send(sock, PROTOCOL, DATA) - if PROTOCOL == 'http' then - local ok = sock:send(DATA) + if PROTOCOL == 'https' then + local ok = sock:ssl_send(DATA) if ok then return true end end - if PROTOCOL == 'https' then + if PROTOCOL == 'http' then local ok = sock:send(DATA) if ok then return true end end - sock:close() return nil, "httpc发送请求失败" end @@ -207,10 +205,13 @@ local function splite_protocol(domain) end port = toint(port) if not port then - port = 80 + port = 80 + if protocol == 'https' then + port = 443 + end end else - domain, port = domain_port, 80 + domain, port = domain_port, protocol == 'https' and 443 or 80 end return { protocol = protocol, @@ -258,10 +259,12 @@ function httpc.get(domain, HEADER, ARGS, TIMEOUT) local sock = tcp:new():timeout(TIMEOUT or __TIMEOUT__) local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) if not ok then + sock:close() return ok, err end local ok, err = sock_send(sock, opt.protocol, REQ) if not ok then + sock:close() return ok, err end return httpc_response(sock, opt.protocol) @@ -312,10 +315,12 @@ function httpc.post(domain, HEADER, BODY, TIMEOUT) local sock = tcp:new():timeout(TIMEOUT or __TIMEOUT__) local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) if not ok then + sock:close() return ok, err end local ok, err = sock_send(sock, opt.protocol, REQ) if not ok then + sock:close() return ok, err end return httpc_response(sock, opt.protocol) @@ -356,10 +361,12 @@ function httpc.json(domain, HEADER, JSON, TIMEOUT) local sock = tcp:new():timeout(TIMEOUT or __TIMEOUT__) local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) if not ok then + sock:close() return ok, err end local ok, err = sock_send(sock, opt.protocol, REQ) if not ok then + sock:close() return ok, err end return httpc_response(sock, opt.protocol) @@ -418,10 +425,12 @@ function httpc.file(domain, HEADER, FILES, TIMEOUT) local sock = tcp:new():timeout(TIMEOUT or __TIMEOUT__) local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) if not ok then + sock:close() return ok, err end local ok, err = sock_send(sock, opt.protocol, REQ) if not ok then + sock:close() return ok, err end return httpc_response(sock, opt.protocol) diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index fd09f8e8..26199208 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -307,32 +307,33 @@ function TCP:ssl_connect(domain, port) if not self.ssl_ctx or not self.ssl then return Log:ERROR("Create a SSL Error! :) ") end + local co = co_self() self.CONNECT_IO = tcp_pop() self.connect_current_co = co self.connect_co = co_new(function () - local EVENTS = EVENT_WRITE - while 1 do - local ok, EVENT = tcp_ssl_connect(self.ssl) - if ok then - if self.timer then - self.timer:stop() - self.timer = nil - end - tcp_push(self.CONNECT_IO) - tcp_stop(self.CONNECT_IO) - self.CONNECT_IO = nil - self.connect_co = nil - self.connect_current_co = nil - return co_wakeup(co, ok) - end - if EVENTS ~= EVENT then - EVENTS = EVENT - tcp_stop(self.CONNECT_IO) - tcp_start(self.CONNECT_IO, self.fd, EVENTS, self.connect_co) + local EVENTS = EVENT_WRITE + while 1 do + local ok, EVENT = tcp_ssl_connect(self.ssl) + if ok or not EVENT then + if self.timer then + self.timer:stop() + self.timer = nil end - co_wait() - end + tcp_push(self.CONNECT_IO) + tcp_stop(self.CONNECT_IO) + self.CONNECT_IO = nil + self.connect_co = nil + self.connect_current_co = nil + return co_wakeup(co, ok) + end + if EVENTS ~= EVENT then + EVENTS = EVENT + tcp_stop(self.CONNECT_IO) + tcp_start(self.CONNECT_IO, self.fd, EVENTS, self.connect_co) + end + co_wait() + end end) self.timer = ti.timeout(self._timeout, function ( ... ) tcp_push(self.CONNECT_IO) @@ -360,21 +361,21 @@ function TCP:close() if self.READ_IO then tcp_stop(self.READ_IO) - tcp_pop(self.READ_IO) + tcp_push(self.READ_IO) self.READ_IO = nil self.read_co = nil end if self.SEND_IO then tcp_stop(self.SEND_IO) - tcp_pop(self.SEND_IO) + tcp_push(self.SEND_IO) self.SEND_IO = nil self.send_co = nil end if self.CONNECT_IO then tcp_stop(self.CONNECT_IO) - tcp_pop(self.CONNECT_IO) + tcp_push(self.CONNECT_IO) self.CONNECT_IO = nil self.connect_co = nil end From 0278efdfcc406431e39da7c121b6c6ae12af109a Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 27 May 2019 05:32:31 +0800 Subject: [PATCH 082/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/main.lua | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/script/main.lua b/script/main.lua index 731a876e..54195ae2 100644 --- a/script/main.lua +++ b/script/main.lua @@ -1,4 +1,5 @@ local httpd = require "httpd" +local httpc = require "httpc" local DB = require "DB" --[[ @@ -28,6 +29,19 @@ app:enable_cookie() app:cookie_secure("https://github.com/CandyMi/core_framework") -- app:cookie_secure("candymi") + +app:ws('/ws', require "ws") + +app:api('/api', function (content) + local code, response = httpc.get("https://www.sojson.com/api/gongan/www.baidu.com") + return code == 200 and response or "httc请求失败" +end) + +app:use('/view', function (content) + return "

                  cfadmin v0.3

                  " +end) + + -- 导入cf内置的admin库 local cfadmin = require "admin" @@ -56,18 +70,6 @@ view.api('/api/admin/test2', function (ctx, db) return '{"code":0,"msg":"hello world"}' end) -app:ws('/ws', require "ws") - -app:api('/api', function (content) - require('logging'):new():DEBUG(content.args or content.body) - return '{"code":200}' -end) - -app:use('/view', function (content) - require('logging'):new():DEBUG(content.files) - return '

                  cfadmin v0.3

                  ' -end) - -- 这里是设置语言的地方 -- 语言表在admin/locales内, 可参照key -> value进行填写. -- 传入一个数组表: 索引1是key, 索引2为显示内容. From 8f51ccdedef4066da8839565b3d68305c4ba8928 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 27 May 2019 17:10:33 +0800 Subject: [PATCH 083/956] =?UTF-8?q?=E8=B0=83=E6=95=B4admin=E5=86=85?= =?UTF-8?q?=E9=83=A8=E7=9A=84before=E9=AA=8C=E8=AF=81=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/init.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lualib/admin/init.lua b/lualib/admin/init.lua index f7f99136..2e8a1872 100644 --- a/lualib/admin/init.lua +++ b/lualib/admin/init.lua @@ -64,13 +64,13 @@ function admin.init_page (app, db) app:before(function (content) local path = get_path(content) if find(path, '^/api/admin/.+') then -- 用于所有/api接口需要传递token header进行验权. - local token = content['headers']['token'] + local token = content['headers']['token'] or content['headers']['Token'] if not token then - return http.throw(401, "Can't Find Token") + return http.throw(401, '{"code":401,"msg":"Token was not exists"}') end local exists = user_token.token_exists(db, token) if not exists then - return http.throw(403, "Token Was not Exists") + return http.throw(403, '{"code":401,"msg":"Token was verify failed"}') end end return http.ok() From 6217ad01c789a4d219e8362401f47dd111a9175d Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 27 May 2019 22:32:54 +0800 Subject: [PATCH 084/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dwss=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=E7=9A=84=E7=AB=AF=E5=8F=A3=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/websocket/client.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lualib/protocol/websocket/client.lua b/lualib/protocol/websocket/client.lua index f0b5af74..f3629b74 100644 --- a/lualib/protocol/websocket/client.lua +++ b/lualib/protocol/websocket/client.lua @@ -166,9 +166,12 @@ local function url_split (self) port = toint(port) if not port then port = 80 + if scheme == 'wss' then + port = 443 + end end else - domain, port = domain_port, 80 + domain, port = domain_port, scheme == 'ws' and 80 or 443 end -- 判断是否需要ssl socket From 9dead0fa31a3ebdfa929969840861bde1aab176a Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 28 May 2019 17:12:24 +0800 Subject: [PATCH 085/956] =?UTF-8?q?=E4=BC=98=E5=8C=96dns=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltimer.c | 12 ++++---- luaclib/src/ludp.c | 8 +++--- lualib/internal/UDP.lua | 2 +- lualib/protocol/dns.lua | 61 +++++++++++++++++++++++++---------------- 4 files changed, 48 insertions(+), 35 deletions(-) diff --git a/luaclib/src/ltimer.c b/luaclib/src/ltimer.c index 71c0c3d8..e6b500bd 100644 --- a/luaclib/src/ltimer.c +++ b/luaclib/src/ltimer.c @@ -8,17 +8,17 @@ TIMEOUT_CB(CORE_P_ core_timer *timer, int revents){ if (revents & EV_TIMER){ - lua_State *co = (lua_State *) core_get_watcher_userdata(timer); + lua_State *co = (lua_State *) core_get_watcher_userdata(timer); - int status = lua_resume(co, NULL, lua_gettop(co) > 0 ? lua_gettop(co) - 1 : 0); + int status = lua_resume(co, NULL, lua_gettop(co) > 0 ? lua_gettop(co) - 1 : 0); - if (status != LUA_OK && status != LUA_YIELD){ + if (status != LUA_OK && status != LUA_YIELD){ - LOG( "ERROR", lua_tostring(co, -1)); + LOG( "ERROR", lua_tostring(co, -1)); - core_timer_stop(CORE_LOOP_ timer); + core_timer_stop(CORE_LOOP_ timer); - } + } } } diff --git a/luaclib/src/ludp.c b/luaclib/src/ludp.c index 14be3707..29145e6f 100644 --- a/luaclib/src/ludp.c +++ b/luaclib/src/ludp.c @@ -13,9 +13,9 @@ udp_socket_new(const char *ipaddr, int port){ /* 设置非阻塞 */ non_blocking(sockfd); - int ENABLE = 1; - /* 端口重用 */ - setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &ENABLE, sizeof(ENABLE)); + int ENABLE = 1; + /* 端口重用 */ + setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &ENABLE, sizeof(ENABLE)); struct sockaddr_in6 SA; SA.sin6_family = AF_INET6; @@ -191,4 +191,4 @@ luaopen_udp(lua_State *L){ luaL_setfuncs(L, udp_libs, 0); luaL_newlib(L, udp_libs); return 1; -} \ No newline at end of file +} diff --git a/lualib/internal/UDP.lua b/lualib/internal/UDP.lua index f0926da3..ed66586e 100644 --- a/lualib/internal/UDP.lua +++ b/lualib/internal/UDP.lua @@ -62,7 +62,7 @@ function UDP:recv(...) end function UDP:send(data) - assert(not self.udp or self.fd or self.fd < 0, "UDP ERROR 参数不完整.") + assert(self.udp and data and self.fd and self.fd > 0, "UDP ERROR 参数不完整.") return udp.send(self.fd, data, #data) end diff --git a/lualib/protocol/dns.lua b/lualib/protocol/dns.lua index 23b49820..93b59062 100644 --- a/lualib/protocol/dns.lua +++ b/lualib/protocol/dns.lua @@ -72,25 +72,27 @@ end local function gen_cache() local file = io.open("/etc/hosts", "r") - dns_cache['localhost'] = {ip = '127.0.0.1'} if file then - for line in file:lines() do - local ip, domain = match(line, '([^# %t]*)[%t ]*(.*)$') - local ok, v = check_ip(ip) - if ok then - if v == 4 then - if not dns_cache[domain] then - dns_cache[domain] = {ip = prefix..ip} - end - else - if not dns_cache[domain] then - dns_cache[domain] = {ip = ip} - end + for line in file:lines() do + local ip, domain = match(line, '^([^#%t ]*)[ ]+(.+)$') + local ok, v = check_ip(ip) + if ok then + domain = match(domain, '([^ ]+)') or domain + if v == 4 then + if not dns_cache[domain] then + dns_cache[domain] = {ip = prefix..ip} end end + if v == 6 then + if not dns_cache[domain] then + dns_cache[domain] = {ip = ip} + end + end + end end file:close() end + dns_cache['localhost'] = {ip = prefix..'127.0.0.1'} end if #dns_list < 1 then @@ -209,24 +211,26 @@ local function dns_query(domain) cos[domain] = wlist -- local start = os.time() + os.clock() -- print("开始解析域名:["..domain.."], 开始时间: ", start) - local dns_resp, len + local dns_client, msg = get_dns_client() if not dns_client then dns_client:close() return nil, msg end - local header = pack_header() - local question = pack_question(domain, msg) + -- local header = pack_header() + -- local question = pack_question(domain, msg) + local req = pack_header() .. pack_question(domain, msg) + local dns_resp, len while 1 do -- 如果一直没收到回应将会反复请求 - dns_client:send(header..question) + dns_client:send(req) dns_resp, len = dns_client:recv() if dns_resp then - dns_client:close() - dns_client = nil - break + dns_client:close() + dns_client = nil + break end if type(len) == "string" then - Log:INFO("正在解析["..domain.."]:"..len) + Log:WARN("正在解析["..domain.."]:"..len) end end if not len or len < LIMIT_HEADER_LEN then @@ -250,12 +254,20 @@ local function dns_query(domain) end local answer for i = 1, answer_header.ancount do - answer, nbyte = unpack_answer(dns_resp, nbyte) - if answer.atype == QTYPE.A or answer.atype == QTYPE.AAAA then - answer.ip = unpack_rdata(answer.rdata, answer.atype) + answer, nbyte = unpack_answer(dns_resp, nbyte) + if answer.atype == QTYPE.A or answer.atype == QTYPE.AAAA then + answer.ip = unpack_rdata(answer.rdata, answer.atype) + local cache = dns_cache[domain] + if not cache then + dns_cache[domain] = {ip = answer.ip, ttl = now() + answer.ttl} + else + if cache.ttl and cache.ttl < now() + answer.ttl then dns_cache[domain] = {ip = answer.ip, ttl = now() + answer.ttl} + end end + end end + local ok, v = check_ip(answer.ip) if not ok then return nil, "unknown ip in this domain: "..domain @@ -316,4 +328,5 @@ function dns.resolve(domain) end -- require "utils" -- var_dump(dns_cache) +-- var_dump(dns_list) return dns From 3477b9e5abe4442a35ee963a561410d4bcf7e197 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 29 May 2019 20:12:34 +0800 Subject: [PATCH 086/956] =?UTF-8?q?=E8=B0=83=E6=95=B4Dockerfile=E7=9A=84?= =?UTF-8?q?=E9=A1=BA=E5=BA=8F,=20=E4=BC=98=E5=8C=96=E6=9E=84=E5=BB=BA?= =?UTF-8?q?=E9=80=9F=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/Dockerfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 5ee3c335..48c53562 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -10,12 +10,13 @@ WORKDIR /root/download COPY ./lua-5.3.5.tar.gz /root/download/lua.tar.gz COPY ./libev-4.25.tar.gz /root/download/libev.tar.gz -RUN yum groupinstall "development tools" -y && yum install autoconf git readline readline-devel openssl openssl-devel -y \ +RUN yum install nc vim autoconf git readline-devel openssl-devel -y \ + && yum groupinstall "development tools" -y \ && tar zxvf lua.tar.gz && cd lua* && make linux MYCFLAGS=-fPIC && make install && cd .. \ && tar zxvf libev.tar.gz && cd libev* && ./configure --prefix=/usr/local && make && make install \ && rm -rf /roo/download /var/cache/yum \ && git clone https://github.com/CandyMi/core_framework /app \ - && cd /app && make rebuild + && cd /app && make build # 使用者可在启动容器时使用-v命令将您的代码目录直接挂载到/app/script目录进行调试操作 WORKDIR /app From aa4d2879b30b988ce66086f10ad2691e38a1c0f7 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 29 May 2019 21:08:26 +0800 Subject: [PATCH 087/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=E9=85=8D=E7=BD=AE,=20=E6=94=AF=E6=8C=813rd=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core.c b/src/core.c index 8a672c60..e562ac14 100644 --- a/src/core.c +++ b/src/core.c @@ -214,10 +214,10 @@ init_lua_libs(lua_State *L){ /* 注入lua搜索域 */ lua_getglobal(L, "package"); - lua_pushliteral(L, "lualib/?.lua;lualib/?/init.lua;script/?.lua;script/?/init.lua;"); + lua_pushliteral(L, "lualib/?.lua;lualib/?/init.lua;./?.lua;./?/init.lua;script/?.lua;script/?/init.lua;"); lua_setfield(L, 1, "path"); - lua_pushliteral(L, "luaclib/?.so;luaclib/?/init.so;"); + lua_pushliteral(L, "luaclib/?.so;luaclib/?/init.so;./?.so;./?/init.so;"); lua_setfield(L, 1, "cpath"); lua_settop(L, 0); From 5acbdd6dff408844b2d61e6cb54c14a00f78b9ac Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 29 May 2019 21:09:31 +0800 Subject: [PATCH 088/956] =?UTF-8?q?=E8=B0=83=E6=95=B4Makefile=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=BC=80=E5=8F=91=E8=80=85=E8=87=AA=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 16 ++++++++++------ luaclib/Makefile | 5 +++-- luaclib/src/lcjson/Makefile | 1 + luaclib/src/lhttpparser/Makefile | 3 ++- luaclib/src/lpeg/makefile | 1 + 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 2f0a5c65..99e589f0 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,9 @@ -.PHONY : build clean +.PHONY : build rebuild clean default : @echo "=======================================" @echo "Please use 'make build' command to build it.." + @echo "Please use 'make rebuild' command to build it.." @echo "Please use 'make clean' command to clean all." @echo "=======================================" @@ -12,15 +13,18 @@ default : # 3. lualib/src/cjson/Makefile build : - cd src && rm -rf *.so *.o && make build - cd luaclib && rm -rf *.so *.o && make build + cd src && make build + cd luaclib && make build + cd 3rd && make build rebuild : rm -rf main cfadmin *.so - cd src && rm -rf *.so *.o && make rebuild - cd luaclib && rm -rf *.so *.o && make rebuild + cd src && make clean && make rebuild + cd luaclib && make clean && make rebuild + cd 3rd && make clean && make rebuild clean : rm -rf main cfadmin *.so cd src && make clean - cd luaclib && make clean \ No newline at end of file + cd luaclib && make clean + cd 3rd && make clean diff --git a/luaclib/Makefile b/luaclib/Makefile index 72c96936..8ab8f2ba 100644 --- a/luaclib/Makefile +++ b/luaclib/Makefile @@ -3,6 +3,7 @@ default : @echo "=======================================" @echo "Please use 'make build' command to build it.." + @echo "Please use 'make rebuild' command to build it.." @echo "Please use 'make clean' command to clean all." @echo "=======================================" @@ -20,7 +21,7 @@ build : $(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore $(CC) -o task.so src/ltask.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore $(CC) -o lcrypt.so src/lcrypt.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore - ### 以下为预留第三方库编译位置 ### + ### 以下为内置第三方库编译位置 ### cd src/lhttpparser && rm -rf *.o *.so && make build # 增加PicoHTTPParser库 cd src/lcjson && rm -rf *.o *.so && make build # 增加cjson库 cd src/lpeg && rm -rf *.o *.so && make build # 增加lpeg库 @@ -33,7 +34,7 @@ rebuild : $(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore $(CC) -o task.so src/ltask.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore $(CC) -o lcrypt.so src/lcrypt.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore - ### 以下为预留第三方库编译位置 ### + ### 以下为内置第三方库编译位置 ### cd src/lhttpparser && rm -rf *.o *.so && make rebuild # 增加PicoHTTPParser库 cd src/lcjson && rm -rf *.o *.so && make rebuild # 增加cjson库 cd src/lpeg && rm -rf *.o *.so && make rebuild # 增加lpeg库 diff --git a/luaclib/src/lcjson/Makefile b/luaclib/src/lcjson/Makefile index 9e7d67ec..d6efdca5 100644 --- a/luaclib/src/lcjson/Makefile +++ b/luaclib/src/lcjson/Makefile @@ -3,6 +3,7 @@ default : @echo "=======================================" @echo "Please use 'make build' command to build it.." + @echo "Please use 'make rebuild' command to build it.." @echo "Please use 'make clean' command to clean all." @echo "=======================================" diff --git a/luaclib/src/lhttpparser/Makefile b/luaclib/src/lhttpparser/Makefile index 10b9bd18..69dac294 100644 --- a/luaclib/src/lhttpparser/Makefile +++ b/luaclib/src/lhttpparser/Makefile @@ -3,6 +3,7 @@ default : @echo "=======================================" @echo "Please use 'make build' command to build it.." + @echo "Please use 'make rebuild' command to build it.." @echo "Please use 'make clean' command to clean all." @echo "=======================================" @@ -12,7 +13,7 @@ CC = cc # 这里为了兼容AMD CPU不作为默认开启选项 # CFLAGS = -O3 -Wall -DNDEBUG -fPIC -msse4 -CFLAGS = -O3 -Wall -DNDEBUG -fPIC +CFLAGS = -O3 -Wall -DNDEBUG -fPIC INCLUDE = -I/usr/local/include LIB = -L/usr/local/lib diff --git a/luaclib/src/lpeg/makefile b/luaclib/src/lpeg/makefile index adc2b3b6..da23cafc 100644 --- a/luaclib/src/lpeg/makefile +++ b/luaclib/src/lpeg/makefile @@ -3,6 +3,7 @@ default : @echo "=======================================" @echo "Please use 'make build' command to build it.." + @echo "Please use 'make rebuild' command to build it.." @echo "Please use 'make clean' command to clean all." @echo "=======================================" From a08523e9ef034531555ef6514ddaa8e0d49af8c3 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 29 May 2019 21:11:52 +0800 Subject: [PATCH 089/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A03rd=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E7=9B=AE=E5=BD=95,=20=E7=94=A8=E4=BA=8E=E5=BC=80?= =?UTF-8?q?=E5=8F=91=E8=80=85=E8=87=AA=E5=AE=9A=E4=B9=89=E7=9A=84=E5=BA=93?= =?UTF-8?q?=E5=B9=B6=E5=9C=A8=E5=86=85=E9=83=A8=E9=99=84=E4=B8=8AREADME?= =?UTF-8?q?=E9=99=84=E4=B8=8A=E4=BD=BF=E7=94=A8=E6=96=B9=E6=B3=95.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rd/Makefile | 20 +++++++++++++++++++ 3rd/README.md | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 3rd/Makefile create mode 100644 3rd/README.md diff --git a/3rd/Makefile b/3rd/Makefile new file mode 100644 index 00000000..bd7fc3fa --- /dev/null +++ b/3rd/Makefile @@ -0,0 +1,20 @@ +.PHONY : build rebuild clean + +default : + @echo "=======================================" + @echo "Please use 'make build' command to build it.." + @echo "Please use 'make rebuild' command to build it.." + @echo "Please use 'make clean' command to clean all." + @echo "=======================================" + +# 当您使用Make build、rebuild、clean时, 这里会被调用. +# 所以, 您应该定义一套合理的脚本用于3rd内的库进行维护. + +build: + @echo "Done." + +rebuild: + @echo "Done." + +clean: + @echo "Done." diff --git a/3rd/README.md b/3rd/README.md new file mode 100644 index 00000000..07dab181 --- /dev/null +++ b/3rd/README.md @@ -0,0 +1,55 @@ +## 用户自定义库目录 + + 此目录是用户自定义目录, cf开发者不会对3rd内部文件进行任何意义上的修改, 3rd内组织方式需要用户自行确定. + + 在使用者编译cf的make build, make rebuild, make clean等命令的时候将会同时传入到3rd的Makefile内. + + 3rd也为用户自定义库提供整合方式或联合编译(如有需要), 使用者在开发阶段可能会需要自己编写一套业务维护库. + + 3rd的库维护者应该(至少)维护上述三个编译命令并使其正常工作. 同时, 维护者也需要至少保证以下两点: + + 1. 3rd库的维护者(至少)需要保证引用名唯一性; + + 2. 3rd库的维护者(至少)需要保证无(除cf)的底层特殊依赖性; + + 注: 在无需编译的情况下, 使用者可以讲makefile看做一套自定义库代码整理集合. 有助于用户自行组织库目录. + +## 如何在3rd维护自己的lua库? + + 1. 将文件copy到3rd目录下(这里假设直接copy到3rd根目录, 当然也可以自行构建目录结构) + + 2. 在main.lu文件内使用```local lib = require "3rd.you_lib_name"``` + + 3. 开始使用. + +## 如何在3rd维护自己的lua C库? + + 1. 按照lua C API开发模式开发完毕(可参考luaclib内的文件). + + 2. 将源码copy到3rd目录下, 并在修改```3rd/Makefile```进行进行联合编译. + + 3. 编译完成之后, 执行自己定义的脚本整理文件与路径. + + 4. 在main.lu文件内使用```local lib = require "3rd.you_lib_name"``` + + 5. 开始使用. + +## 最简单3rd用户库编写实例 + + 首先, 在3rd目录下新建一个名为```printer.lua```的文件, 其内容如下: + + ```lua + return function (...) + return print(...) + end + ``` + + 然后, 清空```script/main.lua```内的所有内容, 然后输入以下内容: + + ```lua + local printer = require "3rd.printer" + + printer("这是我在3rd内编写的库") + ``` + + 最后, 使用```./cfadmin``` 命令运行并查看效果. From 83f7c7ea18f36873343872156e9a60e53728a5b9 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 30 May 2019 02:52:57 +0800 Subject: [PATCH 090/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=9B=B4=E6=96=B9=E4=BE=BF=E6=B5=8B=E8=AF=95=E7=9A=84=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/main.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/main.lua b/script/main.lua index 54195ae2..23545ab3 100644 --- a/script/main.lua +++ b/script/main.lua @@ -33,7 +33,7 @@ app:cookie_secure("https://github.com/CandyMi/core_framework") app:ws('/ws', require "ws") app:api('/api', function (content) - local code, response = httpc.get("https://www.sojson.com/api/gongan/www.baidu.com") + local code, response = httpc.get("https://www.baifubao.com/callback?cmd=1059&callback=phone&phone=13000000000") return code == 200 and response or "httc请求失败" end) From 2771c32c14931bc226136639e6d2d07382193909 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 30 May 2019 02:54:45 +0800 Subject: [PATCH 091/956] =?UTF-8?q?=E4=BC=98=E5=8C=96dns=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0,=20=E8=A7=A3=E5=86=B3=E9=A2=91=E7=B9=81?= =?UTF-8?q?=E4=B8=A2=E5=A4=B1udp=E6=B6=88=E6=81=AF=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/dns.lua | 166 +++++++++++++++++++++------------------ script/test_dns.lua | 170 +++++++++++++++++++++++++++++++--------- 2 files changed, 225 insertions(+), 111 deletions(-) diff --git a/lualib/protocol/dns.lua b/lualib/protocol/dns.lua index 93b59062..a39cdf74 100644 --- a/lualib/protocol/dns.lua +++ b/lualib/protocol/dns.lua @@ -1,14 +1,15 @@ local UDP = require "internal.UDP" -local co = require "internal.Co" + local sys = require "sys" local log = require "logging" local Log = log:new({ dump = true, path = 'protocol-dns'}) -local prefix = '::ffff:' - -local co_self = co.self -local co_wait = co.wait -local co_wakeup = co.wakeup +local cf = require "cf" +local cf_fork = cf.fork +local cf_self = cf.self +local cf_wait = cf.wait +local cf_wakeup = cf.wakeup +local cf_sleep = cf.sleep local check_ipv4 = sys.ipv4 local check_ipv6 = sys.ipv6 @@ -25,7 +26,11 @@ local match = string.match local splite = string.gmatch local pack = string.pack local unpack = string.unpack + local type = type +local ipairs = ipairs + +local prefix = '::ffff:' local dns = {} @@ -98,34 +103,34 @@ end if #dns_list < 1 then local file = io.open("/etc/resolv.conf", "r") if file then - for line in file:lines() do - local ip = match(line, "nameserver (.-)$") - local ok = check_ip(ip) - if ok then - insert(dns_list, ip) - end + for line in file:lines() do + local ip = match(line, "nameserver (.-)$") + local ok = check_ip(ip) + if ok then + insert(dns_list, ip) end - file:close() + end + file:close() end if #dns_list < 1 then - dns_list = {"114.114.114.114", "8.8.8.8"} + dns_list = {"114.114.114.114", "8.8.8.8"} end gen_cache() end local function get_dns_client() if #dns_list >= 1 then - local ip = dns_list[1] - local udp = UDP:new():timeout(dns._timeout or 5) - local ok, v = check_ip(ip) - if v == 4 then - ip = prefix..ip - end - local ok = udp:connect(ip, 53) - if not ok then - return nil, 'Create UDP Socket error.' - end - return udp, v + local ip = dns_list[1] + local udp = UDP:new():timeout(dns._timeout or 30) + local ok, v = check_ip(ip) + if v == 4 then + ip = prefix..ip + end + local ok = udp:connect(ip, 53) + if not ok then + return nil, 'Create UDP Socket error.' + end + return udp, v end return nil, "Can't find system dns in /etc/resolve.conf." end @@ -166,20 +171,20 @@ local function unpack_name(chunk, nbyte) local jump_pointer local tag, offset, label while true do - tag, nbyte = unpack(">B", chunk, nbyte) - if tag & 0xc0 == 0xc0 then - offset,nbyte = unpack(">H", chunk, nbyte - 1) - offset = offset & 0x3fff - if not jump_pointer then - jump_pointer = nbyte - end - nbyte = offset + 1 - elseif tag == 0 then - break - else - label, nbyte = unpack(">s1", chunk, nbyte - 1) - domain[#domain+1] = label + tag, nbyte = unpack(">B", chunk, nbyte) + if tag & 0xc0 == 0xc0 then + offset,nbyte = unpack(">H", chunk, nbyte - 1) + offset = offset & 0x3fff + if not jump_pointer then + jump_pointer = nbyte end + nbyte = offset + 1 + elseif tag == 0 then + break + else + label, nbyte = unpack(">s1", chunk, nbyte - 1) + domain[#domain+1] = label + end end return concat(domain, "."), jump_pointer or nbyte end @@ -205,84 +210,93 @@ end local cos = {} +-- 如果有其它协程也在等待查询, 那么一起唤醒它们 +local function check_wait(domain, wlist, ...) + for _, w in ipairs(wlist) do + cf_wakeup(w, ...) + end + cos[domain] = nil +end + + local function dns_query(domain) local wlist = {} cos[domain] = wlist -- local start = os.time() + os.clock() -- print("开始解析域名:["..domain.."], 开始时间: ", start) - local dns_client, msg = get_dns_client() if not dns_client then dns_client:close() + check_wait(domain, wlist, nil, msg) return nil, msg end - -- local header = pack_header() - -- local question = pack_question(domain, msg) - local req = pack_header() .. pack_question(domain, msg) - local dns_resp, len - while 1 do -- 如果一直没收到回应将会反复请求 + local dns_resp, len, readable + cf_fork(function () + local req = pack_header()..pack_question(domain, msg) + local times = 1 + while 1 do dns_client:send(req) - dns_resp, len = dns_client:recv() - if dns_resp then - dns_client:close() - dns_client = nil - break + cf_sleep(1) + if readable then + return end - if type(len) == "string" then - Log:WARN("正在解析["..domain.."]:"..len) - end - end - if not len or len < LIMIT_HEADER_LEN then - local err = "Malformed dns response package." - return nil, err + Log:WARN("第"..times.."次尝试解析["..domain.."]:") + times = times + 1 + end + end) + dns_resp, len = dns_client:recv() + dns_client:close() + readable = true + if not dns_resp or not len or len < LIMIT_HEADER_LEN then + local err = "1. dns解析失败." + check_wait(domain, wlist, nil, err) + return nil, err end local answer_header, nbyte = unpack_header(dns_resp) if answer_header.qdcount ~= 1 then - local err = "Malformed dns response package." + local err = "2. 错误的DNS回应." + check_wait(domain, wlist, nil, err) return nil, err end if not answer_header.ancount or answer_header.ancount < 1 then - local err = "Can't find ip addr in nameserver." + local err = "3. 无法解析的域名." + check_wait(domain, wlist, nil, err) return nil, err end local question, nbyte = unpack_question(dns_resp, nbyte) if question.name ~= domain then - local err = "quetions not equal." - return nil, err + local err = "4. 回应域名与请求解析的域名不一致." + check_wait(domain, wlist, nil, err) + return nil, err end local answer + local t = now() for i = 1, answer_header.ancount do answer, nbyte = unpack_answer(dns_resp, nbyte) if answer.atype == QTYPE.A or answer.atype == QTYPE.AAAA then answer.ip = unpack_rdata(answer.rdata, answer.atype) local cache = dns_cache[domain] if not cache then - dns_cache[domain] = {ip = answer.ip, ttl = now() + answer.ttl} + dns_cache[domain] = {ip = answer.ip, ttl = t + answer.ttl} else - if cache.ttl and cache.ttl < now() + answer.ttl then - dns_cache[domain] = {ip = answer.ip, ttl = now() + answer.ttl} + if cache.ttl and cache.ttl < t + answer.ttl then + dns_cache[domain] = {ip = answer.ip, ttl = t + answer.ttl} end end end end - local ok, v = check_ip(answer.ip) if not ok then - return nil, "unknown ip in this domain: "..domain + local err = "5. 未知的IP地址." + check_wait(domain, wlist, nil, err..domain) + return nil, err..domain end if ok and v == 4 then - answer.ip = prefix..answer.ip - end - -- local e_n_d = os.time() + os.clock() - -- print("解析域名["..domain.."]完成, 结束时间:", e_n_d, ip, answer.ttl) - -- print("解析域名用时: ", tostring(e_n_d - start)..'s') - for i = #wlist, 1, -1 do - -- 如果有其它协程也在等待查询, 那么一起唤醒它们 - co_wakeup(wlist[i], true, answer.ip) + answer.ip = prefix..answer.ip end - cos[domain] = nil + check_wait(domain, wlist, true, answer.ip) return true, answer.ip end @@ -320,9 +334,9 @@ function dns.resolve(domain) -- 如果有其他协程也正巧在查询这个域名, 那么就加入到等待列表内 local wlist = cos[domain] if wlist then - local co = co_self() + local co = cf_self() insert(wlist, co) - return co_wait() + return cf_wait() end return dns_query(domain) end diff --git a/script/test_dns.lua b/script/test_dns.lua index f36f3263..f892adae 100644 --- a/script/test_dns.lua +++ b/script/test_dns.lua @@ -1,37 +1,137 @@ + local dns = require "protocol.dns" +local dns_resolve = dns.resolve + local cf = require "cf" -cf.fork(function ( ... ) - print("申请解析 1") - local ok, ip = dns.resolve('www.baidu.com') - print("解析完成 1", ok, ip) -end) - -cf.fork(function ( ... ) - print("申请解析 2") - local ok, ip = dns.resolve('www.baidu.com') - print("解析完成 2", ok, ip) -end) - -cf.fork(function ( ... ) - print("申请解析 3") - local ok, ip = dns.resolve('www.baidu.com') - print("解析完成 3", ok, ip) -end) - -cf.fork(function ( ... ) - print("申请解析 11") - local ok, ip = dns.resolve('www.qq.com') - print("解析完成 11", ok, ip) -end) - -cf.fork(function ( ... ) - print("申请解析 12") - local ok, ip = dns.resolve('www.qq.com') - print("解析完成 12", ok, ip) -end) - -cf.fork(function ( ... ) - print("申请解析 13") - local ok, ip = dns.resolve('www.qq.com') - print("解析完成 13", ok, ip) -end) + +local system = require "system" +local now = system.now +local fmt = string.format + +-- 百度 +for i = 1, 10 do + cf.fork(function ( ... ) + local t1 = now() + local domain = 'www.baidu.com' + if i == 1 then + print("开始解析 :", domain, "时间:", fmt("%0.8f/Sec", t1)) + end + local ok, ip = dns_resolve(domain) + local t2 = now() + if i == 10 then + print("结束解析 :", domain, "时间:", fmt("%0.8f/Sec", t2), "耗时:", fmt("%0.8f/Sec", t2 - t1)) + end + end) +end + +-- 腾讯 +for i = 1, 10 do + cf.fork(function ( ... ) + local t1 = now() + local domain = 'www.qq.com' + if i == 1 then + print("开始解析 :", domain, "时间:", fmt("%0.8f/Sec", t1)) + end + local ok, ip = dns_resolve(domain) + local t2 = now() + if i == 10 then + print("结束解析 :", domain, "时间:", fmt("%0.8f/Sec", t2), "耗时:", fmt("%0.8f/Sec", t2 - t1)) + end + end) +end + +-- 淘宝 +for i = 1, 10 do + cf.fork(function ( ... ) + local t1 = now() + local domain = 'www.taobao.com' + if i == 1 then + print("开始解析 :", domain, "时间:", fmt("%0.8f/Sec", t1)) + end + local ok, ip = dns_resolve(domain) + local t2 = now() + if i == 10 then + print("结束解析 :", domain, "时间:", fmt("%0.8f/Sec", t2), "耗时:", fmt("%0.8f/Sec", t2 - t1)) + end + end) +end + +-- 谷歌 +for i = 1, 10 do + cf.fork(function ( ... ) + local t1 = now() + local domain = 'www.google.com' + if i == 1 then + print("开始解析 :", domain, "时间:", fmt("%0.8f/Sec", t1)) + end + local ok, ip = dns_resolve(domain) + local t2 = now() + if i == 10 then + print("结束解析 :", domain, "时间:", fmt("%0.8f/Sec", t2), "耗时:", fmt("%0.8f/Sec", t2 - t1)) + end + end) +end + +-- 网易 +for i = 1, 10 do + cf.fork(function ( ... ) + local t1 = now() + local domain = 'www.163.com' + if i == 1 then + print("开始解析 :", domain, "时间:", fmt("%0.8f/Sec", t1)) + end + local ok, ip = dns_resolve(domain) + local t2 = now() + if i == 10 then + print("结束解析 :", domain, "时间:", fmt("%0.8f/Sec", t2), "耗时:", fmt("%0.8f/Sec", t2 - t1)) + end + end) +end + +-- 京东 +for i = 1, 10 do + cf.fork(function ( ... ) + local t1 = now() + local domain = 'www.jd.com' + if i == 1 then + print("开始解析 :", domain, "时间:", fmt("%0.8f/Sec", t1)) + end + local ok, ip = dns_resolve(domain) + local t2 = now() + if i == 10 then + print("结束解析 :", domain, "时间:", fmt("%0.8f/Sec", t2), "耗时:", fmt("%0.8f/Sec", t2 - t1)) + end + end) +end + +-- 唯品会 +for i = 1, 10 do + cf.fork(function ( ... ) + local t1 = now() + local domain = 'www.vip.com' + if i == 1 then + print("开始解析 :", domain, "时间:", fmt("%0.8f/Sec", t1)) + end + local ok, ip = dns_resolve(domain) + local t2 = now() + if i == 10 then + print("结束解析 :", domain, "时间:", fmt("%0.8f/Sec", t2), "耗时:", fmt("%0.8f/Sec", t2 - t1)) + end + end) +end + +-- 盛大 +for i = 1, 10 do + cf.fork(function ( ... ) + local t1 = now() + local domain = 'www.sdo.com' + if i == 1 then + print("开始解析 :", domain, "时间:", fmt("%0.8f/Sec", t1)) + end + local ok, ip = dns_resolve(domain) + local t2 = now() + if i == 10 then + print("结束解析 :", domain, "时间:", fmt("%0.8f/Sec", t2), "耗时:", fmt("%0.8f/Sec", t2 - t1)) + end + end) +end From 574c206d2ef2f959f0ea6dba718f722b61ba4c0c Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 30 May 2019 14:14:24 +0800 Subject: [PATCH 092/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=88=A4=E6=96=ADCon?= =?UTF-8?q?tent-Type=E4=B8=BA=E7=A9=BA=E7=9A=84=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index d0726027..356351df 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -260,7 +260,7 @@ local function PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEA local XML_ENCODE_2 = 'application/xml' local JSON_ENCODE = 'application/json' local URL_ENCODE = 'application/x-www-form-urlencoded' - local format = match(HEADER['Content-Type'], '(.-/[^;]*)') + local format = match(HEADER['Content-type'] or HEADER['Content-Type'] or '', '(.-/[^;]*)') if format == FILE_ENCODE then local BOUNDARY = match(HEADER['Content-Type'], '^.+=[%-]*(.+)') if BOUNDARY and BOUNDARY ~= '' then From 6e237f085d3700c00676d941364a5c6ff379105a Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 30 May 2019 15:04:56 +0800 Subject: [PATCH 093/956] =?UTF-8?q?=E5=88=86=E7=A6=BBhttpc=E7=9A=84?= =?UTF-8?q?=E4=B8=9A=E5=8A=A1=E5=B0=81=E8=A3=85=E4=B8=8E=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E5=B0=81=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/init.lua | 400 ++++++-------------------------------- lualib/httpc/protocol.lua | 355 +++++++++++++++++++++++++++++++++ 2 files changed, 417 insertions(+), 338 deletions(-) create mode 100644 lualib/httpc/protocol.lua diff --git a/lualib/httpc/init.lua b/lualib/httpc/init.lua index 04d7da90..7ae3869d 100644 --- a/lualib/httpc/init.lua +++ b/lualib/httpc/init.lua @@ -1,10 +1,21 @@ -local class = require "class" -local tcp = require "internal.TCP" -local HTTP = require "protocol.http" +local class = require "httpc.class" + +local protocol = require "httpc.protocol" +local sock_new = protocol.sock_new +local sock_recv = protocol.sock_recv +local sock_send = protocol.sock_send +local sock_connect = protocol.sock_connect +local httpc_response = protocol.httpc_response +local splite_protocol = protocol.splite_protocol +local build_get_req = protocol.build_get_req +local build_post_req = protocol.build_post_req +local build_json_req = protocol.build_json_req +local build_file_req = protocol.build_file_req -local FILEMIME = HTTP.FILEMIME -local RESPONSE_PROTOCOL_PARSER = HTTP.RESPONSE_PROTOCOL_PARSER -local RESPONSE_HEADER_PARSER = HTTP.RESPONSE_HEADER_PARSER +local type = type +local assert = assert +local ipairs = ipairs +local tostring = tostring local random = math.random local find = string.find @@ -16,247 +27,31 @@ local lower = string.lower local insert = table.insert local concat = table.concat local toint = math.tointeger -local type = type -local assert = assert -local ipairs = ipairs -local tostring = tostring - -local CRLF = '\x0d\x0a' -local CRLF2 = '\x0d\x0a\x0d\x0a' - local fmt = string.format local SERVER = "cf/0.1" +local CRLF = '\x0d\x0a' +local CRLF2 = '\x0d\x0a\x0d\x0a' + local __TIMEOUT__ = 15 local httpc = {} -local function sock_recv (sock, PROTOCOL, byte) - if PROTOCOL == 'https' then - local data, len = sock:ssl_recv(byte) - if data then - return data, len - end - end - if PROTOCOL == 'http' then - local data, len = sock:recv(byte) - if data then - return data, len - end - end - return nil, '服务端断开了连接' -end - -local function sock_connect(sock, PROTOCOL, DOAMIN, PORT) - if PROTOCOL == 'https' then - local ok, err = sock:ssl_connect(DOAMIN, PORT) - if ok then - return true - end - end - if PROTOCOL == 'http' then - local ok, err = sock:connect(DOAMIN, PORT) - if ok then - return true - end - end - return nil, 'httpc连接失败.' -end - -local function sock_send(sock, PROTOCOL, DATA) - if PROTOCOL == 'https' then - local ok = sock:ssl_send(DATA) - if ok then - return true - end - end - - if PROTOCOL == 'http' then - local ok = sock:send(DATA) - if ok then - return true - end - end - return nil, "httpc发送请求失败" -end - - -local function httpc_response(sock, SSL) - if not sock then - return nil, "Can't used this method before other httpc method.." - end - local CODE, HEADER, BODY - local Content_Length - local content = {} - local times = 0 - while 1 do - local data, len = sock_recv(sock, SSL, 1024) - if not data then - sock:close() - return nil, "A peer of remote server close this connection." - end - insert(content, data) - local DATA = concat(content) - local posA, posB = find(DATA, CRLF2) - if posB then - CODE = RESPONSE_PROTOCOL_PARSER(split(DATA, 1, posB)) - HEADER = RESPONSE_HEADER_PARSER(split(DATA, 1, posB)) - if not CODE or not HEADER then - sock:close() - return nil, "can't resolvable protocol." - end - local Content_Length = toint(HEADER['Content-Length'] or HEADER['content-length']) - local chunked = HEADER['Transfer-Encoding'] - if not chunked and not Content_Length then - Content_Length = 0 - end - if Content_Length then - if (#DATA - posB) == Content_Length then - sock:close() - return CODE, split(DATA, posB+1, #DATA) - end - local content = {split(DATA, posB+1, #DATA)} - while 1 do - local data, len = sock_recv(sock, SSL, 1024) - if not data then - sock:close() - return CODE, SSL.."[Content_Length] A peer of remote server close this connection." - end - insert(content, data) - local DATA = concat(content) - if Content_Length == #DATA then - sock:close() - return CODE, DATA - end - end - end - if chunked and chunked == "chunked" then - local content = {} - if #DATA > posB then - local DATA = split(DATA, posB+1, #DATA) - if find(DATA, CRLF2) then - local body = {} - for hex, block in splite(DATA, "([%w]*)\r\n(.-)\r\n") do - local len = toint(fmt("0x%s", hex)) - if len and len == #block then - if len == 0 and block == '' then - sock:close() - return CODE, concat(body) - end - insert(body, block) - end - end - end - insert(content, DATA) - end - while 1 do - local data, len = sock_recv(sock, SSL, 1024) - if not data then - sock:close() - return CODE, SSL.."[chunked] A peer of remote server close this connection A." - end - insert(content, data) - local DATA = concat(content) - if find(DATA, CRLF2) then - local body = {} - for hex, block in splite(DATA, "([%a%d]*)\r\n(.-)\r\n") do - local len = toint(fmt("0x%s", hex)) - if len and len == #block then - if len == 0 and block == '' then - sock:close() - return CODE, concat(body) - end - insert(body, block) - end - end - end - end - end - end - end -end - --- 分割httpc domain -local function splite_protocol(domain) - if type(domain) ~= 'string' then - return nil, '1. 非法的域名' - end - - local protocol, domain_port, path = match(domain, '^(http[s]?)://([^/]+)(.*)') - if not protocol or not domain_port or not path then - return nil, '2. 错误的url' - end - - if not path or path == '' then - return nil, "3. http无path需要以'/'结尾." - end - - local domain, port - if find(domain_port, ':') then - local _, Bracket_Pos = find(domain_port, '[%[%]]') - if Bracket_Pos then - domain, port = match(domain_port, '%[(.+)%][:]?(%d*)') - else - domain, port = match(domain_port, '([^:]+):(%d*)') - end - if not domain then - return nil, "4. 无效或者非法的主机名: "..domain_port - end - port = toint(port) - if not port then - port = 80 - if protocol == 'https' then - port = 443 - end - end - else - domain, port = domain_port, protocol == 'https' and 443 or 80 - end - return { - protocol = protocol, - domain = domain, - port = port, - path = path, - } -end - - -- HTTP GET -function httpc.get(domain, HEADER, ARGS, TIMEOUT) - +function httpc.get(domain, headers, args, timeout) local opt, err = splite_protocol(domain) if not opt then return nil, err end - local request = { - fmt("GET %s HTTP/1.1", opt.path), - fmt("Host: %s", opt.domain..':'..opt.port), - 'Accept: */*', - 'Accept-Encoding: identity', - fmt("Connection: keep-alive"), - fmt("User-Agent: %s", SERVER), - } - if type(ARGS) == "table" then - local args = {} - for _, arg in ipairs(ARGS) do - assert(#arg == 2, "args need key[1]->value[2] (2 values)") - insert(args, arg[1]..'='..arg[2]) - end - request[1] = fmt("GET %s HTTP/1.1", opt.path..'?'..concat(args, "&")) - end - if HEADER and type(HEADER) == "table" then - for _, header in ipairs(HEADER) do - assert(lower(header[1]) ~= 'content-length', "please don't give Content-Length") - assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") - insert(request, header[1]..': '..header[2]) - end - end - insert(request, CRLF) - local REQ = concat(request, CRLF) + opt.args = args + opt.headers = headers + opt.server = SERVER + + local REQ = build_get_req(opt) - local sock = tcp:new():timeout(TIMEOUT or __TIMEOUT__) + local sock = sock_new():timeout(timeout or __TIMEOUT__) local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) if not ok then sock:close() @@ -267,52 +62,25 @@ function httpc.get(domain, HEADER, ARGS, TIMEOUT) sock:close() return ok, err end - return httpc_response(sock, opt.protocol) + local code, msg = httpc_response(sock, opt.protocol) + sock:close() + return code, msg end -- HTTP POST -function httpc.post(domain, HEADER, BODY, TIMEOUT) - +function httpc.post(domain, headers, body, timeout) local opt, err = splite_protocol(domain) if not opt then return nil, err end - local request = { - fmt("POST %s HTTP/1.1\r\n", opt.path), - fmt("Host: %s\r\n", opt.domain..':'..opt.port), - 'Accept: */*\r\n', - 'Accept-Encoding: identity\r\n', - fmt("Connection: keep-alive\r\n"), - fmt("User-Agent: %s\r\n", SERVER), - 'Content-Type: application/x-www-form-urlencoded\r\n', - } - if type(HEADER) == "table" then - for _, header in ipairs(HEADER) do - assert(string.lower(header[1]) ~= 'content-length', "please don't give Content-Length") - assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") - insert(request, header[1]..': '..header[2]..CRLF) - end - end - insert(request, CRLF) - - if BODY and type(BODY) == "table" then - local body = {} - for _, b in ipairs(BODY) do - assert(#b == 2, "if BODY is TABLE, BODY need key[1]->value[2] (2 values)") - insert(body, fmt("%s=%s", b[1], b[2])) - end - insert(request, concat(body, "&")) - insert(request, #request - 2, fmt("Content-Length: %s\r\n", #request[#request])) - end - if BODY and type(BODY) == "string" then - insert(request, #request, fmt("Content-Length: %s\r\n", #BODY)) - insert(request, BODY) - end + opt.body = body + opt.headers = headers + opt.server = SERVER - local REQ = concat(request) + local REQ = build_post_req(opt) - local sock = tcp:new():timeout(TIMEOUT or __TIMEOUT__) + local sock = sock_new():timeout(timeout or __TIMEOUT__) local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) if not ok then sock:close() @@ -323,42 +91,27 @@ function httpc.post(domain, HEADER, BODY, TIMEOUT) sock:close() return ok, err end - return httpc_response(sock, opt.protocol) + local code, msg = httpc_response(sock, opt.protocol) + sock:close() + return code, msg end -function httpc.json(domain, HEADER, JSON, TIMEOUT) +function httpc.json(domain, headers, json, timeout) local opt, err = splite_protocol(domain) if not opt then return nil, err end - assert(type(JSON) == "string", "Please passed A vaild json string.") + assert(type(json) == "string", "Please passed A vaild json string.") - local request = { - fmt("POST %s HTTP/1.1\r\n", opt.path), - fmt("Host: %s\r\n", opt.domain..':'..opt.port), - 'Accept: */*\r\n', - 'Accept-Encoding: identity\r\n', - fmt("Connection: keep-alive\r\n"), - fmt("User-Agent: %s\r\n", SERVER), - fmt("Content-Length: %s\r\n", #JSON), - 'Content-Type: application/json\r\n', - } - if HEADER and type(HEADER) == "table" then - for _, header in ipairs(HEADER) do - assert(lower(header[1]) ~= 'content-length', "please don't give Content-Length") - assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") - insert(request, header[1]..': '..header[2]..CRLF) - end - end + opt.json = json + opt.headers = headers + opt.server = SERVER - insert(request, CRLF) - insert(request, JSON) + local REQ = build_post_req(opt) - local REQ = concat(request) - - local sock = tcp:new():timeout(TIMEOUT or __TIMEOUT__) + local sock = sock_new():timeout(timeout or __TIMEOUT__) local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) if not ok then sock:close() @@ -369,60 +122,25 @@ function httpc.json(domain, HEADER, JSON, TIMEOUT) sock:close() return ok, err end - return httpc_response(sock, opt.protocol) + local code, msg = httpc_response(sock, opt.protocol) + sock:close() + return code, msg end -function httpc.file(domain, HEADER, FILES, TIMEOUT) +function httpc.file(domain, headers, files, times) local opt, err = splite_protocol(domain) if not opt then return nil, err end - local request = { - fmt("POST %s HTTP/1.1\r\n", opt.path), - fmt("Host: %s\r\n", opt.domain..':'..opt.port), - 'Accept: */*\r\n', - 'Accept-Encoding: identity\r\n', - fmt("Connection: keep-alive\r\n"), - fmt("User-Agent: %s\r\n", SERVER), - } - - if HEADER and type(HEADER) == "table" then - for _, header in ipairs(HEADER) do - assert(lower(header[1]) ~= 'content-length', "please don't give Content-Length") - assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") - insert(request, header[1]..': '..header[2]..'\r\n') - end - end + opt.files = files + opt.headers = headers + opt.server = SERVER - if FILES then - local bd = random(1000000000, 9999999999) - local boundary_start = fmt("------CFWebService%d", bd) - local boundary_end = fmt("------CFWebService%d--", bd) - insert(request, fmt('Content-Type: multipart/form-data; boundary=----CFWebService%s\r\n', bd)) - local body = {} - for _, file in ipairs(FILES) do - insert(body, boundary_start) - local header = "" - if file.name and file.filename then - header = fmt(' name="%s"; filename="%s"', file.name, file.filename) - end - insert(body, fmt('Content-Disposition: form-data;%s', header)) - insert(body, fmt('Content-Type: %s\r\n', FILEMIME(file.type or '') or 'application/octet-stream')) - insert(body, file.file) - end - body = concat(body, CRLF) - insert(request, fmt("Content-Length: %s\r\n", #body + 2 + #boundary_end)) - insert(request, CRLF) - insert(request, body) - insert(request, CRLF) - insert(request, boundary_end) - end + local REQ = build_file_req(opt) - local REQ = concat(request) - - local sock = tcp:new():timeout(TIMEOUT or __TIMEOUT__) + local sock = sock_new():timeout(TIMEOUT or __TIMEOUT__) local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) if not ok then sock:close() @@ -433,7 +151,13 @@ function httpc.file(domain, HEADER, FILES, TIMEOUT) sock:close() return ok, err end - return httpc_response(sock, opt.protocol) + local code, msg = httpc_response(sock, opt.protocol) + sock:close() + return code, msg +end + +function httpc:new (...) + return class:new(...) end return httpc diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua new file mode 100644 index 00000000..10d578ec --- /dev/null +++ b/lualib/httpc/protocol.lua @@ -0,0 +1,355 @@ +local tcp = require "internal.TCP" + +local HTTP = require "protocol.http" +local FILEMIME = HTTP.FILEMIME +local RESPONSE_PROTOCOL_PARSER = HTTP.RESPONSE_PROTOCOL_PARSER +local RESPONSE_HEADER_PARSER = HTTP.RESPONSE_HEADER_PARSER + + +local random = math.random +local find = string.find +local match = string.match +local split = string.sub +local splite = string.gmatch +local spliter = string.gsub +local lower = string.lower +local insert = table.insert +local concat = table.concat +local toint = math.tointeger +local fmt = string.format +local type = type +local assert = assert +local ipairs = ipairs +local tostring = tostring + +local CRLF = '\x0d\x0a' +local CRLF2 = '\x0d\x0a\x0d\x0a' + +local SERVER = "cf/0.1" + + +local function sock_new () + return tcp:new() +end + +local function sock_recv (sock, PROTOCOL, byte) + if PROTOCOL == 'https' then + local data, len = sock:ssl_recv(byte) + if data then + return data, len + end + end + if PROTOCOL == 'http' then + local data, len = sock:recv(byte) + if data then + return data, len + end + end + return nil, '服务端断开了连接' +end + +local function sock_send(sock, PROTOCOL, DATA) + if PROTOCOL == 'https' then + local ok = sock:ssl_send(DATA) + if ok then + return true + end + end + + if PROTOCOL == 'http' then + local ok = sock:send(DATA) + if ok then + return true + end + end + return nil, "httpc发送请求失败" +end + +local function sock_connect(sock, PROTOCOL, DOAMIN, PORT) + if PROTOCOL == 'https' then + local ok, err = sock:ssl_connect(DOAMIN, PORT) + if ok then + return true + end + end + if PROTOCOL == 'http' then + local ok, err = sock:connect(DOAMIN, PORT) + if ok then + return true + end + end + return nil, 'httpc连接失败.' +end + +local function splite_protocol(domain) + if type(domain) ~= 'string' then + return nil, '1. 非法的域名' + end + + local protocol, domain_port, path = match(domain, '^(http[s]?)://([^/]+)(.*)') + if not protocol or not domain_port or not path then + return nil, '2. 错误的url' + end + + if not path or path == '' then + return nil, "3. http无path需要以'/'结尾." + end + + local domain, port + if find(domain_port, ':') then + local _, Bracket_Pos = find(domain_port, '[%[%]]') + if Bracket_Pos then + domain, port = match(domain_port, '%[(.+)%][:]?(%d*)') + else + domain, port = match(domain_port, '([^:]+):(%d*)') + end + if not domain then + return nil, "4. 无效或者非法的主机名: "..domain_port + end + port = toint(port) + if not port then + port = 80 + if protocol == 'https' then + port = 443 + end + end + else + domain, port = domain_port, protocol == 'https' and 443 or 80 + end + return { + protocol = protocol, + domain = domain, + port = port, + path = path, + } +end + +local function httpc_response(sock, SSL) + if not sock then + return nil, "Can't used this method before other httpc method.." + end + local CODE, HEADER, BODY + local Content_Length + local content = {} + local times = 0 + while 1 do + local data, len = sock_recv(sock, SSL, 1024) + if not data then + return nil, "A peer of remote server close this connection." + end + insert(content, data) + local DATA = concat(content) + local posA, posB = find(DATA, CRLF2) + if posB then + CODE = RESPONSE_PROTOCOL_PARSER(split(DATA, 1, posB)) + HEADER = RESPONSE_HEADER_PARSER(split(DATA, 1, posB)) + if not CODE or not HEADER then + return nil, "can't resolvable protocol." + end + local Content_Length = toint(HEADER['Content-Length'] or HEADER['content-length']) + local chunked = HEADER['Transfer-Encoding'] + if not chunked and not Content_Length then + Content_Length = 0 + end + if Content_Length then + if (#DATA - posB) == Content_Length then + return CODE, split(DATA, posB+1, #DATA) + end + local content = {split(DATA, posB+1, #DATA)} + while 1 do + local data, len = sock_recv(sock, SSL, 1024) + if not data then + return CODE, SSL.."[Content_Length] A peer of remote server close this connection." + end + insert(content, data) + local DATA = concat(content) + if Content_Length == #DATA then + return CODE, DATA + end + end + end + if chunked and chunked == "chunked" then + local content = {} + if #DATA > posB then + local DATA = split(DATA, posB+1, #DATA) + if find(DATA, CRLF2) then + local body = {} + for hex, block in splite(DATA, "([%w]*)\r\n(.-)\r\n") do + local len = toint(fmt("0x%s", hex)) + if len and len == #block then + if len == 0 and block == '' then + return CODE, concat(body) + end + insert(body, block) + end + end + end + insert(content, DATA) + end + while 1 do + local data, len = sock_recv(sock, SSL, 1024) + if not data then + return CODE, SSL.."[chunked] A peer of remote server close this connection A." + end + insert(content, data) + local DATA = concat(content) + if find(DATA, CRLF2) then + local body = {} + for hex, block in splite(DATA, "([%a%d]*)\r\n(.-)\r\n") do + local len = toint(fmt("0x%s", hex)) + if len and len == #block then + if len == 0 and block == '' then + return CODE, concat(body) + end + insert(body, block) + end + end + end + end + end + end + end +end + + +local function build_get_req (opt) + local request = { + fmt("GET %s HTTP/1.1", opt.path), + fmt("Host: %s", opt.domain..':'..opt.port), + 'Accept: */*', + 'Accept-encoding: identity', + fmt("Connection: keep-alive"), + fmt("User-agent: %s", opt.server), + } + if type(opt.args) == "table" then + local args = {} + for _, arg in ipairs(opt.args) do + assert(#arg == 2, "args need key[1]->value[2] (2 values)") + insert(args, arg[1]..'='..arg[2]) + end + request[1] = fmt("GET %s HTTP/1.1", opt.path..'?'..concat(args, "&")) + end + if type(opt.headers) == "table" then + for _, header in ipairs(opt.headers) do + assert(lower(header[1]) ~= 'content-length', "please don't give Content-Length") + assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") + insert(request, header[1]..': '..header[2]) + end + end + insert(request, CRLF) + return concat(request, CRLF) +end + +local function build_post_req (opt) + local request = { + fmt("POST %s HTTP/1.1\r\n", opt.path), + fmt("Host: %s\r\n", opt.domain..':'..opt.port), + 'Accept: */*\r\n', + 'Accept-encoding: identity\r\n', + 'Connection: keep-alive\r\n', + fmt("User-agent: %s\r\n", opt.server), + 'Content-Type: application/x-www-form-urlencoded\r\n', + } + if type(opt.headers) == "table" then + for _, header in ipairs(opt.headers) do + assert(string.lower(header[1]) ~= 'content-length', "please don't give Content-Length") + assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") + insert(request, header[1]..': '..header[2]..CRLF) + end + end + insert(request, CRLF) + + if type(opt.body) == "table" then + local body = {} + for _, b in ipairs(opt.body) do + assert(#b == 2, "if BODY is TABLE, BODY need key[1]->value[2] (2 values)") + insert(body, fmt("%s=%s", b[1], b[2])) + end + insert(request, concat(body, "&")) + insert(request, #request - 2, fmt("Content-Length: %s\r\n", #request[#request])) + end + if type(opt.body) == "string" then + insert(request, #request, fmt("Content-Length: %s\r\n", #opt.body)) + insert(request, opt.body) + end + return concat(request) +end + +local function build_json_req (opt) + local request = { + fmt("POST %s HTTP/1.1", opt.path), + fmt("Host: %s", opt.domain..':'..opt.port), + 'Accept: */*', + 'Accept-encoding: identity', + "Connection: keep-alive", + fmt("User-agent: %s", opt.server), + fmt("Content-length: %s", #opt.json), + 'Content-Type: application/json', + } + if type(opt.headers) == "table" then + for _, header in ipairs(opt.headers) do + assert(lower(header[1]) ~= 'content-length', "please don't give Content-Length") + assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") + insert(request, header[1]..': '..header[2]..CRLF) + end + end + insert(request, CRLF) + return concat(request, CRLF)..opt.json +end + +local function build_file_req (opt) + local request = { + fmt("POST %s HTTP/1.1\r\n", opt.path), + fmt("Host: %s\r\n", opt.domain..':'..opt.port), + 'Accept: */*\r\n', + 'Accept-Encoding: identity\r\n', + fmt("Connection: keep-alive\r\n"), + fmt("User-Agent: %s\r\n", opt.server), + } + + if type(opt.headers) == "table" then + for _, header in ipairs(opt.headers) do + assert(lower(header[1]) ~= 'content-length', "please don't give Content-Length") + assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") + insert(request, header[1]..': '..header[2]..'\r\n') + end + end + + if opt.files then + local bd = random(1000000000, 9999999999) + local boundary_start = fmt("------CFWebService%d", bd) + local boundary_end = fmt("------CFWebService%d--", bd) + insert(request, fmt('Content-Type: multipart/form-data; boundary=----CFWebService%s\r\n', bd)) + local body = {} + local header = "" + for _, file in ipairs(opt.files) do + insert(body, boundary_start) + if file.name and file.filename then + header = fmt(' name="%s"; filename="%s"', file.name, file.filename) + end + insert(body, fmt('Content-Disposition: form-data;%s', header)) + insert(body, fmt('Content-Type: %s\r\n', FILEMIME(file.type or '') or 'application/octet-stream')) + insert(body, file.file) + end + body = concat(body, CRLF) + insert(request, fmt("Content-length: %s\r\n", #body + 2 + #boundary_end)) + insert(request, CRLF) + insert(request, body) + insert(request, CRLF) + insert(request, boundary_end) + end + return concat(request) +end + +return { + sock_new = sock_new, + sock_recv = sock_recv, + sock_send = sock_send, + sock_connect = sock_connect, + httpc_response = httpc_response, + splite_protocol = splite_protocol, + build_get_req = build_get_req, + build_post_req = build_post_req, + build_json_req = build_json_req, + build_file_req = build_file_req, +} From 234eb73f06bee73a41955644894995bca2df5ad2 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 30 May 2019 15:05:42 +0800 Subject: [PATCH 094/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0httpc=E7=9A=84?= =?UTF-8?q?=E7=B1=BB=E5=B0=81=E8=A3=85,=20=E7=94=A8=E4=BA=8Esocket?= =?UTF-8?q?=E5=A4=8D=E7=94=A8=E5=9C=BA=E6=99=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/class.lua | 235 +++++++++++++++++++++++++++++++++++++++++ script/test_httpc.lua | 57 +++++++++- 2 files changed, 289 insertions(+), 3 deletions(-) create mode 100644 lualib/httpc/class.lua diff --git a/lualib/httpc/class.lua b/lualib/httpc/class.lua new file mode 100644 index 00000000..36f5b0cb --- /dev/null +++ b/lualib/httpc/class.lua @@ -0,0 +1,235 @@ +local class = require "class" +local cf = require "cf" +local cf_fork = cf.fork +local cf_wait = cf.wait +local cf_wakeup = cf.wakeup + +local protocol = require "httpc.protocol" +local sock_new = protocol.sock_new +local sock_recv = protocol.sock_recv +local sock_send = protocol.sock_send +local sock_connect = protocol.sock_connect +local httpc_response = protocol.httpc_response +local splite_protocol = protocol.splite_protocol +local build_get_req = protocol.build_get_req +local build_post_req = protocol.build_post_req +local build_json_req = protocol.build_json_req +local build_file_req = protocol.build_file_req + +local type = type +local assert = assert +local ipairs = ipairs +local tostring = tostring + +local random = math.random +local find = string.find +local match = string.match +local split = string.sub +local splite = string.gmatch +local spliter = string.gsub +local lower = string.lower +local insert = table.insert +local concat = table.concat +local toint = math.tointeger +local fmt = string.format + +local SERVER = "cf/0.1" + +local CRLF = '\x0d\x0a' +local CRLF2 = '\x0d\x0a\x0d\x0a' + + + +local httpc = class("httpc") + +function httpc:ctor (opt) + self.server = "cf/0.1" + self.timeout = opt.timeout or 15 +end + +-- get 请求 +function httpc:get (domain, headers, args, timeout) + local opt, err = splite_protocol(domain) + if not opt then + return nil, err + end + + if self.domain and self.domain ~= opt.domain then + return nil, "1. 不同的域名不可使用httpc对象来请求" + end + + if self.port and self.port ~= opt.port then + return nil, "2. 不同的域名不可使用httpc对象来请求" + end + + opt.args = args + opt.headers = headers + opt.server = self.server + + self.domain = opt.domain + self.port = opt.port + + local REQ = build_get_req(opt) + + self.sock = self.sock or sock_new():timeout(self.timeout) + + local ok, err = sock_connect(self.sock, opt.protocol, opt.domain, opt.port) + if not ok then + return ok, err + end + local ok, err = sock_send(self.sock, opt.protocol, REQ) + if not ok then + return ok, err + end + local code, msg = httpc_response(self.sock, opt.protocol) + if not code then + self.sock:close() + self.sock = nil + end + return code, msg +end + +-- post 请求 +function httpc:post (domain, headers, body, timeout) + local opt, err = splite_protocol(domain) + if not opt then + return nil, err + end + + if self.domain and self.domain ~= opt.domain then + return nil, "1. 不同的域名不可使用httpc对象来请求" + end + + if self.port and self.port ~= opt.port then + return nil, "2. 不同的域名不可使用httpc对象来请求" + end + + opt.body = body + opt.headers = headers + opt.server = self.server + + self.domain = opt.domain + self.port = opt.port + + local REQ = build_post_req(opt) + + self.sock = self.sock or sock_new():timeout(self.timeout) + + local ok, err = sock_connect(self.sock, opt.protocol, opt.domain, opt.port) + if not ok then + return ok, err + end + local ok, err = sock_send(self.sock, opt.protocol, REQ) + if not ok then + return ok, err + end + local code, msg = httpc_response(self.sock, opt.protocol) + if not code then + self.sock:close() + self.sock = nil + end + return code, msg +end + +-- json 请求 +function httpc:json (domain, headers, json, timeout) + + assert(type(json) == "string", "Please passed A vaild json string.") + + local opt, err = splite_protocol(domain) + if not opt then + return nil, err + end + + if self.domain and self.domain ~= opt.domain then + return nil, "1. 不同的域名不可使用httpc对象来请求" + end + + if self.port and self.port ~= opt.port then + return nil, "2. 不同的域名不可使用httpc对象来请求" + end + + opt.json = json + opt.headers = headers + opt.server = self.server + + self.domain = opt.domain + self.port = opt.port + + local REQ = build_json_req(opt) + + self.sock = self.sock or sock_new():timeout(self.timeout) + + local ok, err = sock_connect(self.sock, opt.protocol, opt.domain, opt.port) + if not ok then + return ok, err + end + local ok, err = sock_send(self.sock, opt.protocol, REQ) + if not ok then + return ok, err + end + local code, msg = httpc_response(self.sock, opt.protocol) + if not code then + self.sock:close() + self.sock = nil + end + return code, msg +end + +-- file 请求 +function httpc:file (domain, headers, files, timeout) + local opt, err = splite_protocol(domain) + if not opt then + return nil, err + end + + if self.domain and self.domain ~= opt.domain then + return nil, "1. 不同的域名不可使用httpc对象来请求" + end + + if self.port and self.port ~= opt.port then + return nil, "2. 不同的域名不可使用httpc对象来请求" + end + + opt.files = files + opt.headers = headers + opt.server = self.server + + self.domain = opt.domain + self.port = opt.port + + local REQ = build_file_req(opt) + + self.sock = self.sock or sock_new():timeout(self.timeout) + + local ok, err = sock_connect(self.sock, opt.protocol, opt.domain, opt.port) + if not ok then + return ok, err + end + local ok, err = sock_send(self.sock, opt.protocol, REQ) + if not ok then + return ok, err + end + local code, msg = httpc_response(self.sock, opt.protocol) + if not code then + self.sock:close() + self.sock = nil + end + return code, msg +end + +-- 多次请求 +function httpc:multi_request (opt) + if type ~= 'table' then + return nil, "错误的参数类型" + end +end + +function httpc:close () + if self.sock then + self.sock:close() + self.sock = nil + end +end + +return httpc diff --git a/script/test_httpc.lua b/script/test_httpc.lua index 591cc1f2..b564c055 100644 --- a/script/test_httpc.lua +++ b/script/test_httpc.lua @@ -1,23 +1,32 @@ local cf = require "cf" local httpc = require "httpc" local json = require "json" +local system = require "system" +local now = system.now + +cf.fork(function (args) + local code, body = httpc.get("http://localhost:8080/api") + print(code, body) +end) local ti = cf.timeout(2, function ( ... ) cf.fork(function ( ... ) + local t1 = now() + print("开始时间:", t1) -- GET请求: 在参数固定的情况下可以直接写在url内 local code, body = httpc.get("http://localhost:8080/api?page=1&limit=10", {{"Auth", "admin"}}) print(code, body) -- GET请求: 在参数为动态的情况下可以提供请求数组由httpc库进行拼接 - local code, body = httpc.get("http://[::1]:8080/api", {{"Auth", "admin"}}, {{'page', 1}, {'limit', 10}}) + local code, body = httpc.get("http://localhost:8080/api", {{"Auth", "admin"}}, {{'page', 1}, {'limit', 10}}) print(code, body) -- POST HEADER 为数组, BODY为数组 - local code, body = httpc.post("http://[::ffff:127.0.0.1]:8080/api", {{"Auth", "admin"}}, {{'page', 1}, {'limit', 10}}) + local code, body = httpc.post("http://localhost:8080/api", {{"Auth", "admin"}}, {{'page', 1}, {'limit', 10}}) print(code, body) -- POST HEADER 为数组, BODY为字符串 - local code, body = httpc.post("http://127.0.0.1:8080/api", {{"Auth", "admin"}}, "page=1&limit=10") + local code, body = httpc.post("http://localhost:8080/api", {{"Auth", "admin"}}, "page=1&limit=10") print(code, body) -- http json请求示例 @@ -30,5 +39,47 @@ local ti = cf.timeout(2, function ( ... ) {name='2', filename='2.jpg', file='2', type='abc'}, }) print(code, body) + local t2 = now() + print("结束时间:", t1, "总耗时:", t2 - t1) + end) +end) + + +require "utils" +local http = require "httpc.class" +local hc = http:new {} +local ti = cf.timeout(1, function ( ... ) + cf.fork(function ( ... ) + local t1 = now() + print("开始时间:", t1) + -- GET请求: 在参数固定的情况下可以直接写在url内 + local code, body = hc:get("http://localhost:8080/api?page=1&limit=10", {{"Auth", "admin"}}) + print(code, body) + + -- GET请求: 在参数为动态的情况下可以提供请求数组由httpc库进行拼接 + local code, body = hc:get("http://localhost:8080/api", {{"Auth", "admin"}}, {{'page', 1}, {'limit', 10}}) + print(code, body) + + -- POST HEADER 为数组, BODY为数组 + local code, body = hc:post("http://localhost:8080/api", {{"Auth", "admin"}}, {{'page', 1}, {'limit', 10}}) + print(code, body) + + -- POST HEADER 为数组, BODY为字符串 + local code, body = hc:post("http://localhost:8080/api", {{"Auth", "admin"}}, "page=1&limit=10") + print(code, body) + + -- http json请求示例 + local code, body = hc:json("http://localhost:8080/api", {{"Auth", "admin"}}, json.encode({page=1, limit=10})) + print(code, body) + + -- http 上传文件示例 + local code, body = hc:file('http://localhost:8080/api', nil, { + {name='1', filename='1.jpg', file='1', type='abc'}, + {name='2', filename='2.jpg', file='2', type='abc'}, + }) + print(code, body) + local t2 = now() + print("结束时间:", t1, "总耗时:", t2 - t1) + hc:close() end) end) From dd4bb14b3f3f4957184784616f2735b9a6466d56 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 30 May 2019 18:26:11 +0800 Subject: [PATCH 095/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E6=AE=B5?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E7=9A=84=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index 356351df..a0001e97 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -226,7 +226,7 @@ local function PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEA content['args'] = form_urlencode(PATH) end elseif METHOD == "POST" or METHOD == "PUT" then - local body_len = toint(HEADER['Content-Length']) or toint(HEADER['content-type']) + local body_len = toint(HEADER['Content-Length']) or toint(HEADER['Content-length']) if body_len and body_len > 0 then local BODY = '' local RECV_BODY = true From fb727bb6532e0208fd0d20681e84476b7f54c3a9 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 30 May 2019 18:54:00 +0800 Subject: [PATCH 096/956] =?UTF-8?q?=E4=B8=BAxml2lua=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=AE=89=E5=85=A8=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/xml2lua/init.lua | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lualib/xml2lua/init.lua b/lualib/xml2lua/init.lua index c9adc2cd..95fc44bc 100644 --- a/lualib/xml2lua/init.lua +++ b/lualib/xml2lua/init.lua @@ -2,14 +2,20 @@ local xml2lua = require "xml2lua.xml2lua" local xml2lua_handler = require "xml2lua.xmlhandler.tree" +local type = type +local pcall = pcall local xml = {} -- xml字符串解析 function xml.parser(xml_data) local cls = xml2lua.parser(xml2lua_handler:new()) - cls:parse(xml_data) - return cls.handler.root + -- cls:parse(xml_data) + local ok, data = pcall(cls.parse, cls,xml_data) + if not ok then + return data + end + return data end -- xml文件读取 From 4c7b9f2b1b5ac4115676d9508419d979ae25ad38 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 30 May 2019 19:45:16 +0800 Subject: [PATCH 097/956] =?UTF-8?q?=E5=AE=8C=E5=96=84httpc=E7=9A=84?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=96=B9=E6=B3=95=E4=B8=8E=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?httpc=20class=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/main.lua | 4 +-- script/test_httpc.lua | 63 ++++++++++++++++++++++++++++++++----------- 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/script/main.lua b/script/main.lua index 23545ab3..b1ea1f4d 100644 --- a/script/main.lua +++ b/script/main.lua @@ -1,7 +1,8 @@ local httpd = require "httpd" local httpc = require "httpc" local DB = require "DB" - +local xml = require "xml2lua" +require "test_httpc" --[[ 请按照以下步奏初始化后台: 1. 创建一个数据库(名字任意); @@ -29,7 +30,6 @@ app:enable_cookie() app:cookie_secure("https://github.com/CandyMi/core_framework") -- app:cookie_secure("candymi") - app:ws('/ws', require "ws") app:api('/api', function (content) diff --git a/script/test_httpc.lua b/script/test_httpc.lua index b564c055..9fd339da 100644 --- a/script/test_httpc.lua +++ b/script/test_httpc.lua @@ -1,15 +1,13 @@ local cf = require "cf" local httpc = require "httpc" +local http = require "httpc.class" local json = require "json" local system = require "system" local now = system.now -cf.fork(function (args) - local code, body = httpc.get("http://localhost:8080/api") - print(code, body) -end) +require "utils" -local ti = cf.timeout(2, function ( ... ) +cf.timeout(2, function ( ... ) cf.fork(function ( ... ) local t1 = now() print("开始时间:", t1) @@ -39,17 +37,16 @@ local ti = cf.timeout(2, function ( ... ) {name='2', filename='2.jpg', file='2', type='abc'}, }) print(code, body) + local t2 = now() print("结束时间:", t1, "总耗时:", t2 - t1) end) end) -require "utils" -local http = require "httpc.class" -local hc = http:new {} -local ti = cf.timeout(1, function ( ... ) +cf.timeout(1, function ( ... ) cf.fork(function ( ... ) + local hc = http:new {} local t1 = now() print("开始时间:", t1) -- GET请求: 在参数固定的情况下可以直接写在url内 @@ -72,14 +69,50 @@ local ti = cf.timeout(1, function ( ... ) local code, body = hc:json("http://localhost:8080/api", {{"Auth", "admin"}}, json.encode({page=1, limit=10})) print(code, body) - -- http 上传文件示例 - local code, body = hc:file('http://localhost:8080/api', nil, { - {name='1', filename='1.jpg', file='1', type='abc'}, - {name='2', filename='2.jpg', file='2', type='abc'}, - }) - print(code, body) local t2 = now() print("结束时间:", t1, "总耗时:", t2 - t1) hc:close() end) end) + +cf.timeout(3, function () + cf.fork(function ( ... ) + local hc = http:new {} + local t1 = now() + print("开始时间:", t1) + local ok, response = hc:multi_request { + { + domain = "http://localhost:8080/api", + method = "get", + headers = {{"Auth", "admin"}}, + args = {{'page', 1}, {'limit', 10}} + }, + { + domain = "http://localhost:8080/api", + method = "post", + headers = {{"Auth", "admin"}}, + body = {{'page', 1}, {'limit', 10}} + }, + { + domain = "http://localhost:8080/api", + method = "json", + headers = {{"Auth", "admin"}}, + json = json.encode({page=1, limit=10}) + }, + { + domain = "http://localhost:8080/api", + method = "file", + headers = {{"Auth", "admin"}}, + files = { + {name='1', filename='1.jpg', file='1', type='abc'}, + {name='2', filename='2.jpg', file='2', type='abc'}, + } + } + } + local t2 = now() + print("结束时间:", t1, "总耗时:", t2 - t1) + + require('logging'):new():DEBUG(response, "回应数量: " .. #response) + hc:close() + end) +end) From 0430bb47553e7d25dabca59060687bf64ff9f11f Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 30 May 2019 19:46:43 +0800 Subject: [PATCH 098/956] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=9C=AA=E5=88=A0=E9=99=A4=E7=9A=84=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/main.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/main.lua b/script/main.lua index b1ea1f4d..b67ccb4d 100644 --- a/script/main.lua +++ b/script/main.lua @@ -2,7 +2,7 @@ local httpd = require "httpd" local httpc = require "httpc" local DB = require "DB" local xml = require "xml2lua" -require "test_httpc" + --[[ 请按照以下步奏初始化后台: 1. 创建一个数据库(名字任意); From 10e5ac462c987e207e292738d6dc4cb8474e3313 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 30 May 2019 19:49:03 +0800 Subject: [PATCH 099/956] =?UTF-8?q?=E4=B8=BAhttpc=E5=A2=9E=E5=8A=A0http=20?= =?UTF-8?q?class=E5=AF=B9=E8=B1=A1,=E5=A2=9E=E5=8A=A0=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/class.lua | 217 +++++++++++++++++++++++++++++++++----- lualib/httpc/protocol.lua | 8 +- 2 files changed, 193 insertions(+), 32 deletions(-) diff --git a/lualib/httpc/class.lua b/lualib/httpc/class.lua index 36f5b0cb..c13cdf0c 100644 --- a/lualib/httpc/class.lua +++ b/lualib/httpc/class.lua @@ -1,5 +1,11 @@ local class = require "class" + +local system = require "system" +local now = system.now +local is_array_member = system.is_array_member + local cf = require "cf" +local cf_self = cf.self local cf_fork = cf.fork local cf_wait = cf.wait local cf_wakeup = cf.wakeup @@ -38,7 +44,7 @@ local SERVER = "cf/0.1" local CRLF = '\x0d\x0a' local CRLF2 = '\x0d\x0a\x0d\x0a' - +local methods = {'get', 'post', 'json', 'file'} local httpc = class("httpc") @@ -71,16 +77,34 @@ function httpc:get (domain, headers, args, timeout) local REQ = build_get_req(opt) - self.sock = self.sock or sock_new():timeout(self.timeout) - - local ok, err = sock_connect(self.sock, opt.protocol, opt.domain, opt.port) - if not ok then - return ok, err + if not self.sock then + local sock = sock_new():timeout(self.timeout) + local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) + if not ok then + sock:close() + return ok, err + end + self.sock = sock end + local ok, err = sock_send(self.sock, opt.protocol, REQ) if not ok then - return ok, err + self.sock:close() + self.sock = nil + local sock = sock_new():timeout(self.timeout) + local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) + if not ok then + sock:close() + return ok, err + end + ok, err = sock_send(sock, opt.protocol, REQ) + if not ok then + sock:close() + return nil, err + end + self.sock = sock end + local code, msg = httpc_response(self.sock, opt.protocol) if not code then self.sock:close() @@ -113,16 +137,34 @@ function httpc:post (domain, headers, body, timeout) local REQ = build_post_req(opt) - self.sock = self.sock or sock_new():timeout(self.timeout) - - local ok, err = sock_connect(self.sock, opt.protocol, opt.domain, opt.port) - if not ok then - return ok, err + if not self.sock then + local sock = sock_new():timeout(self.timeout) + local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) + if not ok then + sock:close() + return ok, err + end + self.sock = sock end + local ok, err = sock_send(self.sock, opt.protocol, REQ) if not ok then - return ok, err + self.sock:close() + self.sock = nil + local sock = sock_new():timeout(self.timeout) + local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) + if not ok then + sock:close() + return ok, err + end + ok, err = sock_send(sock, opt.protocol, REQ) + if not ok then + sock:close() + return nil, err + end + self.sock = sock end + local code, msg = httpc_response(self.sock, opt.protocol) if not code then self.sock:close() @@ -158,16 +200,34 @@ function httpc:json (domain, headers, json, timeout) local REQ = build_json_req(opt) - self.sock = self.sock or sock_new():timeout(self.timeout) - - local ok, err = sock_connect(self.sock, opt.protocol, opt.domain, opt.port) - if not ok then - return ok, err + if not self.sock then + local sock = sock_new():timeout(self.timeout) + local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) + if not ok then + sock:close() + return ok, err + end + self.sock = sock end + local ok, err = sock_send(self.sock, opt.protocol, REQ) if not ok then - return ok, err + self.sock:close() + self.sock = nil + local sock = sock_new():timeout(self.timeout) + local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) + if not ok then + sock:close() + return ok, err + end + ok, err = sock_send(sock, opt.protocol, REQ) + if not ok then + sock:close() + return nil, err + end + self.sock = sock end + local code, msg = httpc_response(self.sock, opt.protocol) if not code then self.sock:close() @@ -200,16 +260,34 @@ function httpc:file (domain, headers, files, timeout) local REQ = build_file_req(opt) - self.sock = self.sock or sock_new():timeout(self.timeout) - - local ok, err = sock_connect(self.sock, opt.protocol, opt.domain, opt.port) - if not ok then - return ok, err + if not self.sock then + local sock = sock_new():timeout(self.timeout) + local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) + if not ok then + sock:close() + return ok, err + end + self.sock = sock end + local ok, err = sock_send(self.sock, opt.protocol, REQ) if not ok then - return ok, err + self.sock:close() + self.sock = nil + local sock = sock_new():timeout(self.timeout) + local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) + if not ok then + sock:close() + return ok, err + end + ok, err = sock_send(sock, opt.protocol, REQ) + if not ok then + sock:close() + return nil, err + end + self.sock = sock end + local code, msg = httpc_response(self.sock, opt.protocol) if not code then self.sock:close() @@ -218,13 +296,96 @@ function httpc:file (domain, headers, files, timeout) return code, msg end --- 多次请求 +-- 异步请求 function httpc:multi_request (opt) - if type ~= 'table' then - return nil, "错误的参数类型" + if type(opt) ~= 'table' then + return nil, "1. 错误的参数类型" + end + local len = #opt + if len > 0 then + local co = cf_self() + local response = {} + local wakeuped = false + for index = 1, len do + cf_fork(function () + local t = now() + local req = opt[index] + -- 确认method + local method = req.method and req.method:lower() + if type(method) ~= 'string' or not is_array_member(methods, method) then + response[index] = {nil, '不被支持的请求方法.', now() - t} + if #response >= len and not wakeuped then + wakeuped = true + cf_wakeup(co, nil, response) + end + return + end + + -- 解析domain + local opt, err = splite_protocol(req.domain) + if not opt then + response[index] = {nil, err, now() - t} + if #response >= len and not wakeuped then + wakeuped = true + cf_wakeup(co, nil, response) + end + return + end + + opt.json = req.json + opt.body = req.body + opt.args = req.args + opt.files = req.files + opt.headers = req.headers + opt.server = self.server + + local REQ + if method == 'get' then + REQ = build_get_req(opt) + elseif method == 'post' then + REQ = build_post_req(opt) + elseif method == 'json' then + REQ = build_json_req(opt) + elseif method == 'file' then + REQ = build_file_req(opt) + end + + local sock = sock_new():timeout(self.timeout) + local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) + if not ok then + response[index] = {nil, err, now() - t} + if #response >= len and not wakeuped then + wakeuped = true + cf_wakeup(co, nil, response) + end + return sock:close() + end + + ok, err = sock_send(sock, opt.protocol, REQ) + if not ok then + response[index] = {nil, err, now() - t} + if #response >= len and not wakeuped then + wakeuped = true + cf_wakeup(co, nil, response) + end + return sock:close() + end + + local code, msg = httpc_response(sock, opt.protocol) + response[index] = {code, msg, now() - t} + if #response >= len and not wakeuped then + wakeuped = true + cf_wakeup(co, true, response) + end + return sock:close() + end) + end + return cf_wait() end + return nil, "2. 错误的参数" end +-- 关闭连接 function httpc:close () if self.sock then self.sock:close() diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua index 10d578ec..fabfbd3e 100644 --- a/lualib/httpc/protocol.lua +++ b/lualib/httpc/protocol.lua @@ -266,10 +266,10 @@ local function build_post_req (opt) insert(body, fmt("%s=%s", b[1], b[2])) end insert(request, concat(body, "&")) - insert(request, #request - 2, fmt("Content-Length: %s\r\n", #request[#request])) + insert(request, #request - 2, fmt("Content-length: %s\r\n", #request[#request])) end if type(opt.body) == "string" then - insert(request, #request, fmt("Content-Length: %s\r\n", #opt.body)) + insert(request, #request, fmt("Content-length: %s\r\n", #opt.body)) insert(request, opt.body) end return concat(request) @@ -290,11 +290,11 @@ local function build_json_req (opt) for _, header in ipairs(opt.headers) do assert(lower(header[1]) ~= 'content-length', "please don't give Content-Length") assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") - insert(request, header[1]..': '..header[2]..CRLF) + insert(request, header[1]..': '..header[2]) end end insert(request, CRLF) - return concat(request, CRLF)..opt.json + return concat(request, CRLF)..opt.json end local function build_file_req (opt) From 5f36e2d0b2dd468cd88d267584ce9e175b4852cf Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 30 May 2019 21:10:26 +0800 Subject: [PATCH 100/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0httpc=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/class.lua | 99 ++---------------------------------------- lualib/httpc/init.lua | 76 ++++++++++++++++++++++++++++---- 2 files changed, 71 insertions(+), 104 deletions(-) diff --git a/lualib/httpc/class.lua b/lualib/httpc/class.lua index c13cdf0c..4f41628f 100644 --- a/lualib/httpc/class.lua +++ b/lualib/httpc/class.lua @@ -1,14 +1,7 @@ -local class = require "class" - -local system = require "system" -local now = system.now -local is_array_member = system.is_array_member +local hc = require "httpc" +local hc_multi_request = hc.multi_request -local cf = require "cf" -local cf_self = cf.self -local cf_fork = cf.fork -local cf_wait = cf.wait -local cf_wakeup = cf.wakeup +local class = require "class" local protocol = require "httpc.protocol" local sock_new = protocol.sock_new @@ -298,91 +291,7 @@ end -- 异步请求 function httpc:multi_request (opt) - if type(opt) ~= 'table' then - return nil, "1. 错误的参数类型" - end - local len = #opt - if len > 0 then - local co = cf_self() - local response = {} - local wakeuped = false - for index = 1, len do - cf_fork(function () - local t = now() - local req = opt[index] - -- 确认method - local method = req.method and req.method:lower() - if type(method) ~= 'string' or not is_array_member(methods, method) then - response[index] = {nil, '不被支持的请求方法.', now() - t} - if #response >= len and not wakeuped then - wakeuped = true - cf_wakeup(co, nil, response) - end - return - end - - -- 解析domain - local opt, err = splite_protocol(req.domain) - if not opt then - response[index] = {nil, err, now() - t} - if #response >= len and not wakeuped then - wakeuped = true - cf_wakeup(co, nil, response) - end - return - end - - opt.json = req.json - opt.body = req.body - opt.args = req.args - opt.files = req.files - opt.headers = req.headers - opt.server = self.server - - local REQ - if method == 'get' then - REQ = build_get_req(opt) - elseif method == 'post' then - REQ = build_post_req(opt) - elseif method == 'json' then - REQ = build_json_req(opt) - elseif method == 'file' then - REQ = build_file_req(opt) - end - - local sock = sock_new():timeout(self.timeout) - local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) - if not ok then - response[index] = {nil, err, now() - t} - if #response >= len and not wakeuped then - wakeuped = true - cf_wakeup(co, nil, response) - end - return sock:close() - end - - ok, err = sock_send(sock, opt.protocol, REQ) - if not ok then - response[index] = {nil, err, now() - t} - if #response >= len and not wakeuped then - wakeuped = true - cf_wakeup(co, nil, response) - end - return sock:close() - end - - local code, msg = httpc_response(sock, opt.protocol) - response[index] = {code, msg, now() - t} - if #response >= len and not wakeuped then - wakeuped = true - cf_wakeup(co, true, response) - end - return sock:close() - end) - end - return cf_wait() - end - return nil, "2. 错误的参数" + return hc_multi_request(opt) end -- 关闭连接 diff --git a/lualib/httpc/init.lua b/lualib/httpc/init.lua index 7ae3869d..9284bdad 100644 --- a/lualib/httpc/init.lua +++ b/lualib/httpc/init.lua @@ -1,4 +1,12 @@ -local class = require "httpc.class" +local system = require "system" +local now = system.now +local is_array_member = system.is_array_member + +local cf = require "cf" +local cf_self = cf.self +local cf_fork = cf.fork +local cf_wait = cf.wait +local cf_wakeup = cf.wakeup local protocol = require "httpc.protocol" local sock_new = protocol.sock_new @@ -36,10 +44,10 @@ local CRLF2 = '\x0d\x0a\x0d\x0a' local __TIMEOUT__ = 15 -local httpc = {} +local methods = {'get', 'post', 'json', 'file'} -- HTTP GET -function httpc.get(domain, headers, args, timeout) +local function get(domain, headers, args, timeout) local opt, err = splite_protocol(domain) if not opt then return nil, err @@ -68,7 +76,7 @@ function httpc.get(domain, headers, args, timeout) end -- HTTP POST -function httpc.post(domain, headers, body, timeout) +local function post(domain, headers, body, timeout) local opt, err = splite_protocol(domain) if not opt then return nil, err @@ -96,7 +104,7 @@ function httpc.post(domain, headers, body, timeout) return code, msg end -function httpc.json(domain, headers, json, timeout) +local function json(domain, headers, json, timeout) local opt, err = splite_protocol(domain) if not opt then @@ -127,7 +135,7 @@ function httpc.json(domain, headers, json, timeout) return code, msg end -function httpc.file(domain, headers, files, times) +local function file(domain, headers, files, times) local opt, err = splite_protocol(domain) if not opt then @@ -156,8 +164,58 @@ function httpc.file(domain, headers, files, times) return code, msg end -function httpc:new (...) - return class:new(...) +local function multi_request (opt) + if type(opt) ~= 'table' then + return nil, "1. 错误的参数类型" + end + local len = #opt + if len > 0 then + local co = cf_self() + local response = {} + local wakeuped = false + for index = 1, len do + cf_fork(function () + local t = now() + local req = opt[index] + -- 确认method + local method = req.method and req.method:lower() + if type(method) ~= 'string' or not is_array_member(methods, method) then + response[index] = {nil, '不被支持的请求方法.', now() - t} + if #response >= len and not wakeuped then + wakeuped = true + cf_wakeup(co, nil, response) + end + return + end + + local code, msg + if method == 'get' then + code, msg = get(req.domain, req.headers, req.args, req.timeout) + elseif method == 'post' then + code, msg = post(req.domain, req.headers, req.body, req.timeout) + elseif method == 'json' then + code, msg = json(req.domain, req.headers, req.json, req.timeout) + elseif method == 'file' then + code, msg = file(req.domain, req.headers, req.files, req.timeout) + end + response[index] = {code, msg, now() - t} + if #response >= len and not wakeuped then + wakeuped = true + cf_wakeup(co, nil, response) + end + return + end) + end + return cf_wait() + end + return nil, "2. 错误的参数" end -return httpc + +return { + get = get, + post = post, + json = json, + file = file, + multi_request = multi_request, +} From 71366a5ce1b7eb4ad90425cdc58fca1ff6269432 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 31 May 2019 20:03:51 +0800 Subject: [PATCH 101/956] =?UTF-8?q?=E4=BC=98=E5=8C=96tcp=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/TCP.lua | 149 ++++++++++++++++++++-------------------- 1 file changed, 76 insertions(+), 73 deletions(-) diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index 26199208..b55dc968 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -22,7 +22,7 @@ local tcp_stop = tcp.stop local tcp_free_ssl = tcp.free_ssl local tcp_close = tcp.close local tcp_connect = tcp.connect -local tcp_ssl_connect = tcp.ssl_connect +local tcp_ssl_do_handshak = tcp.ssl_connect local tcp_read = tcp.read local tcp_sslread = tcp.ssl_read local tcp_write = tcp.write @@ -154,90 +154,94 @@ function TCP:recv(bytes) if self.ssl then return Log:ERROR("Please use ssl_recv method :)") end + local buf, len = tcp_read(self.fd, bytes) + if buf then + return buf, len + end self.READ_IO = tcp_pop() local co = co_self() self.read_current_co = co_self() self.read_co = co_new(function ( ... ) - local buf, len = tcp_read(self.fd, bytes) + local buf, len = tcp_read(self.fd, bytes) + if self.timer then + self.timer:stop() + self.timer = nil + end + tcp_push(self.READ_IO) + tcp_stop(self.READ_IO) + self.READ_IO = nil + self.read_co = nil + self.read_current_co =nil + if not buf then + return co_wakeup(co) + end + return co_wakeup(co, buf, len) + end) + self.timer = ti.timeout(self._timeout, function ( ... ) + tcp_push(self.READ_IO) + tcp_stop(self.READ_IO) + self.timer = nil + self.read_co = nil + self.READ_IO = nil + self.read_current_co = nil + return co_wakeup(co, nil, "read timeout") + end) + tcp_start(self.READ_IO, self.fd, EVENT_READ, self.read_co) + return co_wait() +end + +function TCP:ssl_recv(bytes) + if not self.ssl then + return Log:ERROR("Please use recv method :)") + end + local buf, len = tcp_sslread(self.ssl, bytes) + if buf then + return buf, len + end + local co = co_self() + self.read_current_co = co_self() + self.READ_IO = tcp_pop() + self.read_co = co_new(function ( ... ) + while 1 do + local buf, len = tcp_sslread(self.ssl, bytes) + if not buf and not len then if self.timer then - self.timer:stop() - self.timer = nil + self.timer:stop() + self.timer = nil end tcp_push(self.READ_IO) tcp_stop(self.READ_IO) self.READ_IO = nil self.read_co = nil - self.read_current_co =nil - if not buf then - return co_wakeup(co) + self.read_current_co = nil + return co_wakeup(co) + end + if buf and len then + if self.timer then + self.timer:stop() + self.timer = nil end - return co_wakeup(co, buf, len) - end) - self.timer = ti.timeout(self._timeout, function ( ... ) tcp_push(self.READ_IO) tcp_stop(self.READ_IO) - self.timer = nil - self.read_co = nil self.READ_IO = nil + self.read_co = nil self.read_current_co = nil - return co_wakeup(co, nil, "read timeout") - end) - tcp_start(self.READ_IO, self.fd, EVENT_READ, self.read_co) - return co_wait() -end - -function TCP:ssl_recv(bytes) - if not self.ssl then - return Log:ERROR("Please use recv method :)") - end - local buf, len = tcp_sslread(self.ssl, bytes) - if not buf then - local co = co_self() - self.read_current_co = co_self() - self.READ_IO = tcp_pop() - self.read_co = co_new(function ( ... ) - while 1 do - local buf, len = tcp_sslread(self.ssl, bytes) - if not buf and not len then - if self.timer then - self.timer:stop() - self.timer = nil - end - tcp_push(self.READ_IO) - tcp_stop(self.READ_IO) - self.READ_IO = nil - self.read_co = nil - self.read_current_co = nil - return co_wakeup(co) - end - if buf and len then - if self.timer then - self.timer:stop() - self.timer = nil - end - tcp_push(self.READ_IO) - tcp_stop(self.READ_IO) - self.READ_IO = nil - self.read_co = nil - self.read_current_co = nil - return co_wakeup(co, buf, len) - end - co_wait() - end - end) - self.timer = ti.timeout(self._timeout, function ( ... ) - tcp_push(self.READ_IO) - tcp_stop(self.READ_IO) - self.timer = nil - self.READ_IO = nil - self.read_co = nil - self.read_current_co = nil - return co_wakeup(co, nil, "read timeout") - end) - tcp_start(self.READ_IO, self.fd, EVENT_READ, self.read_co) - return co_wait() + return co_wakeup(co, buf, len) + end + co_wait() end - return buf, len + end) + self.timer = ti.timeout(self._timeout, function ( ... ) + tcp_push(self.READ_IO) + tcp_stop(self.READ_IO) + self.timer = nil + self.READ_IO = nil + self.read_co = nil + self.read_current_co = nil + return co_wakeup(co, nil, "read timeout") + end) + tcp_start(self.READ_IO, self.fd, EVENT_READ, self.read_co) + return co_wait() end function TCP:listen(ip, port, cb) @@ -283,7 +287,7 @@ function TCP:connect(domain, port) if connected then return co_wakeup(co, true) end - return co_wakeup(co, false, '连接失败') + return co_wakeup(co, false, 'connect refused') end) self.timer = ti.timeout(self._timeout, function ( ... ) tcp_push(self.CONNECT_IO) @@ -307,14 +311,13 @@ function TCP:ssl_connect(domain, port) if not self.ssl_ctx or not self.ssl then return Log:ERROR("Create a SSL Error! :) ") end - local co = co_self() self.CONNECT_IO = tcp_pop() self.connect_current_co = co self.connect_co = co_new(function () local EVENTS = EVENT_WRITE while 1 do - local ok, EVENT = tcp_ssl_connect(self.ssl) + local ok, EVENT = tcp_ssl_do_handshak(self.ssl) if ok or not EVENT then if self.timer then self.timer:stop() @@ -325,7 +328,7 @@ function TCP:ssl_connect(domain, port) self.CONNECT_IO = nil self.connect_co = nil self.connect_current_co = nil - return co_wakeup(co, ok) + return co_wakeup(co, ok, not ok and 'ssl_connect refused') end if EVENTS ~= EVENT then EVENTS = EVENT From 0e18e7581d37a2d51c237914038de85d703d7eb1 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 31 May 2019 20:35:07 +0800 Subject: [PATCH 102/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0cfadmin=E7=9A=84docke?= =?UTF-8?q?r-compose.yaml=E6=96=87=E4=BB=B6,=20=E6=96=B9=E4=BE=BF=E5=A4=A7?= =?UTF-8?q?=E5=AE=B6docker-compose=E4=B8=80=E9=94=AE=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E4=BD=93=E9=AA=8Ccfadmin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose-with-cfadmin.yaml | 33 +++++++++++++++++++++++++ docker/docker-compose-with-nginx.yaml | 1 + docker/docker-compose-with-traefik.yaml | 2 ++ 3 files changed, 36 insertions(+) create mode 100644 docker/docker-compose-with-cfadmin.yaml diff --git a/docker/docker-compose-with-cfadmin.yaml b/docker/docker-compose-with-cfadmin.yaml new file mode 100644 index 00000000..2182bf3f --- /dev/null +++ b/docker/docker-compose-with-cfadmin.yaml @@ -0,0 +1,33 @@ +# docker-compose.yaml +version: "2" +services: + WebApp: + # container_name: Web_admin + image: candymi/cfweb:latest + restart: always + volumes: + - ../script/:/app/script/ + ports: + - "80:8080" + depends_on: + - WebDB + links: + - WebDB + networks: + - local + + WebDB: + # container_name: Web_DB + image: mysql:5.6 + restart: always + environment: + - MYSQL_ROOT_PASSWORD=123456789 + - MYSQL_DATABASE=cfadmin + volumes: + - ../lualib/admin/db/:/docker-entrypoint-initdb.d/ + ports: + - "3306:3306" + +networks: + local: + driver: bridge diff --git a/docker/docker-compose-with-nginx.yaml b/docker/docker-compose-with-nginx.yaml index 5e3d5798..97236900 100644 --- a/docker/docker-compose-with-nginx.yaml +++ b/docker/docker-compose-with-nginx.yaml @@ -3,6 +3,7 @@ version: "2" services: WebProxy: image: nginx:latest + restart: always ports: - 80:80 volumes: diff --git a/docker/docker-compose-with-traefik.yaml b/docker/docker-compose-with-traefik.yaml index 81084bad..219f0689 100644 --- a/docker/docker-compose-with-traefik.yaml +++ b/docker/docker-compose-with-traefik.yaml @@ -3,6 +3,7 @@ version: "2" services: WebProxy: image: traefik + restart: always command: --api --docker # Enables the web UI and tells Traefik to listen to docker ports: - "80:80" # The HTTP port @@ -14,6 +15,7 @@ services: WebApp: image: candymi/cfweb:latest + restart: always labels: - "traefik.port=8080" - "traefik.backend=WebApp" From 1405cd7b155ce56e62022c9a6a7a5ac6d8de802f Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 31 May 2019 20:39:21 +0800 Subject: [PATCH 103/956] =?UTF-8?q?=E5=AE=8C=E5=96=84docker-compose?= =?UTF-8?q?=E6=96=87=E4=BB=B6,=20=E6=9B=B4=E6=96=B0=E6=97=A0=E7=97=9B?= =?UTF-8?q?=E5=BF=AB=E9=80=9F=E4=BD=93=E9=AA=8Ccfadmin=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E7=9A=84=E6=96=B9=E5=BC=8F.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose-with-cfadmin.yaml | 2 +- docker/script/main.lua | 101 ++++++++++++++++++++++-- script/main.lua | 3 +- 3 files changed, 97 insertions(+), 9 deletions(-) diff --git a/docker/docker-compose-with-cfadmin.yaml b/docker/docker-compose-with-cfadmin.yaml index 2182bf3f..f4c53a28 100644 --- a/docker/docker-compose-with-cfadmin.yaml +++ b/docker/docker-compose-with-cfadmin.yaml @@ -6,7 +6,7 @@ services: image: candymi/cfweb:latest restart: always volumes: - - ../script/:/app/script/ + - script/:/app/script/ ports: - "80:8080" depends_on: diff --git a/docker/script/main.lua b/docker/script/main.lua index 146d2a66..cc07897f 100644 --- a/docker/script/main.lua +++ b/docker/script/main.lua @@ -1,16 +1,105 @@ local httpd = require "httpd" -local http = require "httpd.http" +local httpc = require "httpc" +local DB = require "DB" -local app = httpd:new("httpd") +--[[ +请按照以下步奏初始化后台: + 1. 创建一个数据库(名字任意); + 2. 请手动打开lualib/db/database.sql文件, 复制里面的SQL语句在GUI工具中执行一次; + 3. 执行完成之后, 将您填写的数据库替换database字段, 并且charset需要设置一致. +]] -app:before(function (content) - return http.ok() +local db = DB:new({ + host = 'WebDB', + port = 3306, + username = 'root', + password = '123456789', + charset = 'utf8', + database = 'cfadmin', + max = 100, +}) + +db:connect() + +-- 导入httpd对象 +local app = httpd:new("App") +-- httpd启用Cookie扩展 +app:enable_cookie() +-- httpd设置Cookie加密的密匙 +app:cookie_secure("https://github.com/CandyMi/core_framework") +-- app:cookie_secure("candymi") + +app:ws('/ws', require "ws") + +app:api('/api', function (content) + local code, response = httpc.get("https://www.baifubao.com/callback?cmd=1059&callback=phone&phone=13000000000") + return code == 200 and response or "httc请求失败" +end) + +app:use('/view', function (content) + return "

                  cfadmin v0.3

                  " +end) + + +-- 导入cf内置的admin库 +local cfadmin = require "admin" + +-- 注册后台页面路由 +cfadmin.init_page(app, db) + + +-- 这个函数仅在第一次初始化数据的时候适用 +-- 初始化完成之后, 请不要再运行. +cfadmin.init_db() + +-- 这里设置首页的显示的页面 +-- cfadmin.init_home(location or domain + path) +-- cfadmin.init_home('https://www.baidu.com') + +local view = require "admin.view" +-- 参数: +-- 1. ctx是一个http req 对象, 目前内置包括: get_method, get_args, get_path, get_raw_path, get_headers, get_cookie +-- 2. db初始化后的db对象, 方便用户直接使用. + +view.use('/admin/test1', function (ctx, db) + return "hello world" end) -app:api('/api/login', function (content) - return '{"code":200, "data":{"token":"admin","uid":1}}' +view.api('/api/admin/test2', function (ctx, db) + return '{"code":0,"msg":"hello world"}' end) +-- 这里是设置语言的地方 +-- 语言表在admin/locales内, 可参照key -> value进行填写. +-- 传入一个数组表: 索引1是key, 索引2为显示内容. + +-- cfadmin.add_locale_item('ZH-CN', { +-- {'login.form.title', '这是登录页Title'}, +-- {'dashboard.header.logo', '仪表盘 Logo'} +-- }) + +-- cfadmin.add_locale_item('EN-US', { +-- {'login.form.title', 'This is Login Page Title'}, +-- {'dashboard.header.logo', 'dashboard Logo'} +-- }) + +-- 开启页面缓存能显著提升页面渲染性能. 生产环境下建议开启. +-- 也因为cf缓存模板页面内容, 所以开发模式下不建议开启. +-- cfadmin.cached() + +-- 这个方法可以用来设置静态文件域名与前缀. +-- 如果静态文件在其它域名或者无法访问, 可以使用这个参数修改.(域名后必须加上'/') +-- cfadmin.static('/') + +-- 设置cfadmin的区域语言, 默认为: ZH-CN +-- cfadmin.set_locale('EN-US') + +-- 设置客户端静态文件ttl值内无需再次请求, 减少服务端消耗 +-- app:static('static', 30) +app:static('static') + +-- httpd监听端口 app:listen("0.0.0.0", 8080) +-- 运行 app:run() diff --git a/script/main.lua b/script/main.lua index b67ccb4d..840c62c9 100644 --- a/script/main.lua +++ b/script/main.lua @@ -1,7 +1,6 @@ local httpd = require "httpd" local httpc = require "httpc" local DB = require "DB" -local xml = require "xml2lua" --[[ 请按照以下步奏初始化后台: @@ -11,7 +10,7 @@ local xml = require "xml2lua" ]] local db = DB:new({ - host = '10.0.0.16', + host = 'localhost', port = 3306, username = 'root', password = '123456789', From 63861a4bacdec537983de0003437fb12cdd1843d Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 31 May 2019 22:38:34 +0800 Subject: [PATCH 104/956] =?UTF-8?q?=E4=BC=98=E5=8C=96dns=E7=BC=93=E5=AD=98?= =?UTF-8?q?=E7=94=9F=E6=88=90=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/dns.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lualib/protocol/dns.lua b/lualib/protocol/dns.lua index a39cdf74..1e03af93 100644 --- a/lualib/protocol/dns.lua +++ b/lualib/protocol/dns.lua @@ -79,10 +79,10 @@ local function gen_cache() local file = io.open("/etc/hosts", "r") if file then for line in file:lines() do - local ip, domain = match(line, '^([^#%t ]*)[ ]+(.+)$') + local ip, domain = match(line, '([^#%G]*)[%G]+([^%G]+)') + -- print(line, 'ip=['..(ip or '')..']', 'domain=['..(domain or '')..']') local ok, v = check_ip(ip) if ok then - domain = match(domain, '([^ ]+)') or domain if v == 4 then if not dns_cache[domain] then dns_cache[domain] = {ip = prefix..ip} From 0e60d6f67b34dd54c97d4e3486fe2fbe389a5226 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 31 May 2019 23:29:08 +0800 Subject: [PATCH 105/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0DB=E7=9A=843=E7=A7=92?= =?UTF-8?q?=E8=B6=85=E6=97=B6=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/DB/init.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index 3fc68afa..99e71e3b 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -72,9 +72,10 @@ local function DB_CREATE (opt) local db while 1 do db = mysql:new() + db:set_timeout(3) local connect, err = db:connect(opt) if connect then - break + break end db:close() Log:ERROR('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接") From a557fa93f9e04d074568b06de5dab6eb7afb72af Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 31 May 2019 23:30:04 +0800 Subject: [PATCH 106/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0admin/db/init.lua?= =?UTF-8?q?=E7=9A=84=E6=8F=92=E5=85=A5=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/db/init.lua | 42 +++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/lualib/admin/db/init.lua b/lualib/admin/db/init.lua index 525b65cb..063aeaac 100644 --- a/lualib/admin/db/init.lua +++ b/lualib/admin/db/init.lua @@ -1,41 +1,43 @@ local crypt = require "crypt" local config = require "admin.config" +local log = require "logging" +local Log = log:new({path = 'admin-db'}) + local fmt = string.format local os_time = os.time -- 作为初始化DB工作, 这个函数(must)只能运行一次. -- 一般情况下, 大家在设计完成后都会手动简历数据表并导入内容. -- 此文件仅作为作者调试与使用者开发使用, 不对此文件做任何其它保证. -local log = require "logging" -local Log = log:new({path = 'admin-db'}) +local create_admin = fmt([[ +INSERT INTO + `cfadmin_users` + (`id`, `name`, `username`, `password`, `email`, `phone`, `role`, `create_at`, `update_at`, `active`) + VALUES + ('1', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '1')]], +'管理员', 'admin', crypt.hexencode(crypt.sha1('admin')), '869646063@qq.com', '13000000000', '1', os_time(), os_time()) + +local create_admin_role = fmt([[ +INSERT INTO + cfadmin_roles + (`id`, `name`, `is_admin`, `create_at`, `update_at`, `active`) + VALUES + ('1', '管理员', '1', '%s', '%s', '1') +]], os_time(), os_time()) return function () local ret, err local db = config.db local now = os_time() - -- 初始化角色 - ret, err = db:query(fmt([[ - INSERT INTO - cfadmin_roles - (`id`, `name`, `is_admin`, `create_at`, `update_at`, `active`) - VALUES - ('1', '管理员', '1', '%s', '%s', '1') - ]], now, now)) + -- 初始化用户 + ret, err = db:query(create_admin) if not ret then Log:ERROR(err) - return nil, err end - -- 初始化用户 - ret, err = db:query(fmt([[ - INSERT INTO - `cfadmin_users` - (`name`, `username`, `password`, `email`, `phone`, `role`, `create_at`, `update_at`, `active`) - VALUES - ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '1')]], - '管理员', 'admin', crypt.hexencode(crypt.sha1('admin')), '869646063@qq.com', '13000000000', '1', now, now)) + -- 初始化角色 + ret, err = db:query(create_admin_role) if not ret then Log:ERROR(err) - return nil, err end return true, '初始化完成' end From 7bb99555b3873eeb5bfa3ae4215f7faf32f2a45a Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 31 May 2019 23:31:05 +0800 Subject: [PATCH 107/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8DUDP=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E6=89=93=E5=8D=B0=E6=97=A5=E5=BF=97=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/UDP.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lualib/internal/UDP.lua b/lualib/internal/UDP.lua index ed66586e..d45bfcdd 100644 --- a/lualib/internal/UDP.lua +++ b/lualib/internal/UDP.lua @@ -24,12 +24,9 @@ end function UDP:connect(ip, port) - if not self.udp then - return nil, "Can't Create a UDP socket." - end self.fd = udp.connect(ip, port) - if self.fd < 0 then - return nil, "a peer of connect from udp port maybe closed." + if not self.fd or self.fd <= 0 then + return nil, "Can't Creat UDP Socket" end return true end @@ -62,7 +59,10 @@ function UDP:recv(...) end function UDP:send(data) - assert(self.udp and data and self.fd and self.fd > 0, "UDP ERROR 参数不完整.") + if type(data) ~= 'string' or not self.fd or self.fd <= 0 then + Log:ERROR("Send udp Error: 不完整的参数:"..(data or '')..','..(self.fd or '-1')) + return + end return udp.send(self.fd, data, #data) end From 8a7187560f0e3cca02e5ce53d0ebcfa37bfcfcf6 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 31 May 2019 23:32:33 +0800 Subject: [PATCH 108/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=88=A4=E6=96=AD?= =?UTF-8?q?=E5=86=99=E5=85=A5=E5=A4=B1=E8=B4=A5=E5=90=8E=E7=9B=B4=E6=8E=A5?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E6=96=AD=E5=BC=80=E8=BF=9E=E6=8E=A5,=20?= =?UTF-8?q?=E4=B8=8D=E5=86=8D=E9=80=9A=E8=BF=87read=E5=88=A4=E6=96=AD?= =?UTF-8?q?=E6=98=AF=E5=90=A6=E6=96=AD=E5=BC=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/mysql.lua | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua index 5c5af623..47582ab3 100644 --- a/lualib/protocol/mysql.lua +++ b/lualib/protocol/mysql.lua @@ -401,7 +401,7 @@ end function MySQL.set_timeout(self, timeout) - return self.sock:settimeout(timeout) + return self.sock:timeout(timeout) end @@ -569,13 +569,9 @@ function MySQL.close(self) end function MySQL.send_query(self, query) - - self.packet_no = -1 - - local cmd_packet = strchar(COM_QUERY) .. query - - _send_packet(self, cmd_packet, 1 + #query) - + self.packet_no = -1 + local cmd_packet = strchar(COM_QUERY) .. query + return _send_packet(self, cmd_packet, 1 + #query) end @@ -646,20 +642,18 @@ function MySQL.read_result(self, est_nrows) return rows end - function MySQL.query(self, query, est_nrows) - - if not query or type(query) ~= "string" then - return nil, "Attemp to pass a invaild SQL." - end - - self:send_query(query) - - return self:read_result(est_nrows) - + if type(query) ~= "string" then + return nil, "Attemp to pass a invaild SQL." + end + local ok = self:send_query(query) + if not ok then + self.state = nil + return nil, 'connection already close' + end + return self:read_result(est_nrows) end - function MySQL.set_compact_arrays(self, value) self.compact = value end From d5f3b5fb21aa17e9caafad6b8d9bca327624a100 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 31 May 2019 23:34:56 +0800 Subject: [PATCH 109/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=BB=BD?= =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=9A=84cfadmin=E4=B8=8E=E4=B8=8Emysql?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E3=80=81=E8=BF=90=E8=A1=8C=E7=9A=84?= =?UTF-8?q?docker-compose.yaml=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/db/database.sql | 105 ++++++++++++++++++++++++ docker/docker-compose-with-cfadmin.yaml | 20 ++--- docker/script/ws.lua | 41 +++++++++ 3 files changed, 156 insertions(+), 10 deletions(-) create mode 100644 docker/db/database.sql create mode 100644 docker/script/ws.lua diff --git a/docker/db/database.sql b/docker/db/database.sql new file mode 100644 index 00000000..5b74d1db --- /dev/null +++ b/docker/db/database.sql @@ -0,0 +1,105 @@ +# ************************************************************ +# Sequel Pro SQL dump +# Version 4541 +# +# http://www.sequelpro.com/ +# https://github.com/sequelpro/sequelpro +# +# Host: 127.0.0.1 (MySQL 5.7.25) +# Database: cfadmin +# Generation Time: 2019-05-21 02:32:16 +0000 +# ************************************************************ + +# Dump of table cfadmin_headers +# ------------------------------------------------------------ + +CREATE TABLE IF NOT EXISTS `cfadmin_headers` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', + `name` varchar(255) NOT NULL COMMENT '头部名称', + `url` varchar(255) NOT NULL COMMENT '头部URL', + `create_at` int(11) unsigned NOT NULL COMMENT '创建时间', + `update_at` int(10) unsigned NOT NULL COMMENT '修改时间', + `active` tinyint(4) unsigned NOT NULL COMMENT '删除标志', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + + +# Dump of table cfadmin_menus +# ------------------------------------------------------------ + +CREATE TABLE IF NOT EXISTS `cfadmin_menus` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', + `parent` int(11) unsigned NOT NULL, + `name` varchar(255) NOT NULL COMMENT '菜单名称', + `url` varchar(255) DEFAULT NULL COMMENT '菜单链接', + `icon` char(255) DEFAULT NULL COMMENT '菜单图标', + `create_at` int(11) unsigned NOT NULL COMMENT '创建时间', + `update_at` int(11) unsigned NOT NULL COMMENT '更新时间', + `active` tinyint(4) unsigned NOT NULL COMMENT '删除标志', + PRIMARY KEY (`id`), + KEY `com_index` (`active`,`url`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + + +# Dump of table cfadmin_permissions +# ------------------------------------------------------------ + +CREATE TABLE IF NOT EXISTS `cfadmin_permissions` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', + `role_id` int(11) unsigned NOT NULL COMMENT '所属角色', + `menu_id` int(11) unsigned NOT NULL COMMENT '所属菜单', + `create_at` int(11) unsigned NOT NULL COMMENT '创建时间', + `update_at` int(11) unsigned NOT NULL COMMENT '修改时间', + `active` tinyint(4) unsigned NOT NULL COMMENT '是否启用', + PRIMARY KEY (`id`), + KEY `com_index` (`active`,`role_id`,`menu_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + + +# Dump of table cfadmin_roles +# ------------------------------------------------------------ + +CREATE TABLE IF NOT EXISTS `cfadmin_roles` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', + `name` varchar(255) NOT NULL COMMENT '角色名称', + `is_admin` tinyint(4) unsigned NOT NULL COMMENT '管理员标志', + `create_at` int(11) unsigned NOT NULL COMMENT '创建时间', + `update_at` int(1) unsigned NOT NULL COMMENT '修改时间', + `active` tinyint(4) unsigned NOT NULL COMMENT '删除标志', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + + +# Dump of table cfadmin_tokens +# ------------------------------------------------------------ + +CREATE TABLE IF NOT EXISTS `cfadmin_tokens` ( + `uid` int(11) unsigned NOT NULL COMMENT '用户ID', + `name` varchar(255) NOT NULL COMMENT '用户名称', + `token` varchar(255) NOT NULL COMMENT '用户TOKEN', + `create_at` int(11) unsigned NOT NULL COMMENT '登录时间', + PRIMARY KEY (`uid`) +) ENGINE=MEMORY DEFAULT CHARSET=utf8; + + + +# Dump of table cfadmin_users +# ------------------------------------------------------------ + +CREATE TABLE IF NOT EXISTS `cfadmin_users` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', + `name` varchar(255) NOT NULL COMMENT '用户名', + `username` varchar(255) NOT NULL COMMENT '用户账户', + `password` varchar(255) NOT NULL COMMENT '用户密码', + `email` varchar(255) NOT NULL COMMENT '用户邮箱', + `phone` bigint(11) unsigned NOT NULL COMMENT '用户手机', + `role` int(11) unsigned NOT NULL COMMENT '用户角色', + `create_at` int(11) unsigned NOT NULL COMMENT '创建时间', + `update_at` int(11) unsigned NOT NULL COMMENT '修改时间', + `active` tinyint(4) unsigned NOT NULL COMMENT '删除标志', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/docker/docker-compose-with-cfadmin.yaml b/docker/docker-compose-with-cfadmin.yaml index f4c53a28..03eb01b4 100644 --- a/docker/docker-compose-with-cfadmin.yaml +++ b/docker/docker-compose-with-cfadmin.yaml @@ -2,32 +2,32 @@ version: "2" services: WebApp: - # container_name: Web_admin - image: candymi/cfweb:latest + image: candymi/cfweb restart: always volumes: - - script/:/app/script/ + - ./script/:/app/script/ ports: - - "80:8080" + - 8080:8080 depends_on: - WebDB links: - - WebDB + - WebDB:WebDB networks: - - local + - Web_Net WebDB: - # container_name: Web_DB image: mysql:5.6 restart: always environment: - MYSQL_ROOT_PASSWORD=123456789 - MYSQL_DATABASE=cfadmin volumes: - - ../lualib/admin/db/:/docker-entrypoint-initdb.d/ + - ./db/:/docker-entrypoint-initdb.d/ ports: - - "3306:3306" + - 3306:3306 + networks: + - Web_Net networks: - local: + Web_Net: driver: bridge diff --git a/docker/script/ws.lua b/docker/script/ws.lua new file mode 100644 index 00000000..cf523ccd --- /dev/null +++ b/docker/script/ws.lua @@ -0,0 +1,41 @@ +local class = require "class" +local cf = require "cf" +local json = require "json" +local websocket = class("websocket") + +function websocket:ctor(opt) + self.ws = opt.ws -- websocket对象 + self.send_masked = false -- 掩码(默认为false, 不建议修改或者使用) + self.max_payload_len = 65535 -- 最大有效载荷长度(默认为65535, 不建议修改或者使用) + self.timeout = 15 -- 默认为一直等待, 非number类型会导致异常. + self.count = 0 +end + +function websocket:on_open() + print('on_open') + self.timer = cf.at(0.01, function ( ... ) -- 定时器 + self.count = self.count + 1 + self.ws:send(tostring(self.count)) + end) +end + +function websocket:on_message(data, typ) + print('on_message', self.ws, data) + self.ws:send('welcome') + -- self.ws:close(data) +end + +function websocket:on_error(error) + print('on_error', self.ws, error) +end + +function websocket:on_close(data) + print('on_close', self.ws, data) + if self.timer then -- 清理定时器 + print("清理定时器") + self.timer:stop() + self.timer = nil + end +end + +return websocket From 84731c8672173bb2a64e3eb919a6f36cb6721dd1 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 31 May 2019 23:41:24 +0800 Subject: [PATCH 110/956] =?UTF-8?q?=E8=A1=A5=E5=85=85=E5=88=A4=E6=96=ADmys?= =?UTF-8?q?ql=E5=8D=8F=E8=AE=AE=E5=86=85=E7=9A=84=E8=BF=9E=E6=8E=A5send=5F?= =?UTF-8?q?packet=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/mysql.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua index 47582ab3..294050db 100644 --- a/lualib/protocol/mysql.lua +++ b/lualib/protocol/mysql.lua @@ -527,7 +527,10 @@ function MySQL.connect(self, opts) -- print("packet content length: ", packet_len) -- print("packet content: ", _dump(concat(req, ""))) - _send_packet(self, req, packet_len) + local ok = _send_packet(self, req, packet_len) + if not ok then + return nil, "send packet was failed." + end --print("packet sent ", bytes, " bytes") From 0d4256327ef1d651f73d7d713358e8dfd99fe8ed Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 1 Jun 2019 02:12:05 +0800 Subject: [PATCH 111/956] =?UTF-8?q?=E8=B0=83=E6=95=B4TCP.lua?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/TCP.lua | 149 ++++++++++++++++++++-------------------- 1 file changed, 73 insertions(+), 76 deletions(-) diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index b55dc968..26199208 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -22,7 +22,7 @@ local tcp_stop = tcp.stop local tcp_free_ssl = tcp.free_ssl local tcp_close = tcp.close local tcp_connect = tcp.connect -local tcp_ssl_do_handshak = tcp.ssl_connect +local tcp_ssl_connect = tcp.ssl_connect local tcp_read = tcp.read local tcp_sslread = tcp.ssl_read local tcp_write = tcp.write @@ -154,94 +154,90 @@ function TCP:recv(bytes) if self.ssl then return Log:ERROR("Please use ssl_recv method :)") end - local buf, len = tcp_read(self.fd, bytes) - if buf then - return buf, len - end self.READ_IO = tcp_pop() local co = co_self() self.read_current_co = co_self() self.read_co = co_new(function ( ... ) - local buf, len = tcp_read(self.fd, bytes) - if self.timer then - self.timer:stop() - self.timer = nil - end - tcp_push(self.READ_IO) - tcp_stop(self.READ_IO) - self.READ_IO = nil - self.read_co = nil - self.read_current_co =nil - if not buf then - return co_wakeup(co) - end - return co_wakeup(co, buf, len) - end) - self.timer = ti.timeout(self._timeout, function ( ... ) - tcp_push(self.READ_IO) - tcp_stop(self.READ_IO) - self.timer = nil - self.read_co = nil - self.READ_IO = nil - self.read_current_co = nil - return co_wakeup(co, nil, "read timeout") - end) - tcp_start(self.READ_IO, self.fd, EVENT_READ, self.read_co) - return co_wait() -end - -function TCP:ssl_recv(bytes) - if not self.ssl then - return Log:ERROR("Please use recv method :)") - end - local buf, len = tcp_sslread(self.ssl, bytes) - if buf then - return buf, len - end - local co = co_self() - self.read_current_co = co_self() - self.READ_IO = tcp_pop() - self.read_co = co_new(function ( ... ) - while 1 do - local buf, len = tcp_sslread(self.ssl, bytes) - if not buf and not len then + local buf, len = tcp_read(self.fd, bytes) if self.timer then - self.timer:stop() - self.timer = nil + self.timer:stop() + self.timer = nil end tcp_push(self.READ_IO) tcp_stop(self.READ_IO) self.READ_IO = nil self.read_co = nil - self.read_current_co = nil - return co_wakeup(co) - end - if buf and len then - if self.timer then - self.timer:stop() - self.timer = nil + self.read_current_co =nil + if not buf then + return co_wakeup(co) end + return co_wakeup(co, buf, len) + end) + self.timer = ti.timeout(self._timeout, function ( ... ) tcp_push(self.READ_IO) tcp_stop(self.READ_IO) - self.READ_IO = nil + self.timer = nil self.read_co = nil + self.READ_IO = nil self.read_current_co = nil - return co_wakeup(co, buf, len) - end - co_wait() + return co_wakeup(co, nil, "read timeout") + end) + tcp_start(self.READ_IO, self.fd, EVENT_READ, self.read_co) + return co_wait() +end + +function TCP:ssl_recv(bytes) + if not self.ssl then + return Log:ERROR("Please use recv method :)") end - end) - self.timer = ti.timeout(self._timeout, function ( ... ) - tcp_push(self.READ_IO) - tcp_stop(self.READ_IO) - self.timer = nil - self.READ_IO = nil - self.read_co = nil - self.read_current_co = nil - return co_wakeup(co, nil, "read timeout") - end) - tcp_start(self.READ_IO, self.fd, EVENT_READ, self.read_co) - return co_wait() + local buf, len = tcp_sslread(self.ssl, bytes) + if not buf then + local co = co_self() + self.read_current_co = co_self() + self.READ_IO = tcp_pop() + self.read_co = co_new(function ( ... ) + while 1 do + local buf, len = tcp_sslread(self.ssl, bytes) + if not buf and not len then + if self.timer then + self.timer:stop() + self.timer = nil + end + tcp_push(self.READ_IO) + tcp_stop(self.READ_IO) + self.READ_IO = nil + self.read_co = nil + self.read_current_co = nil + return co_wakeup(co) + end + if buf and len then + if self.timer then + self.timer:stop() + self.timer = nil + end + tcp_push(self.READ_IO) + tcp_stop(self.READ_IO) + self.READ_IO = nil + self.read_co = nil + self.read_current_co = nil + return co_wakeup(co, buf, len) + end + co_wait() + end + end) + self.timer = ti.timeout(self._timeout, function ( ... ) + tcp_push(self.READ_IO) + tcp_stop(self.READ_IO) + self.timer = nil + self.READ_IO = nil + self.read_co = nil + self.read_current_co = nil + return co_wakeup(co, nil, "read timeout") + end) + tcp_start(self.READ_IO, self.fd, EVENT_READ, self.read_co) + return co_wait() + end + return buf, len end function TCP:listen(ip, port, cb) @@ -287,7 +283,7 @@ function TCP:connect(domain, port) if connected then return co_wakeup(co, true) end - return co_wakeup(co, false, 'connect refused') + return co_wakeup(co, false, '连接失败') end) self.timer = ti.timeout(self._timeout, function ( ... ) tcp_push(self.CONNECT_IO) @@ -311,13 +307,14 @@ function TCP:ssl_connect(domain, port) if not self.ssl_ctx or not self.ssl then return Log:ERROR("Create a SSL Error! :) ") end + local co = co_self() self.CONNECT_IO = tcp_pop() self.connect_current_co = co self.connect_co = co_new(function () local EVENTS = EVENT_WRITE while 1 do - local ok, EVENT = tcp_ssl_do_handshak(self.ssl) + local ok, EVENT = tcp_ssl_connect(self.ssl) if ok or not EVENT then if self.timer then self.timer:stop() @@ -328,7 +325,7 @@ function TCP:ssl_connect(domain, port) self.CONNECT_IO = nil self.connect_co = nil self.connect_current_co = nil - return co_wakeup(co, ok, not ok and 'ssl_connect refused') + return co_wakeup(co, ok) end if EVENTS ~= EVENT then EVENTS = EVENT From 6e4e41233f007a050f5246c98d9dc000d2000c79 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 1 Jun 2019 02:33:06 +0800 Subject: [PATCH 112/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0docker-compose?= =?UTF-8?q?=E6=96=87=E4=BB=B6,=20=E6=94=AF=E6=8C=81cfadmin=E5=BF=AB?= =?UTF-8?q?=E9=80=9F=E5=90=AF=E5=8A=A8=E4=BD=93=E9=AA=8C.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose-with-cfadmin.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docker/docker-compose-with-cfadmin.yaml b/docker/docker-compose-with-cfadmin.yaml index 03eb01b4..24d88702 100644 --- a/docker/docker-compose-with-cfadmin.yaml +++ b/docker/docker-compose-with-cfadmin.yaml @@ -6,6 +6,8 @@ services: restart: always volumes: - ./script/:/app/script/ + # 如需设置时区, 建议请将localtime提取到home目录下再挂在使用 + # - /etc/localtime:/etc/localtime ports: - 8080:8080 depends_on: @@ -23,6 +25,8 @@ services: - MYSQL_DATABASE=cfadmin volumes: - ./db/:/docker-entrypoint-initdb.d/ + # 如需设置时区, 建议请将localtime提取到home目录下再挂在使用 + # - /etc/localtime:/etc/localtime ports: - 3306:3306 networks: From 1af93e468377dda622f3fcc189ecf97580c565a2 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 1 Jun 2019 19:06:09 +0800 Subject: [PATCH 113/956] =?UTF-8?q?=E8=B0=83=E6=95=B4TCP=E7=9A=84read?= =?UTF-8?q?=E8=A1=8C=E4=B8=BA,=20=E5=A2=9E=E5=8A=A0=E5=B0=9D=E8=AF=95?= =?UTF-8?q?=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 11 ++++++++++- lualib/internal/TCP.lua | 25 +++++++++++++++++-------- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index aa9c3959..824e173e 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -16,7 +16,11 @@ void SETSOCKETOPT(int sockfd, int mode){ int ret = 0; /* 设置非阻塞 */ - non_blocking(sockfd); + ret = non_blocking(sockfd); + if (ret) { + LOG("ERROR", "non_blocking 设置失败."); + return exit(-1); + } /* 地址/端口重用 */ ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &Enable, sizeof(Enable)); @@ -228,6 +232,11 @@ tcp_read(lua_State *L){ } if (0 > rsize) { if (errno == EINTR) continue; + if (errno == EWOULDBLOCK) { + lua_pushlstring(L, "", 0); + lua_pushinteger(L, 0); + return 2; + } } } while(0); diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index 26199208..b8d3a0ff 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -1,4 +1,3 @@ -local ti = require "internal.Timer" local dns = require "protocol.dns" local co = require "internal.Co" local class = require "class" @@ -17,12 +16,15 @@ local co_spwan = co.spwan local co_self = co.self local co_wait = coroutine.yield +local ti = require "internal.Timer" +local ti_timeout = ti.timeout + local tcp_start = tcp.start local tcp_stop = tcp.stop local tcp_free_ssl = tcp.free_ssl local tcp_close = tcp.close local tcp_connect = tcp.connect -local tcp_ssl_connect = tcp.ssl_connect +local tcp_ssl_do_handshak = tcp.ssl_connect local tcp_read = tcp.read local tcp_sslread = tcp.ssl_read local tcp_write = tcp.write @@ -154,8 +156,15 @@ function TCP:recv(bytes) if self.ssl then return Log:ERROR("Please use ssl_recv method :)") end - self.READ_IO = tcp_pop() + local data, len = tcp_read(self.fd, bytes) + if not data then + return nil, "close" + end + if data and len > 0 then + return data, len + end local co = co_self() + self.READ_IO = tcp_pop() self.read_current_co = co_self() self.read_co = co_new(function ( ... ) local buf, len = tcp_read(self.fd, bytes) @@ -173,7 +182,7 @@ function TCP:recv(bytes) end return co_wakeup(co, buf, len) end) - self.timer = ti.timeout(self._timeout, function ( ... ) + self.timer = ti_timeout(self._timeout, function ( ... ) tcp_push(self.READ_IO) tcp_stop(self.READ_IO) self.timer = nil @@ -225,7 +234,7 @@ function TCP:ssl_recv(bytes) co_wait() end end) - self.timer = ti.timeout(self._timeout, function ( ... ) + self.timer = ti_timeout(self._timeout, function ( ... ) tcp_push(self.READ_IO) tcp_stop(self.READ_IO) self.timer = nil @@ -285,7 +294,7 @@ function TCP:connect(domain, port) end return co_wakeup(co, false, '连接失败') end) - self.timer = ti.timeout(self._timeout, function ( ... ) + self.timer = ti_timeout(self._timeout, function ( ... ) tcp_push(self.CONNECT_IO) tcp_stop(self.CONNECT_IO) self.timer = nil @@ -314,7 +323,7 @@ function TCP:ssl_connect(domain, port) self.connect_co = co_new(function () local EVENTS = EVENT_WRITE while 1 do - local ok, EVENT = tcp_ssl_connect(self.ssl) + local ok, EVENT = tcp_ssl_do_handshak(self.ssl) if ok or not EVENT then if self.timer then self.timer:stop() @@ -335,7 +344,7 @@ function TCP:ssl_connect(domain, port) co_wait() end end) - self.timer = ti.timeout(self._timeout, function ( ... ) + self.timer = ti_timeout(self._timeout, function ( ... ) tcp_push(self.CONNECT_IO) tcp_stop(self.CONNECT_IO) self.timer = nil From 86122440652301106d7c32e8db734dac8fa18f1f Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 1 Jun 2019 19:53:32 +0800 Subject: [PATCH 114/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=9F=90=E4=BA=9B?= =?UTF-8?q?=E5=B9=B3=E5=8F=B0accept=20socket=E9=BB=98=E8=AE=A4=E4=B8=BA?= =?UTF-8?q?=E9=98=BB=E5=A1=9E=E5=AF=BC=E8=87=B4=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 3 ++- lualib/internal/TCP.lua | 7 ++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 824e173e..25062fdd 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -194,6 +194,7 @@ IO_ACCEPT(CORE_P_ core_io *io, int revents){ LOG("INFO", strerror(errno)); return ; } + non_blocking(client); //在某些平台下, 这个socket是阻塞的. lua_State *co = (lua_State *) core_get_watcher_userdata(io); if (lua_status(co) == LUA_YIELD || lua_status(co) == LUA_OK){ char buf[INET6_ADDRSTRLEN]; @@ -233,7 +234,7 @@ tcp_read(lua_State *L){ if (0 > rsize) { if (errno == EINTR) continue; if (errno == EWOULDBLOCK) { - lua_pushlstring(L, "", 0); + lua_pushnil(L); lua_pushinteger(L, 0); return 2; } diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index b8d3a0ff..e151ebcd 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -157,11 +157,8 @@ function TCP:recv(bytes) return Log:ERROR("Please use ssl_recv method :)") end local data, len = tcp_read(self.fd, bytes) - if not data then - return nil, "close" - end - if data and len > 0 then - return data, len + if not len or len > 0 then + return data, not len and 'close' or len end local co = co_self() self.READ_IO = tcp_pop() From 9dc8713e2c56bc66769e6db45060c9c4c1dd7f7d Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 1 Jun 2019 20:01:02 +0800 Subject: [PATCH 115/956] =?UTF-8?q?=E8=B0=83=E6=95=B4DB=E4=B8=8ECahche?= =?UTF-8?q?=E7=9A=84=E8=BF=9E=E6=8E=A5=E6=97=A5=E5=BF=97=E4=B8=BAWARN?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/Cache/init.lua | 4 ++-- lualib/DB/init.lua | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lualib/Cache/init.lua b/lualib/Cache/init.lua index b5f3cfa3..71258a1a 100644 --- a/lualib/Cache/init.lua +++ b/lualib/Cache/init.lua @@ -51,9 +51,9 @@ local function CREATE_CACHE(opt) if ok then break end - Log:ERROR('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接") - times = times + 1 + Log:WARN('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接") rds:close() + times = times + 1 timer.sleep(3) end if not opt.INITIALIZATION then diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index 99e71e3b..e162c582 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -77,8 +77,8 @@ local function DB_CREATE (opt) if connect then break end + Log:WARN('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接") db:close() - Log:ERROR('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接") times = times + 1 timer.sleep(3) end From 017a764fb9831c7f734f0ae4c8aec2efb7eeaaec Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 2 Jun 2019 01:34:14 +0800 Subject: [PATCH 116/956] =?UTF-8?q?=E4=BC=98=E5=8C=96httpc=E7=9A=84chunked?= =?UTF-8?q?=E4=B8=8EContent-Length,=20httpc=E6=80=A7=E8=83=BD=E6=9B=B4?= =?UTF-8?q?=E9=AB=98=E6=B6=88=E8=80=97=E6=9B=B4=E5=B0=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lhttpparser/lhttpparser.c | 49 ++++++++++++++++---- lualib/httpc/protocol.lua | 66 ++++++++++++--------------- lualib/protocol/http.lua | 13 ++++++ 3 files changed, 81 insertions(+), 47 deletions(-) diff --git a/luaclib/src/lhttpparser/lhttpparser.c b/luaclib/src/lhttpparser/lhttpparser.c index 1a557353..9328bc11 100644 --- a/luaclib/src/lhttpparser/lhttpparser.c +++ b/luaclib/src/lhttpparser/lhttpparser.c @@ -14,7 +14,7 @@ lparser_request_protocol(lua_State *L){ const char *path; size_t method_len, path_len, num_headers; - struct phr_header headers[32]; + struct phr_header headers[128]; memset(headers, 0x0, sizeof(headers)); num_headers = sizeof(headers) / sizeof(headers[0]); @@ -37,7 +37,7 @@ lparser_response_protocol(lua_State *L){ size_t msg_len, num_headers; const char* msg; - struct phr_header headers[32]; + struct phr_header headers[128]; memset(headers, 0x0, sizeof(headers)); num_headers = sizeof(headers) / sizeof(headers[0]); @@ -61,16 +61,17 @@ lparser_request_header(lua_State *L){ const char *path; size_t method_len, path_len, num_headers; - struct phr_header headers[64]; + struct phr_header headers[128]; memset(headers, 0x0, sizeof(headers)); num_headers = sizeof(headers) / sizeof(headers[0]); ret = phr_parse_request(buf, buf_len, &method, &method_len, &path, &path_len, &minor_version, headers, &num_headers, 0); if (0 > ret) return 0; - lua_createtable(L, 0, 32); - for (i = 0; i < 32; i++){ - if (!headers[i].name || !headers[i].value) break; + lua_createtable(L, 0, 128); + for (i = 0; i < 128; i++){ + if (!headers[i].name || !headers[i].value) + break; lua_pushlstring(L, headers[i].name, headers[i].name_len); lua_pushlstring(L, headers[i].value, headers[i].value_len); lua_rawset(L, lua_gettop(L) - 2); @@ -88,16 +89,17 @@ lparser_response_header(lua_State *L){ size_t msg_len, num_headers; const char* msg; - struct phr_header headers[64]; + struct phr_header headers[128]; memset(headers, 0x0, sizeof(headers)); num_headers = sizeof(headers) / sizeof(headers[0]); ret = phr_parse_response(buf, buf_len, &minor_version, &status, &msg, &msg_len, headers, &num_headers, 0); if (0 > ret) return 0; - lua_createtable(L, 0, 32); - for (i = 0; i < 32; i++){ - if (!headers[i].name || !headers[i].value) break; + lua_createtable(L, 0, 128); + for (i = 0; i < 128; i++){ + if (!headers[i].name || !headers[i].value) + break; lua_pushlstring(L, headers[i].name, headers[i].name_len); lua_pushlstring(L, headers[i].value, headers[i].value_len); lua_rawset(L, lua_gettop(L) - 2); @@ -105,6 +107,32 @@ lparser_response_header(lua_State *L){ return 1; } +int +lparser_response_chunked(lua_State *L){ + size_t buf_len; + const char* data = luaL_checklstring(L, 1, &buf_len); + if (!data) return luaL_error(L, "parser_response_trunck_decode need a string buf."); + + char *buf = (char *)xcalloc(1, buf_len); + if (!buf) return luaL_error(L, "parser_response_trunck_decode create a string buf error."); + + strncpy(buf, data, buf_len); + + struct phr_chunked_decoder decoder = {}; + decoder.consume_trailer = 1; + + int last = phr_decode_chunked(&decoder, buf, &buf_len); + if (0 > last) { + lua_pushnil(L); + lua_pushinteger(L, last); + xfree(buf); + return 2; + } + lua_pushlstring(L, buf, strlen(buf)); + xfree(buf); + return 1; +} + LUAMOD_API int luaopen_httpparser(lua_State *L) { luaL_checkversion(L); @@ -113,6 +141,7 @@ luaopen_httpparser(lua_State *L) { {"parser_response_protocol", lparser_response_protocol}, {"parser_request_header", lparser_request_header}, {"parser_response_header", lparser_response_header}, + {"parser_response_chunked", lparser_response_chunked}, {NULL, NULL} }; luaL_newlib(L, httpparser_libs); diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua index fabfbd3e..4a6a4199 100644 --- a/lualib/httpc/protocol.lua +++ b/lualib/httpc/protocol.lua @@ -4,6 +4,7 @@ local HTTP = require "protocol.http" local FILEMIME = HTTP.FILEMIME local RESPONSE_PROTOCOL_PARSER = HTTP.RESPONSE_PROTOCOL_PARSER local RESPONSE_HEADER_PARSER = HTTP.RESPONSE_HEADER_PARSER +local RESPONSE_CHUNKED_PARSER = HTTP.RESPONSE_CHUNKED_PARSER local random = math.random @@ -133,7 +134,7 @@ local function httpc_response(sock, SSL) local content = {} local times = 0 while 1 do - local data, len = sock_recv(sock, SSL, 1024) + local data, len = sock_recv(sock, SSL, 2048) if not data then return nil, "A peer of remote server close this connection." end @@ -147,64 +148,55 @@ local function httpc_response(sock, SSL) return nil, "can't resolvable protocol." end local Content_Length = toint(HEADER['Content-Length'] or HEADER['content-length']) - local chunked = HEADER['Transfer-Encoding'] + local chunked = HEADER['Transfer-Encoding'] or HEADER['Transfer-encoding'] if not chunked and not Content_Length then Content_Length = 0 end if Content_Length then if (#DATA - posB) == Content_Length then - return CODE, split(DATA, posB+1, #DATA) + return CODE, split(DATA, posB + 1, #DATA) end - local content = {split(DATA, posB+1, #DATA)} + local content = {split(DATA, posB + 1, #DATA)} + local Len = #content[1] while 1 do - local data, len = sock_recv(sock, SSL, 1024) + local data, len = sock_recv(sock, SSL, 2048) if not data then return CODE, SSL.."[Content_Length] A peer of remote server close this connection." end insert(content, data) - local DATA = concat(content) - if Content_Length == #DATA then - return CODE, DATA - end + if Len + len == Content_Length then + return CODE, concat(content) + end end end if chunked and chunked == "chunked" then local content = {} if #DATA > posB then - local DATA = split(DATA, posB+1, #DATA) - if find(DATA, CRLF2) then - local body = {} - for hex, block in splite(DATA, "([%w]*)\r\n(.-)\r\n") do - local len = toint(fmt("0x%s", hex)) - if len and len == #block then - if len == 0 and block == '' then - return CODE, concat(body) - end - insert(body, block) - end - end - end - insert(content, DATA) + local buf = split(DATA, posB + 1, #DATA) + data, len = RESPONSE_CHUNKED_PARSER(buf) + if len == -1 then + return nil, "错误的http trunked" + end + if data then + local Pos = find(data, CRLF..(0)..CRLF2) + return CODE, split(data, 1, Pos and Pos - #CRLF2 - 1 or -1) + end + insert(content, buf) end while 1 do - local data, len = sock_recv(sock, SSL, 1024) + local data, len = sock_recv(sock, SSL, 2048) if not data then return CODE, SSL.."[chunked] A peer of remote server close this connection A." end insert(content, data) - local DATA = concat(content) - if find(DATA, CRLF2) then - local body = {} - for hex, block in splite(DATA, "([%a%d]*)\r\n(.-)\r\n") do - local len = toint(fmt("0x%s", hex)) - if len and len == #block then - if len == 0 and block == '' then - return CODE, concat(body) - end - insert(body, block) - end - end - end + local data, len = RESPONSE_CHUNKED_PARSER(concat(content)) + if len == -1 then + return nil, "错误的http trunked. 1" + end + if data then + local Pos = find(data, CRLF..(0)..CRLF2) + return CODE, split(data, 1, Pos and Pos - #CRLF2 - 1 or -1) + end end end end diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index a0001e97..07dd6690 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -34,6 +34,7 @@ local REQUEST_PROTOCOL_PARSER = httpparser.parser_request_protocol local RESPONSE_PROTOCOL_PARSER = httpparser.parser_response_protocol local REQUEST_HEADER_PARSER = httpparser.parser_request_header local RESPONSE_HEADER_PARSER = httpparser.parser_response_header +local RESPONSE_CHUNKED_PARSER = httpparser.parser_response_chunked local type = type local assert = assert @@ -180,15 +181,27 @@ local HTTP_PROTOCOL = { } -- 以下为 HTTP Client 所需所用方法 + +-- 解析回应头部 function HTTP_PROTOCOL.RESPONSE_HEADER_PARSER(header) return RESPONSE_HEADER_PARSER(header) end +-- 解析回应协议 function HTTP_PROTOCOL.RESPONSE_PROTOCOL_PARSER(protocol) local VERSION, CODE, STATUS = RESPONSE_PROTOCOL_PARSER(protocol) return CODE end +-- 解析回应chunked +function HTTP_PROTOCOL.RESPONSE_CHUNKED_PARSER (data) + local ok, data, pos = pcall(RESPONSE_CHUNKED_PARSER, data) + if not ok then + return nil, -1 + end + return data, pos +end + -- 以下为 HTTP Server 所需所用方法 local function REQUEST_STATUCODE_RESPONSE(code) return HTTP_CODE[code] or "attempt to Passed A Invaid Code to response message." From 09e2e7a97dfd669b772249ec7ba05300df73b4aa Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 2 Jun 2019 02:24:56 +0800 Subject: [PATCH 117/956] =?UTF-8?q?=E4=BC=98=E5=8C=96httpc=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/protocol.lua | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua index 4a6a4199..7e18ca60 100644 --- a/lualib/httpc/protocol.lua +++ b/lualib/httpc/protocol.lua @@ -136,7 +136,7 @@ local function httpc_response(sock, SSL) while 1 do local data, len = sock_recv(sock, SSL, 2048) if not data then - return nil, "A peer of remote server close this connection." + return nil, SSL.." A peer of remote server close this connection." end insert(content, data) local DATA = concat(content) @@ -145,7 +145,7 @@ local function httpc_response(sock, SSL) CODE = RESPONSE_PROTOCOL_PARSER(split(DATA, 1, posB)) HEADER = RESPONSE_HEADER_PARSER(split(DATA, 1, posB)) if not CODE or not HEADER then - return nil, "can't resolvable protocol." + return nil, SSL.." can't resolvable protocol." end local Content_Length = toint(HEADER['Content-Length'] or HEADER['content-length']) local chunked = HEADER['Transfer-Encoding'] or HEADER['Transfer-encoding'] @@ -161,10 +161,11 @@ local function httpc_response(sock, SSL) while 1 do local data, len = sock_recv(sock, SSL, 2048) if not data then - return CODE, SSL.."[Content_Length] A peer of remote server close this connection." + return nil, SSL.."[Content_Length] A peer of remote server close this connection." end insert(content, data) - if Len + len == Content_Length then + Len = Len + len + if Len >= Content_Length then return CODE, concat(content) end end @@ -175,7 +176,7 @@ local function httpc_response(sock, SSL) local buf = split(DATA, posB + 1, #DATA) data, len = RESPONSE_CHUNKED_PARSER(buf) if len == -1 then - return nil, "错误的http trunked" + return nil, SSL.." 错误的http trunked" end if data then local Pos = find(data, CRLF..(0)..CRLF2) @@ -191,7 +192,7 @@ local function httpc_response(sock, SSL) insert(content, data) local data, len = RESPONSE_CHUNKED_PARSER(concat(content)) if len == -1 then - return nil, "错误的http trunked. 1" + return nil, SSL.." 错误的http trunked. 1" end if data then local Pos = find(data, CRLF..(0)..CRLF2) From 6184fe8e99d1e6843cb5c4c86ae1192b98dfc10f Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 2 Jun 2019 03:40:41 +0800 Subject: [PATCH 118/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0Dockerfile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 48c53562..55ab9ff1 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -10,8 +10,8 @@ WORKDIR /root/download COPY ./lua-5.3.5.tar.gz /root/download/lua.tar.gz COPY ./libev-4.25.tar.gz /root/download/libev.tar.gz -RUN yum install nc vim autoconf git readline-devel openssl-devel -y \ - && yum groupinstall "development tools" -y \ +RUN rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 \ + && yum install nc gcc vim autoconf make git readline-devel openssl-devel -y \ && tar zxvf lua.tar.gz && cd lua* && make linux MYCFLAGS=-fPIC && make install && cd .. \ && tar zxvf libev.tar.gz && cd libev* && ./configure --prefix=/usr/local && make && make install \ && rm -rf /roo/download /var/cache/yum \ From 8ed0d41e7e52d52b0bddbaad0d609841797acd88 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 3 Jun 2019 11:16:01 +0800 Subject: [PATCH 119/956] =?UTF-8?q?=E8=B0=83=E6=95=B4websocket=20server?= =?UTF-8?q?=E4=BB=A3=E7=A0=81,=20=E8=A7=A3=E5=86=B3=E7=89=B9=E6=AE=8A?= =?UTF-8?q?=E5=9C=BA=E6=99=AF=E4=B8=8B=E5=8F=AF=E8=83=BD=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E7=9A=84=E5=BE=AA=E7=8E=AF=E5=BC=95=E7=94=A8=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/websocket/server.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lualib/protocol/websocket/server.lua b/lualib/protocol/websocket/server.lua index 734cfcc3..6f2d5ab7 100644 --- a/lualib/protocol/websocket/server.lua +++ b/lualib/protocol/websocket/server.lua @@ -109,15 +109,15 @@ function websocket:start() local on_message = cls.on_message local on_error = cls.on_error local on_close = cls.on_close + self.cls = nil + self.sock._timeout = cls.timeout + self.send_masked = cls.send_masked + self.max_payload_len = cls.max_payload_len or 65535 local ok, err = pcall(on_open, cls) if not ok then self.sock = nil return Log:ERROR(err) end - self.cls = nil - self.sock._timeout = cls.timeout - self.send_masked = cls.send_masked - self.max_payload_len = cls.max_payload_len or 65535 while 1 do local data, typ, err = _recv_frame(sock, self.max_payload_len, self.send_masked) if (not data and not typ) or typ == 'close' then From 359dfd95bf2a83e20aa5b74ab29ce0a50772d2b6 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 3 Jun 2019 12:05:11 +0800 Subject: [PATCH 120/956] =?UTF-8?q?=E8=B0=83=E6=95=B4picohttpparser?= =?UTF-8?q?=E7=9A=84Makefile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lhttpparser/Makefile | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/luaclib/src/lhttpparser/Makefile b/luaclib/src/lhttpparser/Makefile index 69dac294..3f71bfe3 100644 --- a/luaclib/src/lhttpparser/Makefile +++ b/luaclib/src/lhttpparser/Makefile @@ -11,9 +11,11 @@ CC = cc # 如果确认是intel的CPU并且有SSE4可以自行开启这个编译参数, 经过测试http解析速度至少快2倍 # 这里为了兼容AMD CPU不作为默认开启选项 -# CFLAGS = -O3 -Wall -DNDEBUG -fPIC -msse4 +# CFLAGS = -O3 -w -shared -fPIC -DNDEBUG -msse4 -CFLAGS = -O3 -Wall -DNDEBUG -fPIC +CFLAGS = -O3 -w -shared -fPIC -DNDEBUG + +DLL = -lcore INCLUDE = -I/usr/local/include LIB = -L/usr/local/lib @@ -24,11 +26,11 @@ lhttpparser.o: lhttpparser.c httpparser.h OBJS = httpparser.o lhttpparser.o build: $(OBJS) - $(CC) -o httpparser.so $(OBJS) $(INCLUDE) $(LIB) -O3 -shared -fPIC -lcore + $(CC) -o httpparser.so $(OBJS) $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) mv httpparser.so ../../ rebuild: $(OBJS) - $(CC) -o httpparser.so $(OBJS) $(INCLUDE) $(LIB) -O3 -shared -fPIC -lcore + $(CC) -o httpparser.so $(OBJS) $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) mv httpparser.so ../../ clean: From 9e1c6d17e57053fb80ff1c82e6ffec746a734a31 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 3 Jun 2019 14:04:30 +0800 Subject: [PATCH 121/956] =?UTF-8?q?=E4=BC=98=E5=8C=96httpd=E4=B8=8Ehttpc?= =?UTF-8?q?=E6=B5=81=E7=A8=8B,=20=E4=BC=98=E5=8C=96=E5=8D=8F=E8=AE=AE?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=E5=99=A8=E7=9A=84=E7=9B=B8=E5=85=B3=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lhttpparser/lhttpparser.c | 164 ++++++++++---------------- lualib/httpc/protocol.lua | 15 ++- lualib/httpd/Router.lua | 5 +- lualib/protocol/http.lua | 51 +++++--- 4 files changed, 104 insertions(+), 131 deletions(-) diff --git a/luaclib/src/lhttpparser/lhttpparser.c b/luaclib/src/lhttpparser/lhttpparser.c index 9328bc11..2a4290fd 100644 --- a/luaclib/src/lhttpparser/lhttpparser.c +++ b/luaclib/src/lhttpparser/lhttpparser.c @@ -4,54 +4,34 @@ #include "httpparser.h" int -lparser_request_protocol(lua_State *L){ - size_t buf_len; - const char* buf = luaL_checklstring(L, 1, &buf_len); - if (!buf) return luaL_error(L, "lparser_request_protocol need a str buf."); +lparser_response_chunked(lua_State *L){ + size_t buf_len; + const char* data = luaL_checklstring(L, 1, &buf_len); + if (!data) return luaL_error(L, "parser_response_trunck_decode need a string buf."); - int min, ret; - const char *method; - const char *path; - size_t method_len, path_len, num_headers; + char *buf = (char *)xcalloc(1, buf_len); + if (!buf) return luaL_error(L, "parser_response_trunck_decode create a string buf error."); - struct phr_header headers[128]; - memset(headers, 0x0, sizeof(headers)); + strncpy(buf, data, buf_len); - num_headers = sizeof(headers) / sizeof(headers[0]); - ret = phr_parse_request(buf, buf_len, &method, &method_len, &path, &path_len, &min, headers, &num_headers, 0); - if (0 > ret) return 0; + struct phr_chunked_decoder decoder = {}; + decoder.consume_trailer = 1; - lua_pushlstring(L, method, method_len); // METHOD - lua_pushlstring(L, path, path_len); // PATH - lua_pushnumber(L, min > 0 ? 1.1 : 1.0); // VERSION - return 3; + int last = phr_decode_chunked(&decoder, buf, &buf_len); + if (0 > last) { + lua_pushnil(L); + lua_pushinteger(L, last); + xfree(buf); + return 2; + } + lua_pushlstring(L, buf, strlen(buf)); + xfree(buf); + return 1; } -int -lparser_response_protocol(lua_State *L){ - size_t buf_len; - const char* buf = luaL_checklstring(L, 1, &buf_len); - if (!buf) return luaL_error(L, "parser_response_protocol need a str buf."); - - int status, minor_version, ret; - size_t msg_len, num_headers; - const char* msg; - - struct phr_header headers[128]; - memset(headers, 0x0, sizeof(headers)); - - num_headers = sizeof(headers) / sizeof(headers[0]); - ret = phr_parse_response(buf, buf_len, &minor_version, &status, &msg, &msg_len, headers, &num_headers, 0); - if (0 > ret) return 0; - - lua_pushnumber(L, minor_version > 0 ? 1.1 : 1.0); // VERSION - lua_pushinteger(L, status); // STATUS CODE - lua_pushlstring(L, msg, msg_len); // STATUS MSG - return 3; -} -int -lparser_request_header(lua_State *L){ +static int +lparser_http_request(lua_State *L){ size_t buf_len; const char* buf = luaL_checklstring(L, 1, &buf_len); if (!buf) return luaL_error(L, "lparser_request_header need a str buf."); @@ -68,81 +48,61 @@ lparser_request_header(lua_State *L){ ret = phr_parse_request(buf, buf_len, &method, &method_len, &path, &path_len, &minor_version, headers, &num_headers, 0); if (0 > ret) return 0; - lua_createtable(L, 0, 128); - for (i = 0; i < 128; i++){ - if (!headers[i].name || !headers[i].value) - break; - lua_pushlstring(L, headers[i].name, headers[i].name_len); - lua_pushlstring(L, headers[i].value, headers[i].value_len); - lua_rawset(L, lua_gettop(L) - 2); - } - return 1; -} - -int -lparser_response_header(lua_State *L){ - size_t buf_len; - const char* buf = luaL_checklstring(L, 1, &buf_len); - if (!buf) return luaL_error(L, "parser_response_protocol need a str buf."); - - int status, minor_version, ret, i; - size_t msg_len, num_headers; - const char* msg; - - struct phr_header headers[128]; - memset(headers, 0x0, sizeof(headers)); - - num_headers = sizeof(headers) / sizeof(headers[0]); - ret = phr_parse_response(buf, buf_len, &minor_version, &status, &msg, &msg_len, headers, &num_headers, 0); - if (0 > ret) return 0; + lua_pushlstring(L, method, method_len); // METHOD + lua_pushlstring(L, path, path_len); // PATH + lua_pushnumber(L, minor_version > 0 ? 1.1 : 1.0); // VERSION lua_createtable(L, 0, 128); for (i = 0; i < 128; i++){ - if (!headers[i].name || !headers[i].value) - break; - lua_pushlstring(L, headers[i].name, headers[i].name_len); - lua_pushlstring(L, headers[i].value, headers[i].value_len); - lua_rawset(L, lua_gettop(L) - 2); + if (!headers[i].name || !headers[i].value) + break; + lua_pushlstring(L, headers[i].name, headers[i].name_len); + lua_pushlstring(L, headers[i].value, headers[i].value_len); + lua_rawset(L, lua_gettop(L) - 2); } - return 1; + return 4; } -int -lparser_response_chunked(lua_State *L){ +static int +lparser_http_response(lua_State *L){ size_t buf_len; - const char* data = luaL_checklstring(L, 1, &buf_len); - if (!data) return luaL_error(L, "parser_response_trunck_decode need a string buf."); - - char *buf = (char *)xcalloc(1, buf_len); - if (!buf) return luaL_error(L, "parser_response_trunck_decode create a string buf error."); - - strncpy(buf, data, buf_len); - - struct phr_chunked_decoder decoder = {}; - decoder.consume_trailer = 1; - - int last = phr_decode_chunked(&decoder, buf, &buf_len); - if (0 > last) { - lua_pushnil(L); - lua_pushinteger(L, last); - xfree(buf); - return 2; + const char* buf = luaL_checklstring(L, 1, &buf_len); + if (!buf) return luaL_error(L, "parser_response_protocol need a str buf."); + + int status, minor_version, ret, i; + size_t msg_len, num_headers; + const char* msg; + + struct phr_header headers[128]; + memset(headers, 0x0, sizeof(headers)); + + num_headers = sizeof(headers) / sizeof(headers[0]); + ret = phr_parse_response(buf, buf_len, &minor_version, &status, &msg, &msg_len, headers, &num_headers, 0); + if (0 > ret) return 0; + + lua_pushnumber(L, minor_version > 0 ? 1.1 : 1.0); // VERSION + lua_pushinteger(L, status); // STATUS CODE + lua_pushlstring(L, msg, msg_len); // STATUS MSG + + lua_createtable(L, 0, 128); + for (i = 0; i < 128; i++){ + if (!headers[i].name || !headers[i].value) + break; + lua_pushlstring(L, headers[i].name, headers[i].name_len); + lua_pushlstring(L, headers[i].value, headers[i].value_len); + lua_rawset(L, lua_gettop(L) - 2); } - lua_pushlstring(L, buf, strlen(buf)); - xfree(buf); - return 1; + return 4; } LUAMOD_API int luaopen_httpparser(lua_State *L) { luaL_checkversion(L); luaL_Reg httpparser_libs[] = { - {"parser_request_protocol", lparser_request_protocol}, - {"parser_response_protocol", lparser_response_protocol}, - {"parser_request_header", lparser_request_header}, - {"parser_response_header", lparser_response_header}, - {"parser_response_chunked", lparser_response_chunked}, - {NULL, NULL} + {"parser_response_chunked", lparser_response_chunked}, + {"parser_http_request", lparser_http_request}, + {"parser_http_response", lparser_http_response}, + {NULL, NULL} }; luaL_newlib(L, httpparser_libs); return 1; diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua index 7e18ca60..0d716899 100644 --- a/lualib/httpc/protocol.lua +++ b/lualib/httpc/protocol.lua @@ -2,8 +2,7 @@ local tcp = require "internal.TCP" local HTTP = require "protocol.http" local FILEMIME = HTTP.FILEMIME -local RESPONSE_PROTOCOL_PARSER = HTTP.RESPONSE_PROTOCOL_PARSER -local RESPONSE_HEADER_PARSER = HTTP.RESPONSE_HEADER_PARSER +local PARSER_HTTP_RESPONSE = HTTP.PARSER_HTTP_RESPONSE local RESPONSE_CHUNKED_PARSER = HTTP.RESPONSE_CHUNKED_PARSER @@ -129,7 +128,7 @@ local function httpc_response(sock, SSL) if not sock then return nil, "Can't used this method before other httpc method.." end - local CODE, HEADER, BODY + local VERSION, CODE, STATUS, HEADER, BODY local Content_Length local content = {} local times = 0 @@ -142,15 +141,19 @@ local function httpc_response(sock, SSL) local DATA = concat(content) local posA, posB = find(DATA, CRLF2) if posB then - CODE = RESPONSE_PROTOCOL_PARSER(split(DATA, 1, posB)) - HEADER = RESPONSE_HEADER_PARSER(split(DATA, 1, posB)) + VERSION, CODE, STATUS, HEADER = PARSER_HTTP_RESPONSE(DATA) + -- CODE = RESPONSE_PROTOCOL_PARSER(split(DATA, 1, posB)) + -- HEADER = RESPONSE_HEADER_PARSER(split(DATA, 1, posB)) if not CODE or not HEADER then return nil, SSL.." can't resolvable protocol." end local Content_Length = toint(HEADER['Content-Length'] or HEADER['content-length']) local chunked = HEADER['Transfer-Encoding'] or HEADER['Transfer-encoding'] if not chunked and not Content_Length then - Content_Length = 0 + return nil, "不支持的请求体解析方式:"..( + (HEADER['Content-Length'] or HEADER['content-length']) or + (HEADER['Transfer-Encoding'] or HEADER['Transfer-encoding']) or + "未知的解析方式") end if Content_Length then if (#DATA - posB) == Content_Length then diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index 445e315b..3465ad70 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -1,9 +1,6 @@ local log = require "logging" local Log = log:new({dump = true, path = 'httpd-Router'}) -local crypt = require "crypt" -local hexencode = crypt.hexencode - local math = math local string = string local split = string.sub @@ -43,7 +40,7 @@ local function hex_route(route) for r in splite(route, '/([^/%?]+)') do tab[#tab + 1] = r end - return hexencode(concat(tab)) + return concat(tab) end local function registery_static (prefix, route_type) diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index 07dd6690..f4a6365d 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -9,7 +9,7 @@ local crypt = require "crypt" local sha1 = crypt.sha1 local base64 = crypt.base64encode local now = sys.now -local DATE = os.date +local DATE = require("sys").date local insert = table.insert local form = require "httpd.Form" @@ -30,10 +30,8 @@ local ROUTE_FIND = Router.find local ROUTE_REGISTERY = Router.registery local httpparser = require "httpparser" -local REQUEST_PROTOCOL_PARSER = httpparser.parser_request_protocol -local RESPONSE_PROTOCOL_PARSER = httpparser.parser_response_protocol -local REQUEST_HEADER_PARSER = httpparser.parser_request_header -local RESPONSE_HEADER_PARSER = httpparser.parser_response_header +local PARSER_HTTP_REQUEST = httpparser.parser_http_request +local PARSER_HTTP_RESPONSE = httpparser.parser_http_response local RESPONSE_CHUNKED_PARSER = httpparser.parser_response_chunked local type = type @@ -180,17 +178,22 @@ local HTTP_PROTOCOL = { [4] = "WS", } --- 以下为 HTTP Client 所需所用方法 - --- 解析回应头部 -function HTTP_PROTOCOL.RESPONSE_HEADER_PARSER(header) - return RESPONSE_HEADER_PARSER(header) +-- 解析http请求 +function HTTP_PROTOCOL.PARSER_HTTP_REQUEST (buffer) + local ok, method, path, version, header = pcall(PARSER_HTTP_REQUEST, buffer) + if not ok then + return nil + end + return method, path, version, header end --- 解析回应协议 -function HTTP_PROTOCOL.RESPONSE_PROTOCOL_PARSER(protocol) - local VERSION, CODE, STATUS = RESPONSE_PROTOCOL_PARSER(protocol) - return CODE +-- 解析http回应 +function HTTP_PROTOCOL.PARSER_HTTP_RESPONSE (buffer) + local ok, version, code, status, header = pcall(PARSER_HTTP_RESPONSE, buffer) + if not ok then + return nil + end + return version, code, status, header end -- 解析回应chunked @@ -390,6 +393,15 @@ local function Switch_Protocol(http, cls, sock, header, method, version, path, i return wsserver:new({cls = cls, sock = sock}):start() end +local response = {nil, CRLF2, nil} +local function send_response (sock, headers, body) + response[1] = concat(headers, CRLF) + response[3] = body + local ok = sock:send(concat(response)) + response[1], response[3] = nil, nil + return ok +end + function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) local buffers = {} local ttl = http.ttl @@ -413,9 +425,8 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) local CRLF_START, CRLF_END = find(buffer, CRLF2) if CRLF_START and CRLF_END then local start = now() - local PROTOCOL_START, PROTOCOL_END = find(buffer, CRLF) - local METHOD, PATH, VERSION = REQUEST_PROTOCOL_PARSER(buffer) -- 协议有问题返回400 + local METHOD, PATH, VERSION, HEADER = PARSER_HTTP_REQUEST(buffer) if not METHOD or not PATH or not VERSION then sock:send(ERROR_RESPONSE(http, 400, PATH, ipaddr, METHOD or "GET", now() - start)) return sock:close() @@ -426,8 +437,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) return sock:close() end -- 没有HEADER返回400 - local HEADER = REQUEST_HEADER_PARSER(buffer) - if not HEADER then + if not HEADER or not next(HEADER) then sock:send(ERROR_RESPONSE(http, 400, PATH, ipaddr, METHOD, now() - start)) return sock:close() end @@ -636,7 +646,10 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) if Connection == 'Connection: keep-alive' then header[#header+1] = "Keep-Alive: timeout="..timeout end - sock:send(concat({concat(header, CRLF), CRLF2, body})) + local ok = send_response(sock, header, body) + if not ok then + return sock:close() + end http:tolog(statucode, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, now() - start) if statucode ~= 200 or Connection ~= 'Connection: keep-alive' then return sock:close() From e6d2a473a041193ca0ad3e8f710f4c9b5293b16d Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 3 Jun 2019 14:13:44 +0800 Subject: [PATCH 122/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8DTCP=E7=9A=84backlog?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E9=85=8D=E7=BD=AE=E7=9A=84=E9=97=AE=E9=A2=98?= =?UTF-8?q?,=20=E4=BC=98=E5=8C=96=E7=BC=93=E5=AD=98IO=E5=AF=B9=E8=B1=A1?= =?UTF-8?q?=E5=AD=98=E5=8F=96=E6=8F=90=E5=8D=87=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/TCP.lua | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index e151ebcd..ffa39b9e 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -1,15 +1,16 @@ -local dns = require "protocol.dns" -local co = require "internal.Co" local class = require "class" -local tcp = require "tcp" + local log = require "logging" local Log = log:new({ dump = true, path = 'internal-TCP' }) local split = string.sub local insert = table.insert local remove = table.remove + +local dns = require "protocol.dns" local dns_resolve = dns.resolve +local co = require "internal.Co" local co_new = co.new local co_wakeup = co.wakeup local co_spwan = co.spwan @@ -19,6 +20,7 @@ local co_wait = coroutine.yield local ti = require "internal.Timer" local ti_timeout = ti.timeout +local tcp = require "tcp" local tcp_start = tcp.start local tcp_stop = tcp.stop local tcp_free_ssl = tcp.free_ssl @@ -47,7 +49,7 @@ local function tcp_pop() end local function tcp_push(tcp) - insert(POOL, tcp) + POOL[#POOL+1] = tcp end local TCP = class("TCP") @@ -248,7 +250,7 @@ end function TCP:listen(ip, port, cb) self.LISTEN_IO = tcp_pop() - self.fd = tcp_new_server_fd(ip, port) + self.fd = tcp_new_server_fd(ip, port, self._backlog) if not self.fd then return Log:ERROR("this IP and port Create A bind or listen method Faild! :) ") end @@ -260,7 +262,7 @@ function TCP:listen(ip, port, cb) end end end) - return tcp.listen(self.LISTEN_IO, self.fd, self.co, self._backlog) + return tcp_listen(self.LISTEN_IO, self.fd, self.co) end function TCP:connect(domain, port) From 56ba1ad3a9b91213aac375401f652ccc78f5f353 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 3 Jun 2019 14:26:43 +0800 Subject: [PATCH 123/956] =?UTF-8?q?cf=E5=BA=93=E6=96=B0=E5=A2=9Esleep(0)?= =?UTF-8?q?=E4=B8=BB=E5=8A=A8=E8=AE=A9=E5=87=BA=E5=8D=8F=E7=A8=8B=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E6=9D=83=E8=AF=AD=E4=B9=89,=20=E4=BC=98=E5=8C=96TCP?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1=E7=9A=84=E5=88=9B=E5=BB=BA=E9=80=9F=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/cf/init.lua | 13 +++++++++++++ lualib/internal/TCP.lua | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lualib/cf/init.lua b/lualib/cf/init.lua index 5586e125..c05c6dc8 100644 --- a/lualib/cf/init.lua +++ b/lualib/cf/init.lua @@ -21,8 +21,21 @@ function cf.at(repeats, func) return at(repeats, func) end +-- 新增主动让出协程执行权的功能 +local function yield () + local co = self() + fork(function (...) + wakeup(co) + end) + return wait() +end +cf.yield = yield + -- 协程休眠指定时间 function cf.sleep(time) + if time == 0 then + return yield() + end return sleep(time) end diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index ffa39b9e..00c47a5a 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -21,6 +21,7 @@ local ti = require "internal.Timer" local ti_timeout = ti.timeout local tcp = require "tcp" +local tcp_new = tcp.new local tcp_start = tcp.start local tcp_stop = tcp.stop local tcp_free_ssl = tcp.free_ssl @@ -45,7 +46,7 @@ local function tcp_pop() if #POOL > 0 then return remove(POOL) end - return tcp.new() + return tcp_new() end local function tcp_push(tcp) From f3e7b61e34561f525261cd03f1f586d744a4f4fb Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 3 Jun 2019 14:36:05 +0800 Subject: [PATCH 124/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E8=BE=93=E5=87=BA=E6=8F=90=E5=8D=87httpd=E6=95=B4=E4=BD=93?= =?UTF-8?q?=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/init.lua | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua index 5a16cc7b..eb58a135 100644 --- a/lualib/httpd/init.lua +++ b/lualib/httpd/init.lua @@ -12,7 +12,10 @@ local ipairs = ipairs local fmt = string.format local match = string.match local io_open = io.open +local io_write = io.write +local io_flush = io.flush local os_date = os.date +local toint = math.tointeger -- 请求解析 local EVENT_DISPATCH = HTTP.EVENT_DISPATCH @@ -137,23 +140,36 @@ function httpd:log(path) end function httpd:tolog(code, path, ip, ip_list, method, speed) - if self.logging then - self.logging:dump(fmt("[%s] - %s - %s - %s - %s - %d - req_time: %0.6f/Sec\n", os_date("%Y/%m/%d %H:%M:%S"), ip, ip_list, path, method, code, speed)) - end - print(fmt("[%s] - %s - %s - %s - %s - %d - req_time: %0.6f/Sec", os_date("%Y/%m/%d %H:%M:%S"), ip, ip_list, path, method, code, speed)) + if self.logging then + self.logging:dump(fmt("[%s] - %s - %s - %s - %s - %d - req_time: %0.6f/Sec\n", os_date("%Y/%m/%d %H:%M:%S"), ip, ip_list, path, method, code, speed)) + end + if self.output then + io_write(fmt("[%s] - %s - %s - %s - %s - %d - req_time: %0.6f/Sec\n", os_date("%Y/%m/%d %H:%M:%S"), ip, ip_list, path, method, code, speed)) + end end -- 监听请求 -function httpd:listen(ip, port) - self.IO:listen(ip, port, function (fd, ipaddr) - return EVENT_DISPATCH(fd, match(ipaddr, '^::[f]+:(.+)') or ipaddr, self) - end) - return self +function httpd:listen(ip, port, backlog) + if type(backlog) == 'number' then + self.IO:set_backlog(toint(backlog)) + end + self.IO:listen(ip, port, function (fd, ipaddr) + return EVENT_DISPATCH(fd, match(ipaddr, '^::[f]+:(.+)') or ipaddr, self) + end, backlog) + return self end -- 正确的运行方式 function httpd:run() - return cf.wait() + local output = io.output() + if io.type(output) == 'file' then + self.output = true + output:setvbuf("full", 1 << 20) + cf.at(0.1, function () + return io_flush() -- 定期刷新缓冲, 日志缓冲平凡导致的性能问题 + end) + end + return cf.wait() end return httpd From 550b277b3efddf3e3c07dd2172f3e36751bbc7f1 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 3 Jun 2019 14:37:22 +0800 Subject: [PATCH 125/956] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E6=97=A0=E7=94=A8?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua index eb58a135..759ca7d3 100644 --- a/lualib/httpd/init.lua +++ b/lualib/httpd/init.lua @@ -155,7 +155,7 @@ function httpd:listen(ip, port, backlog) end self.IO:listen(ip, port, function (fd, ipaddr) return EVENT_DISPATCH(fd, match(ipaddr, '^::[f]+:(.+)') or ipaddr, self) - end, backlog) + end) return self end From c245369592e811ee9784efc359033994e23f0d65 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 4 Jun 2019 00:18:26 +0800 Subject: [PATCH 126/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0httpc=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_httpc.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/script/test_httpc.lua b/script/test_httpc.lua index 9fd339da..fc92b7c6 100644 --- a/script/test_httpc.lua +++ b/script/test_httpc.lua @@ -7,7 +7,7 @@ local now = system.now require "utils" -cf.timeout(2, function ( ... ) +cf.timeout(3, function ( ... ) cf.fork(function ( ... ) local t1 = now() print("开始时间:", t1) @@ -32,7 +32,7 @@ cf.timeout(2, function ( ... ) print(code, body) -- http 上传文件示例 - local code, body = httpc.file('http://localhost:8080/view', nil, { + local code, body = httpc.file('http://localhost:8080/api', nil, { {name='1', filename='1.jpg', file='1', type='abc'}, {name='2', filename='2.jpg', file='2', type='abc'}, }) @@ -75,7 +75,7 @@ cf.timeout(1, function ( ... ) end) end) -cf.timeout(3, function () +cf.timeout(5, function () cf.fork(function ( ... ) local hc = http:new {} local t1 = now() @@ -112,7 +112,7 @@ cf.timeout(3, function () local t2 = now() print("结束时间:", t1, "总耗时:", t2 - t1) - require('logging'):new():DEBUG(response, "回应数量: " .. #response) + require('logging'):new({path = 'test_httpc', dump=true}):DEBUG(response, "回应数量: " .. #response) hc:close() end) end) From d87d2acb5942d0cd935396f6a8267bc2a9403dfd Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 4 Jun 2019 00:24:55 +0800 Subject: [PATCH 127/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E6=89=93=E5=8D=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/logging/init.lua | 54 +++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua index c6171c6d..7c406212 100644 --- a/lualib/logging/init.lua +++ b/lualib/logging/init.lua @@ -3,7 +3,6 @@ local class = require "class" local system = require "system" local now = system.now local type = type -local print = print local assert = assert local pairs = pairs local tostring = tostring @@ -11,8 +10,10 @@ local getmetatable = getmetatable local modf = math.modf local debug_getinfo = debug.getinfo -local os_date = os.date +local os_date = require("sys").date local io_open = io.open +local io_write = io.write +local io_type = io.type local format = string.format local concat = table.concat local modf = math.modf @@ -21,8 +22,7 @@ local modf = math.modf -- 格式化时间: [年-月-日 时:分:秒,毫秒] local function fmt_Y_m_d_H_M_S() local ts, f = modf(now()) - f = format("%03.0f", f * 1e3) - return concat({'[', os_date('%Y-%m-%d %H:%M:%S'), ',', f, ']'}) + return concat({'[', os_date('%Y-%m-%d %H:%M:%S'), ',', format("%0.3f", f * 1e3), ']'}) end -- 格式化时间: [年-月-日 时:分:秒] @@ -97,51 +97,63 @@ local Log = class("Log") function Log:ctor (opt) if type(opt) == 'table' then - self.to_dump = opt.dump + self.dumped = opt.dump self.path = opt.path - self.now = Y_m_d() + self.today = Y_m_d() end end -- 常规日志 function Log:INFO (...) - print(FMT("\27[32m"..debuginfo(), "[INFO]".."\27[0m", ...)) + io_write(FMT("\27[32m"..debuginfo(), "[INFO]".."\27[0m", ...)) self:dump(FMT(debuginfo(), "[INFO]", ...)) end -- 错误日志 function Log:ERROR (...) - print(FMT("\27[31m"..debuginfo(), "[ERROR]".."\27[0m", ...)) + io_write(FMT("\27[31m"..debuginfo(), "[ERROR]".."\27[0m", ...)) self:dump(FMT(debuginfo(), "[ERROR]", ...)) end -- 调试日志 function Log:DEBUG (...) - print(FMT("\27[36m"..debuginfo(), "[DEBUG]".."\27[0m", ...)) + io_write(FMT("\27[36m"..debuginfo(), "[DEBUG]".."\27[0m", ...)) self:dump(FMT(debuginfo(), "[DEBUG]", ...)) end -- 警告日志 function Log:WARN (...) - print(FMT("\27[33m"..debuginfo(), "[WARN]".."\27[0m", ...)) + io_write(FMT("\27[33m"..debuginfo(), "[WARN]".."\27[0m", ...)) self:dump(FMT(debuginfo(), "[WARN]", ...)) end -- dump日志到磁盘 function Log:dump(log) - if self.to_dump and self.path then - if not self.file then - self.file, err = io_open('logs/'..self.path..'_'..self.now..'.log', 'a') - else - local now = Y_m_d() - if now ~= self.now then - self.file:close() - self.now = now - self.file = io_open('logs/'..self.path..'_'..self.now..'.log', 'a') - end + if not self.dumped or type(self.path) ~= 'string' then + return + end + local today = Y_m_d() + if today ~= self.today then + if self.file then + self.file:close() + self.file = nil + end + local file, err = io_open('logs/'..self.path..'_'..today..'.log', 'a') + if not file then + return io_type(io.output()) == 'file' and io_write('打开文件失败.'..(err or '')..'\n') + end + self.file, self.today = file, today + file:setvbuf("line") + end + if not self.file then + local file, err = io_open('logs/'..self.path..'_'..today..'.log', 'a') + if not file then + return io_type(io.output()) == 'file' and io_write('打开文件失败.'..(err or '')..'\n') end - self.file:write(log):flush() + file:setvbuf("line") + self.file = file end + return self.file:write(log) end return Log From 128cabdead4396d0286c173dd08a10070af16c37 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 4 Jun 2019 00:26:55 +0800 Subject: [PATCH 128/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0test=5Fhttpc=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E8=BE=93=E5=87=BA=E7=9A=84=E9=94=99=E8=AF=AF=E6=89=93?= =?UTF-8?q?=E5=8D=B0=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_httpc.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/test_httpc.lua b/script/test_httpc.lua index fc92b7c6..98e40ce9 100644 --- a/script/test_httpc.lua +++ b/script/test_httpc.lua @@ -110,7 +110,7 @@ cf.timeout(5, function () } } local t2 = now() - print("结束时间:", t1, "总耗时:", t2 - t1) + print("结束时间:", t2, "总耗时:", t2 - t1) require('logging'):new({path = 'test_httpc', dump=true}):DEBUG(response, "回应数量: " .. #response) hc:close() From 01c4dbb520beb5050267c15f235690329e1eaf8c Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 4 Jun 2019 09:26:51 +0800 Subject: [PATCH 129/956] =?UTF-8?q?=E4=BC=98=E5=8C=96ev=5Floop=E5=BE=AA?= =?UTF-8?q?=E7=8E=AF=E6=89=A7=E8=A1=8C=E6=95=88=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_ev.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/core_ev.h b/src/core_ev.h index d3d8bd27..0bc299a4 100644 --- a/src/core_ev.h +++ b/src/core_ev.h @@ -10,6 +10,14 @@ #define EV_VERIFY 0 +#define EV_USE_FLOOR 1 + +#define EV_USE_REALTIME 1 + +#define EV_USE_MONOTONIC 1 + +#define EV_USE_CLOCK_SYSCALL 1 + #define EV_USE_4HEAP 1 #define EV_HEAP_CACHE_AT 1 @@ -20,10 +28,10 @@ /* eventfd 与 signalfd */ #if defined(__linux) || defined(__linux__) + #define EV_USE_EPOLL 1 #define EV_USE_INOTIFY 1 #define EV_USE_SIGNALFD 1 #define EV_USE_EVENTFD 1 - #define EV_USE_EPOLL 1 #endif #if defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) @@ -32,4 +40,4 @@ #include -#endif \ No newline at end of file +#endif From 67762d8cb21c2e18f13c00d560256a5c158d5a86 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 4 Jun 2019 09:39:28 +0800 Subject: [PATCH 130/956] =?UTF-8?q?=E5=87=8F=E5=B0=91httpd=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E5=88=B7=E6=96=B0=E9=A2=91=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua index 759ca7d3..3264c6e0 100644 --- a/lualib/httpd/init.lua +++ b/lualib/httpd/init.lua @@ -165,8 +165,8 @@ function httpd:run() if io.type(output) == 'file' then self.output = true output:setvbuf("full", 1 << 20) - cf.at(0.1, function () - return io_flush() -- 定期刷新缓冲, 日志缓冲平凡导致的性能问题 + cf.at(0.2, function () + return io_flush() -- 定期刷新缓冲, 减少日志缓冲频繁导致的性能问题 end) end return cf.wait() From 0d84bd39961e0c1d127b6f5b57ba71f3683f44e7 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 5 Jun 2019 10:06:46 +0800 Subject: [PATCH 131/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=8E=A7=E5=88=B6=E6=98=AF=E5=90=A6=E6=98=BE=E7=A4=BA=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E8=AF=AD=E8=A8=80=E6=A0=87=E7=AD=BE=E7=9A=84=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/config.lua | 1 + lualib/admin/html/dashboard/header.html | 24 +++++++++++++----------- lualib/admin/http/dashboard.lua | 1 + lualib/admin/init.lua | 5 +++++ 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/lualib/admin/config.lua b/lualib/admin/config.lua index 60ba26a5..34b97317 100644 --- a/lualib/admin/config.lua +++ b/lualib/admin/config.lua @@ -3,6 +3,7 @@ local config = { github = 'https://github.com/candymi/core_framework', -- 跳转地址 cache = false, -- 是否缓存模板 locale = "ZH-CN", -- 当前语言 + display_lang = true, -- 默认显示语言标签 locales = require "admin.locales", -- 语言表 secure = 'cfadmin', -- 生成token的secure cookie_timeout = 86400 -- Cookie超时时间 diff --git a/lualib/admin/html/dashboard/header.html b/lualib/admin/html/dashboard/header.html index a32bf19c..7496ec5e 100644 --- a/lualib/admin/html/dashboard/header.html +++ b/lualib/admin/html/dashboard/header.html @@ -16,17 +16,19 @@ {% end %} {% end %} -
                • - {* locale['dashboard.header.langs'] *} -
                  -
                  - {* locale['dashboard.header.langs.simplified-chinese'] *} -
                  -
                  - {* locale['dashboard.header.langs.us-english'] *} -
                  -
                  -
                • + {% if display_lang then %} +
                • + {* locale['dashboard.header.langs'] *} +
                  +
                  + {* locale['dashboard.header.langs.simplified-chinese'] *} +
                  +
                  + {* locale['dashboard.header.langs.us-english'] *} +
                  +
                  +
                • + {% end %}
                • {* username *}
                  diff --git a/lualib/admin/http/dashboard.lua b/lualib/admin/http/dashboard.lua index 01f3ba83..11be7592 100644 --- a/lualib/admin/http/dashboard.lua +++ b/lualib/admin/http/dashboard.lua @@ -126,6 +126,7 @@ function dashboard.render(content) menu = config.system_menu_render, header = config.system_header_render, role = config.system_role_render, + display_lang = config.display_lang, profile = config.profile_render, locale = get_locale(Cookie.getCookie("CFLANG")) } diff --git a/lualib/admin/init.lua b/lualib/admin/init.lua index 2e8a1872..419c5e12 100644 --- a/lualib/admin/init.lua +++ b/lualib/admin/init.lua @@ -53,6 +53,11 @@ function admin.init_home (url) config.home = url or config.home end +-- 设置是否显示头部语言标签 +function admin.display_lang (display) + config.display_lang = display +end + function admin.init_page (app, db) config.app = assert(app, '初始化必须传入有效的http对象') config.db = assert(db, '初始化必须传入有效的db对象') From 2e0220cad39a082a7985843d3a194cd2ac263db8 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 5 Jun 2019 14:18:15 +0800 Subject: [PATCH 132/956] =?UTF-8?q?=E4=BC=98=E5=8C=96url.encode=E4=B8=8Eur?= =?UTF-8?q?l.decode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lsys.c | 78 +++++++++++++++++++++++++++++++++++++-------- lualib/url/init.lua | 27 +++++++++++----- 2 files changed, 83 insertions(+), 22 deletions(-) diff --git a/luaclib/src/lsys.c b/luaclib/src/lsys.c index 133f7c1c..08469904 100644 --- a/luaclib/src/lsys.c +++ b/luaclib/src/lsys.c @@ -2,6 +2,10 @@ #include "../../src/core.h" +#define hex_char(ch) ({(uint8_t)((ch) > 9 ? (ch) + 55: (ch) + 48);}) + +#define is_normal_char(ch) ({((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || ((ch) >= '0' && (ch) <= '9') ? 1 : 0;}) + // 提供一个精确到毫秒的时间戳 static int lnow(lua_State *L){ @@ -9,7 +13,7 @@ lnow(lua_State *L){ return 1; } -int /* 此方法可用于检查是否为有效ipv4地址*/ +static int /* 此方法可用于检查是否为有效ipv4地址*/ lipv4(lua_State *L){ const char *IP = lua_tostring(L, 1); if (!IP) return luaL_error(L, "ipv4 error: 请至少传递一个string类型参数\n"); @@ -18,7 +22,7 @@ lipv4(lua_State *L){ return 1; } -int /* 此方法可用于检查是否为有效ipv6地址*/ +static int /* 此方法可用于检查是否为有效ipv6地址*/ lipv6(lua_State *L){ const char *IP = lua_tostring(L, 1); if (!IP) return luaL_error(L, "ipv6 error: 请至少传递一个string类型参数\n"); @@ -27,7 +31,7 @@ lipv6(lua_State *L){ return 1; } -int +static int ldate(lua_State *L){ const char *fmt = lua_tostring(L, 1); if (!fmt) return luaL_error(L, "Date: 错误的格式化方法"); @@ -39,16 +43,62 @@ ldate(lua_State *L){ return 1; } +static int /* url编码 */ +lurlencode(lua_State *L){ + size_t url_len; + const char* url = luaL_checklstring(L, 1, &url_len); + if (!url) return luaL_error(L, "urlencode error: 需要传递一个有效的url字符串!"); + luaL_Buffer convert_url; + luaL_buffinit(L, &convert_url); + while (*url) { + uint8_t ch = (uint8_t)*url++; + if (ch == ' ') { + luaL_addlstring(&convert_url, "%20", 3); + continue; + } + if (is_normal_char(ch) || strchr("-_.!~*'()", ch)){ + luaL_addchar(&convert_url, ch); + continue; + } + char ver[3] = {'%', hex_char(((uint8_t)ch) >> 4), hex_char(((uint8_t)ch) & 15)}; + luaL_addlstring(&convert_url, ver, 3); + } + luaL_pushresult(&convert_url); + return 1; +} + +static int /* url解码 */ +lurldecode(lua_State *L){ + size_t url_len, i = 0, j = 0; + const char* url = luaL_checklstring(L, 1, &url_len); + if (!url) return luaL_error(L, "urldecode error: 需要传递一个有效的url字符串!"); + char convert_url[url_len]; + while (i < url_len) { + if (url[i] != '%') { + convert_url[j++] = url[i++]; + continue; + } + char vert[2] = {url[i + 1], url[i + 2]}; + convert_url[j++] = (uint8_t)((vert[0] - 48 - ((vert[0] >= 'A') ? 7 : 0) - ((vert[0] >= 'a') ? 32 : 0)) * 16 + (vert[1] - 48 - ((vert[1] >= 'A') ? 7 : 0) - ((vert[1] >= 'a') ? 32 : 0))); + i += 3; + } + if (j < i) convert_url[j] = '\0'; + lua_pushstring(L, convert_url); + return 1; +} + LUAMOD_API int luaopen_sys(lua_State *L){ - luaL_checkversion(L); - luaL_Reg sys_libs[] = { - {"now", lnow}, - {"ipv4", lipv4}, - {"ipv6", lipv6}, - {"date", ldate}, - {NULL, NULL} - }; - luaL_newlib(L, sys_libs); - return 1; -} \ No newline at end of file + luaL_checkversion(L); + luaL_Reg sys_libs[] = { + {"now", lnow}, + {"ipv4", lipv4}, + {"ipv6", lipv6}, + {"date", ldate}, + {"urlencode", lurlencode}, + {"urldecode", lurldecode}, + {NULL, NULL} + }; + luaL_newlib(L, sys_libs); + return 1; +} diff --git a/lualib/url/init.lua b/lualib/url/init.lua index d5d42c9f..6074eb36 100644 --- a/lualib/url/init.lua +++ b/lualib/url/init.lua @@ -1,19 +1,30 @@ -local tonumber = tonumber -local byte = string.byte -local char = string.char -local fmt = string.format -local spliter = string.gsub +-- lua版实现 +-- local tonumber = tonumber +-- local byte = string.byte +-- local char = string.char +-- local fmt = string.format +-- local spliter = string.gsub + +-- C版实现 +local encode = require("sys").urlencode +local decode = require("sys").urldecode + +--[[ +经过测试: 100万此编码/解码两者性能相差30倍, 正好是lua与C的性能差距. +]] local url = {} -- urlencode编码 function url.encode(s) - return spliter(spliter(s, "([^%w%.%- ])", function(c) return fmt("%%%02X", byte(c)) end), " ", "+") + -- return spliter(spliter(s, "([^%w%.%- ])", function(c) return fmt("%%%02X", byte(c)) end), " ", "+") + return encode(s) end --- urlencode解码 +-- urldecode解码 function url.decode(s) - return spliter(s, '%%(%x%x)', function(h) return char(tonumber(h, 16)) end) + -- return spliter(s, '%%(%x%x)', function(h) return char(tonumber(h, 16)) end) + return decode(s) end return url From a4d0d6f0d7fd6091a953349e653a12e5de21436c Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 5 Jun 2019 15:00:29 +0800 Subject: [PATCH 133/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lsys.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/luaclib/src/lsys.c b/luaclib/src/lsys.c index 08469904..69af146b 100644 --- a/luaclib/src/lsys.c +++ b/luaclib/src/lsys.c @@ -43,7 +43,8 @@ ldate(lua_State *L){ return 1; } -static int /* url编码 */ +/* url编码 */ +static int lurlencode(lua_State *L){ size_t url_len; const char* url = luaL_checklstring(L, 1, &url_len); @@ -67,23 +68,26 @@ lurlencode(lua_State *L){ return 1; } -static int /* url解码 */ +/* url解码 */ +static int lurldecode(lua_State *L){ - size_t url_len, i = 0, j = 0; + size_t url_len; const char* url = luaL_checklstring(L, 1, &url_len); if (!url) return luaL_error(L, "urldecode error: 需要传递一个有效的url字符串!"); - char convert_url[url_len]; - while (i < url_len) { - if (url[i] != '%') { - convert_url[j++] = url[i++]; + luaL_Buffer convert_url; + luaL_buffinit(L, &convert_url); + while (*url) { + uint8_t ch = (uint8_t)*url++; + if (ch != '%') { + luaL_addchar(&convert_url, ch); continue; } - char vert[2] = {url[i + 1], url[i + 2]}; - convert_url[j++] = (uint8_t)((vert[0] - 48 - ((vert[0] >= 'A') ? 7 : 0) - ((vert[0] >= 'a') ? 32 : 0)) * 16 + (vert[1] - 48 - ((vert[1] >= 'A') ? 7 : 0) - ((vert[1] >= 'a') ? 32 : 0))); - i += 3; + char vert[2]; + vert[0] = (uint8_t)*url++; + vert[1] = (uint8_t)*url++; + luaL_addchar(&convert_url, (uint8_t)((vert[0] - 48 - ((vert[0] >= 'A') ? 7 : 0) - ((vert[0] >= 'a') ? 32 : 0)) * 16 + (vert[1] - 48 - ((vert[1] >= 'A') ? 7 : 0) - ((vert[1] >= 'a') ? 32 : 0)))); } - if (j < i) convert_url[j] = '\0'; - lua_pushstring(L, convert_url); + luaL_pushresult(&convert_url); return 1; } From 431f1ac3531f88fa29cc96bf54bf8aef573ef15c Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 6 Jun 2019 22:12:00 +0800 Subject: [PATCH 134/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0new=5Ftab,=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96table=E5=88=9B=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lsys.c | 9 +++++++++ lualib/protocol/mysql.lua | 2 +- lualib/protocol/websocket/protocol.lua | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/luaclib/src/lsys.c b/luaclib/src/lsys.c index 69af146b..f9cae344 100644 --- a/luaclib/src/lsys.c +++ b/luaclib/src/lsys.c @@ -91,6 +91,14 @@ lurldecode(lua_State *L){ return 1; } +static int +lnew_tab(lua_State *L){ + lua_Integer array_size = luaL_checkinteger(L, 1); + lua_Integer hash_size = luaL_checkinteger(L, 2); + lua_createtable(L, array_size, hash_size); + return 1; +} + LUAMOD_API int luaopen_sys(lua_State *L){ luaL_checkversion(L); @@ -99,6 +107,7 @@ luaopen_sys(lua_State *L){ {"ipv4", lipv4}, {"ipv6", lipv6}, {"date", ldate}, + {"new_tab", lnew_tab}, {"urlencode", lurlencode}, {"urldecode", lurldecode}, {NULL, NULL} diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua index 294050db..d43fba58 100644 --- a/lualib/protocol/mysql.lua +++ b/lualib/protocol/mysql.lua @@ -13,7 +13,7 @@ local sha1= crypt.sha1 local setmetatable = setmetatable local error = error local tonumber = tonumber -local new_tab = function (narr, nrec) return {} end +local new_tab = require("sys").new_tab local CHARSET_MAP = { _default = 0, diff --git a/lualib/protocol/websocket/protocol.lua b/lualib/protocol/websocket/protocol.lua index 809f8bf3..cdd142b3 100644 --- a/lualib/protocol/websocket/protocol.lua +++ b/lualib/protocol/websocket/protocol.lua @@ -10,7 +10,7 @@ local rand = math.random local tostring = tostring local type = type -local new_tab = function (narr, nrec) return {} end +local new_tab = require("sys").new_tab local _M = new_tab(0, 5) From 5c02078ecef375cd28e659ae9ee9ce83c57797bc Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 7 Jun 2019 20:39:02 +0800 Subject: [PATCH 135/956] =?UTF-8?q?=E8=B0=83=E6=95=B4crypt=E5=BA=93,=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0sha256=E7=AE=97=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/Makefile | 4 +- luaclib/src/lcrypt.c | 1436 ----------------------------------- luaclib/src/lcrypt/Makefile | 28 + luaclib/src/lcrypt/crc.c | 230 ++++++ luaclib/src/lcrypt/lcrypt.c | 984 ++++++++++++++++++++++++ luaclib/src/lcrypt/sha1.c | 223 ++++++ luaclib/src/lcrypt/sha256.c | 168 ++++ lualib/crypt/init.lua | 33 +- 8 files changed, 1666 insertions(+), 1440 deletions(-) delete mode 100644 luaclib/src/lcrypt.c create mode 100644 luaclib/src/lcrypt/Makefile create mode 100644 luaclib/src/lcrypt/crc.c create mode 100644 luaclib/src/lcrypt/lcrypt.c create mode 100644 luaclib/src/lcrypt/sha1.c create mode 100644 luaclib/src/lcrypt/sha256.c diff --git a/luaclib/Makefile b/luaclib/Makefile index 8ab8f2ba..40d3498b 100644 --- a/luaclib/Makefile +++ b/luaclib/Makefile @@ -20,8 +20,8 @@ build : $(CC) -o udp.so src/ludp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore $(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore $(CC) -o task.so src/ltask.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore - $(CC) -o lcrypt.so src/lcrypt.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore ### 以下为内置第三方库编译位置 ### + cd src/lcrypt && rm -rf *.o *.so && make build cd src/lhttpparser && rm -rf *.o *.so && make build # 增加PicoHTTPParser库 cd src/lcjson && rm -rf *.o *.so && make build # 增加cjson库 cd src/lpeg && rm -rf *.o *.so && make build # 增加lpeg库 @@ -33,8 +33,8 @@ rebuild : $(CC) -o udp.so src/ludp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore $(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore $(CC) -o task.so src/ltask.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore - $(CC) -o lcrypt.so src/lcrypt.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore ### 以下为内置第三方库编译位置 ### + cd src/lcrypt && rm -rf *.o *.so && make rebuild cd src/lhttpparser && rm -rf *.o *.so && make rebuild # 增加PicoHTTPParser库 cd src/lcjson && rm -rf *.o *.so && make rebuild # 增加cjson库 cd src/lpeg && rm -rf *.o *.so && make rebuild # 增加lpeg库 diff --git a/luaclib/src/lcrypt.c b/luaclib/src/lcrypt.c deleted file mode 100644 index 6e2a8bcb..00000000 --- a/luaclib/src/lcrypt.c +++ /dev/null @@ -1,1436 +0,0 @@ -/* --- This file is modified version from https://github.com/cloudwu/skynet --- The license is under the BSD license. --- Modified by Candy (merge sha1 and crypt into an file) -*/ - -#define LUA_LIB - -#include "../../src/core.h" - -#define SMALL_CHUNK 256 - -typedef struct { - uint32_t state[5]; - uint32_t count[2]; - uint8_t buffer[64]; -} SHA1_CTX; - -#define SHA1_DIGEST_SIZE 20 - - - -static void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]); - -#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) - -/* blk0() and blk() perform the initial expand. */ -/* I got the idea of expanding during the round function from SSLeay */ -/* FIXME: can we do this in an endian-proof way? */ -#ifdef WORDS_BIGENDIAN -#define blk0(i) block.l[i] -#else -#define blk0(i) (block.l[i] = (rol(block.l[i],24)&0xFF00FF00) \ - |(rol(block.l[i],8)&0x00FF00FF)) -#endif -#define blk(i) (block.l[i&15] = rol(block.l[(i+13)&15]^block.l[(i+8)&15] \ - ^block.l[(i+2)&15]^block.l[i&15],1)) - -/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ -#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); -#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); -#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); -#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); -#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); - -/* CRC32 TAB */ -static uint32_t CRC32[] = { - 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, - 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, - 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, - 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, - 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, - 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, - 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, - 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, - 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, - 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, - 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, - 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, - 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, - 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, - 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, - 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, - 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, - 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, - 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, - 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, - 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, - 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, - 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, - 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, - 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, - 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, - 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, - 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, - 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, - 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, - 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, - 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, - 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, - 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, - 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, - 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, - 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, - 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, - 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, - 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, - 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, - 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, - 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, - 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, - 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, - 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, - 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, - 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, - 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, - 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, - 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, - 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, - 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, - 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, - 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, - 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, - 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, - 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, - 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, - 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, - 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, - 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, - 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, - 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL -}; - -/* CRC64 TAB */ -static uint64_t CRC64[] = { - 0x0000000000000000, 0x7ad870c830358979, - 0xf5b0e190606b12f2, 0x8f689158505e9b8b, - 0xc038e5739841b68f, 0xbae095bba8743ff6, - 0x358804e3f82aa47d, 0x4f50742bc81f2d04, - 0xab28ecb46814fe75, 0xd1f09c7c5821770c, - 0x5e980d24087fec87, 0x24407dec384a65fe, - 0x6b1009c7f05548fa, 0x11c8790fc060c183, - 0x9ea0e857903e5a08, 0xe478989fa00bd371, - 0x7d08ff3b88be6f81, 0x07d08ff3b88be6f8, - 0x88b81eabe8d57d73, 0xf2606e63d8e0f40a, - 0xbd301a4810ffd90e, 0xc7e86a8020ca5077, - 0x4880fbd87094cbfc, 0x32588b1040a14285, - 0xd620138fe0aa91f4, 0xacf86347d09f188d, - 0x2390f21f80c18306, 0x594882d7b0f40a7f, - 0x1618f6fc78eb277b, 0x6cc0863448deae02, - 0xe3a8176c18803589, 0x997067a428b5bcf0, - 0xfa11fe77117cdf02, 0x80c98ebf2149567b, - 0x0fa11fe77117cdf0, 0x75796f2f41224489, - 0x3a291b04893d698d, 0x40f16bccb908e0f4, - 0xcf99fa94e9567b7f, 0xb5418a5cd963f206, - 0x513912c379682177, 0x2be1620b495da80e, - 0xa489f35319033385, 0xde51839b2936bafc, - 0x9101f7b0e12997f8, 0xebd98778d11c1e81, - 0x64b116208142850a, 0x1e6966e8b1770c73, - 0x8719014c99c2b083, 0xfdc17184a9f739fa, - 0x72a9e0dcf9a9a271, 0x08719014c99c2b08, - 0x4721e43f0183060c, 0x3df994f731b68f75, - 0xb29105af61e814fe, 0xc849756751dd9d87, - 0x2c31edf8f1d64ef6, 0x56e99d30c1e3c78f, - 0xd9810c6891bd5c04, 0xa3597ca0a188d57d, - 0xec09088b6997f879, 0x96d1784359a27100, - 0x19b9e91b09fcea8b, 0x636199d339c963f2, - 0xdf7adabd7a6e2d6f, 0xa5a2aa754a5ba416, - 0x2aca3b2d1a053f9d, 0x50124be52a30b6e4, - 0x1f423fcee22f9be0, 0x659a4f06d21a1299, - 0xeaf2de5e82448912, 0x902aae96b271006b, - 0x74523609127ad31a, 0x0e8a46c1224f5a63, - 0x81e2d7997211c1e8, 0xfb3aa75142244891, - 0xb46ad37a8a3b6595, 0xceb2a3b2ba0eecec, - 0x41da32eaea507767, 0x3b024222da65fe1e, - 0xa2722586f2d042ee, 0xd8aa554ec2e5cb97, - 0x57c2c41692bb501c, 0x2d1ab4dea28ed965, - 0x624ac0f56a91f461, 0x1892b03d5aa47d18, - 0x97fa21650afae693, 0xed2251ad3acf6fea, - 0x095ac9329ac4bc9b, 0x7382b9faaaf135e2, - 0xfcea28a2faafae69, 0x8632586aca9a2710, - 0xc9622c4102850a14, 0xb3ba5c8932b0836d, - 0x3cd2cdd162ee18e6, 0x460abd1952db919f, - 0x256b24ca6b12f26d, 0x5fb354025b277b14, - 0xd0dbc55a0b79e09f, 0xaa03b5923b4c69e6, - 0xe553c1b9f35344e2, 0x9f8bb171c366cd9b, - 0x10e3202993385610, 0x6a3b50e1a30ddf69, - 0x8e43c87e03060c18, 0xf49bb8b633338561, - 0x7bf329ee636d1eea, 0x012b592653589793, - 0x4e7b2d0d9b47ba97, 0x34a35dc5ab7233ee, - 0xbbcbcc9dfb2ca865, 0xc113bc55cb19211c, - 0x5863dbf1e3ac9dec, 0x22bbab39d3991495, - 0xadd33a6183c78f1e, 0xd70b4aa9b3f20667, - 0x985b3e827bed2b63, 0xe2834e4a4bd8a21a, - 0x6debdf121b863991, 0x1733afda2bb3b0e8, - 0xf34b37458bb86399, 0x8993478dbb8deae0, - 0x06fbd6d5ebd3716b, 0x7c23a61ddbe6f812, - 0x3373d23613f9d516, 0x49aba2fe23cc5c6f, - 0xc6c333a67392c7e4, 0xbc1b436e43a74e9d, - 0x95ac9329ac4bc9b5, 0xef74e3e19c7e40cc, - 0x601c72b9cc20db47, 0x1ac40271fc15523e, - 0x5594765a340a7f3a, 0x2f4c0692043ff643, - 0xa02497ca54616dc8, 0xdafce7026454e4b1, - 0x3e847f9dc45f37c0, 0x445c0f55f46abeb9, - 0xcb349e0da4342532, 0xb1eceec59401ac4b, - 0xfebc9aee5c1e814f, 0x8464ea266c2b0836, - 0x0b0c7b7e3c7593bd, 0x71d40bb60c401ac4, - 0xe8a46c1224f5a634, 0x927c1cda14c02f4d, - 0x1d148d82449eb4c6, 0x67ccfd4a74ab3dbf, - 0x289c8961bcb410bb, 0x5244f9a98c8199c2, - 0xdd2c68f1dcdf0249, 0xa7f41839ecea8b30, - 0x438c80a64ce15841, 0x3954f06e7cd4d138, - 0xb63c61362c8a4ab3, 0xcce411fe1cbfc3ca, - 0x83b465d5d4a0eece, 0xf96c151de49567b7, - 0x76048445b4cbfc3c, 0x0cdcf48d84fe7545, - 0x6fbd6d5ebd3716b7, 0x15651d968d029fce, - 0x9a0d8ccedd5c0445, 0xe0d5fc06ed698d3c, - 0xaf85882d2576a038, 0xd55df8e515432941, - 0x5a3569bd451db2ca, 0x20ed197575283bb3, - 0xc49581ead523e8c2, 0xbe4df122e51661bb, - 0x3125607ab548fa30, 0x4bfd10b2857d7349, - 0x04ad64994d625e4d, 0x7e7514517d57d734, - 0xf11d85092d094cbf, 0x8bc5f5c11d3cc5c6, - 0x12b5926535897936, 0x686de2ad05bcf04f, - 0xe70573f555e26bc4, 0x9ddd033d65d7e2bd, - 0xd28d7716adc8cfb9, 0xa85507de9dfd46c0, - 0x273d9686cda3dd4b, 0x5de5e64efd965432, - 0xb99d7ed15d9d8743, 0xc3450e196da80e3a, - 0x4c2d9f413df695b1, 0x36f5ef890dc31cc8, - 0x79a59ba2c5dc31cc, 0x037deb6af5e9b8b5, - 0x8c157a32a5b7233e, 0xf6cd0afa9582aa47, - 0x4ad64994d625e4da, 0x300e395ce6106da3, - 0xbf66a804b64ef628, 0xc5bed8cc867b7f51, - 0x8aeeace74e645255, 0xf036dc2f7e51db2c, - 0x7f5e4d772e0f40a7, 0x05863dbf1e3ac9de, - 0xe1fea520be311aaf, 0x9b26d5e88e0493d6, - 0x144e44b0de5a085d, 0x6e963478ee6f8124, - 0x21c640532670ac20, 0x5b1e309b16452559, - 0xd476a1c3461bbed2, 0xaeaed10b762e37ab, - 0x37deb6af5e9b8b5b, 0x4d06c6676eae0222, - 0xc26e573f3ef099a9, 0xb8b627f70ec510d0, - 0xf7e653dcc6da3dd4, 0x8d3e2314f6efb4ad, - 0x0256b24ca6b12f26, 0x788ec2849684a65f, - 0x9cf65a1b368f752e, 0xe62e2ad306bafc57, - 0x6946bb8b56e467dc, 0x139ecb4366d1eea5, - 0x5ccebf68aecec3a1, 0x2616cfa09efb4ad8, - 0xa97e5ef8cea5d153, 0xd3a62e30fe90582a, - 0xb0c7b7e3c7593bd8, 0xca1fc72bf76cb2a1, - 0x45775673a732292a, 0x3faf26bb9707a053, - 0x70ff52905f188d57, 0x0a2722586f2d042e, - 0x854fb3003f739fa5, 0xff97c3c80f4616dc, - 0x1bef5b57af4dc5ad, 0x61372b9f9f784cd4, - 0xee5fbac7cf26d75f, 0x9487ca0fff135e26, - 0xdbd7be24370c7322, 0xa10fceec0739fa5b, - 0x2e675fb4576761d0, 0x54bf2f7c6752e8a9, - 0xcdcf48d84fe75459, 0xb71738107fd2dd20, - 0x387fa9482f8c46ab, 0x42a7d9801fb9cfd2, - 0x0df7adabd7a6e2d6, 0x772fdd63e7936baf, - 0xf8474c3bb7cdf024, 0x829f3cf387f8795d, - 0x66e7a46c27f3aa2c, 0x1c3fd4a417c62355, - 0x935745fc4798b8de, 0xe98f353477ad31a7, - 0xa6df411fbfb21ca3, 0xdc0731d78f8795da, - 0x536fa08fdfd90e51, 0x29b7d047efec8728, -}; - - -/* the eight DES S-boxes */ - -static uint32_t SB1[64] = { - 0x01010400, 0x00000000, 0x00010000, 0x01010404, - 0x01010004, 0x00010404, 0x00000004, 0x00010000, - 0x00000400, 0x01010400, 0x01010404, 0x00000400, - 0x01000404, 0x01010004, 0x01000000, 0x00000004, - 0x00000404, 0x01000400, 0x01000400, 0x00010400, - 0x00010400, 0x01010000, 0x01010000, 0x01000404, - 0x00010004, 0x01000004, 0x01000004, 0x00010004, - 0x00000000, 0x00000404, 0x00010404, 0x01000000, - 0x00010000, 0x01010404, 0x00000004, 0x01010000, - 0x01010400, 0x01000000, 0x01000000, 0x00000400, - 0x01010004, 0x00010000, 0x00010400, 0x01000004, - 0x00000400, 0x00000004, 0x01000404, 0x00010404, - 0x01010404, 0x00010004, 0x01010000, 0x01000404, - 0x01000004, 0x00000404, 0x00010404, 0x01010400, - 0x00000404, 0x01000400, 0x01000400, 0x00000000, - 0x00010004, 0x00010400, 0x00000000, 0x01010004 -}; - -static uint32_t SB2[64] = { - 0x80108020, 0x80008000, 0x00008000, 0x00108020, - 0x00100000, 0x00000020, 0x80100020, 0x80008020, - 0x80000020, 0x80108020, 0x80108000, 0x80000000, - 0x80008000, 0x00100000, 0x00000020, 0x80100020, - 0x00108000, 0x00100020, 0x80008020, 0x00000000, - 0x80000000, 0x00008000, 0x00108020, 0x80100000, - 0x00100020, 0x80000020, 0x00000000, 0x00108000, - 0x00008020, 0x80108000, 0x80100000, 0x00008020, - 0x00000000, 0x00108020, 0x80100020, 0x00100000, - 0x80008020, 0x80100000, 0x80108000, 0x00008000, - 0x80100000, 0x80008000, 0x00000020, 0x80108020, - 0x00108020, 0x00000020, 0x00008000, 0x80000000, - 0x00008020, 0x80108000, 0x00100000, 0x80000020, - 0x00100020, 0x80008020, 0x80000020, 0x00100020, - 0x00108000, 0x00000000, 0x80008000, 0x00008020, - 0x80000000, 0x80100020, 0x80108020, 0x00108000 -}; - -static uint32_t SB3[64] = { - 0x00000208, 0x08020200, 0x00000000, 0x08020008, - 0x08000200, 0x00000000, 0x00020208, 0x08000200, - 0x00020008, 0x08000008, 0x08000008, 0x00020000, - 0x08020208, 0x00020008, 0x08020000, 0x00000208, - 0x08000000, 0x00000008, 0x08020200, 0x00000200, - 0x00020200, 0x08020000, 0x08020008, 0x00020208, - 0x08000208, 0x00020200, 0x00020000, 0x08000208, - 0x00000008, 0x08020208, 0x00000200, 0x08000000, - 0x08020200, 0x08000000, 0x00020008, 0x00000208, - 0x00020000, 0x08020200, 0x08000200, 0x00000000, - 0x00000200, 0x00020008, 0x08020208, 0x08000200, - 0x08000008, 0x00000200, 0x00000000, 0x08020008, - 0x08000208, 0x00020000, 0x08000000, 0x08020208, - 0x00000008, 0x00020208, 0x00020200, 0x08000008, - 0x08020000, 0x08000208, 0x00000208, 0x08020000, - 0x00020208, 0x00000008, 0x08020008, 0x00020200 -}; - -static uint32_t SB4[64] = { - 0x00802001, 0x00002081, 0x00002081, 0x00000080, - 0x00802080, 0x00800081, 0x00800001, 0x00002001, - 0x00000000, 0x00802000, 0x00802000, 0x00802081, - 0x00000081, 0x00000000, 0x00800080, 0x00800001, - 0x00000001, 0x00002000, 0x00800000, 0x00802001, - 0x00000080, 0x00800000, 0x00002001, 0x00002080, - 0x00800081, 0x00000001, 0x00002080, 0x00800080, - 0x00002000, 0x00802080, 0x00802081, 0x00000081, - 0x00800080, 0x00800001, 0x00802000, 0x00802081, - 0x00000081, 0x00000000, 0x00000000, 0x00802000, - 0x00002080, 0x00800080, 0x00800081, 0x00000001, - 0x00802001, 0x00002081, 0x00002081, 0x00000080, - 0x00802081, 0x00000081, 0x00000001, 0x00002000, - 0x00800001, 0x00002001, 0x00802080, 0x00800081, - 0x00002001, 0x00002080, 0x00800000, 0x00802001, - 0x00000080, 0x00800000, 0x00002000, 0x00802080 -}; - -static uint32_t SB5[64] = { - 0x00000100, 0x02080100, 0x02080000, 0x42000100, - 0x00080000, 0x00000100, 0x40000000, 0x02080000, - 0x40080100, 0x00080000, 0x02000100, 0x40080100, - 0x42000100, 0x42080000, 0x00080100, 0x40000000, - 0x02000000, 0x40080000, 0x40080000, 0x00000000, - 0x40000100, 0x42080100, 0x42080100, 0x02000100, - 0x42080000, 0x40000100, 0x00000000, 0x42000000, - 0x02080100, 0x02000000, 0x42000000, 0x00080100, - 0x00080000, 0x42000100, 0x00000100, 0x02000000, - 0x40000000, 0x02080000, 0x42000100, 0x40080100, - 0x02000100, 0x40000000, 0x42080000, 0x02080100, - 0x40080100, 0x00000100, 0x02000000, 0x42080000, - 0x42080100, 0x00080100, 0x42000000, 0x42080100, - 0x02080000, 0x00000000, 0x40080000, 0x42000000, - 0x00080100, 0x02000100, 0x40000100, 0x00080000, - 0x00000000, 0x40080000, 0x02080100, 0x40000100 -}; - -static uint32_t SB6[64] = { - 0x20000010, 0x20400000, 0x00004000, 0x20404010, - 0x20400000, 0x00000010, 0x20404010, 0x00400000, - 0x20004000, 0x00404010, 0x00400000, 0x20000010, - 0x00400010, 0x20004000, 0x20000000, 0x00004010, - 0x00000000, 0x00400010, 0x20004010, 0x00004000, - 0x00404000, 0x20004010, 0x00000010, 0x20400010, - 0x20400010, 0x00000000, 0x00404010, 0x20404000, - 0x00004010, 0x00404000, 0x20404000, 0x20000000, - 0x20004000, 0x00000010, 0x20400010, 0x00404000, - 0x20404010, 0x00400000, 0x00004010, 0x20000010, - 0x00400000, 0x20004000, 0x20000000, 0x00004010, - 0x20000010, 0x20404010, 0x00404000, 0x20400000, - 0x00404010, 0x20404000, 0x00000000, 0x20400010, - 0x00000010, 0x00004000, 0x20400000, 0x00404010, - 0x00004000, 0x00400010, 0x20004010, 0x00000000, - 0x20404000, 0x20000000, 0x00400010, 0x20004010 -}; - -static uint32_t SB7[64] = { - 0x00200000, 0x04200002, 0x04000802, 0x00000000, - 0x00000800, 0x04000802, 0x00200802, 0x04200800, - 0x04200802, 0x00200000, 0x00000000, 0x04000002, - 0x00000002, 0x04000000, 0x04200002, 0x00000802, - 0x04000800, 0x00200802, 0x00200002, 0x04000800, - 0x04000002, 0x04200000, 0x04200800, 0x00200002, - 0x04200000, 0x00000800, 0x00000802, 0x04200802, - 0x00200800, 0x00000002, 0x04000000, 0x00200800, - 0x04000000, 0x00200800, 0x00200000, 0x04000802, - 0x04000802, 0x04200002, 0x04200002, 0x00000002, - 0x00200002, 0x04000000, 0x04000800, 0x00200000, - 0x04200800, 0x00000802, 0x00200802, 0x04200800, - 0x00000802, 0x04000002, 0x04200802, 0x04200000, - 0x00200800, 0x00000000, 0x00000002, 0x04200802, - 0x00000000, 0x00200802, 0x04200000, 0x00000800, - 0x04000002, 0x04000800, 0x00000800, 0x00200002 -}; - -static uint32_t SB8[64] = { - 0x10001040, 0x00001000, 0x00040000, 0x10041040, - 0x10000000, 0x10001040, 0x00000040, 0x10000000, - 0x00040040, 0x10040000, 0x10041040, 0x00041000, - 0x10041000, 0x00041040, 0x00001000, 0x00000040, - 0x10040000, 0x10000040, 0x10001000, 0x00001040, - 0x00041000, 0x00040040, 0x10040040, 0x10041000, - 0x00001040, 0x00000000, 0x00000000, 0x10040040, - 0x10000040, 0x10001000, 0x00041040, 0x00040000, - 0x00041040, 0x00040000, 0x10041000, 0x00001000, - 0x00000040, 0x10040040, 0x00001000, 0x00041040, - 0x10001000, 0x00000040, 0x10000040, 0x10040000, - 0x10040040, 0x10000000, 0x00040000, 0x10001040, - 0x00000000, 0x10041040, 0x00040040, 0x10000040, - 0x10040000, 0x10001000, 0x10001040, 0x00000000, - 0x10041040, 0x00041000, 0x00041000, 0x00001040, - 0x00001040, 0x00040040, 0x10000000, 0x10041000 -}; - -/* PC1: left and right halves bit-swap */ - -static uint32_t LHs[16] = { - 0x00000000, 0x00000001, 0x00000100, 0x00000101, - 0x00010000, 0x00010001, 0x00010100, 0x00010101, - 0x01000000, 0x01000001, 0x01000100, 0x01000101, - 0x01010000, 0x01010001, 0x01010100, 0x01010101 -}; - -static uint32_t RHs[16] = { - 0x00000000, 0x01000000, 0x00010000, 0x01010000, - 0x00000100, 0x01000100, 0x00010100, 0x01010100, - 0x00000001, 0x01000001, 0x00010001, 0x01010001, - 0x00000101, 0x01000101, 0x00010101, 0x01010101, -}; - -/* platform-independant 32-bit integer manipulation macros */ - -#define GET_UINT32(n,b,i) \ -{ \ - (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ - | ( (uint32_t) (b)[(i) + 1] << 16 ) \ - | ( (uint32_t) (b)[(i) + 2] << 8 ) \ - | ( (uint32_t) (b)[(i) + 3] ); \ -} - -#define PUT_UINT32(n,b,i) \ -{ \ - (b)[(i) ] = (uint8_t) ( (n) >> 24 ); \ - (b)[(i) + 1] = (uint8_t) ( (n) >> 16 ); \ - (b)[(i) + 2] = (uint8_t) ( (n) >> 8 ); \ - (b)[(i) + 3] = (uint8_t) ( (n) ); \ -} - -/* Initial Permutation macro */ - -#define DES_IP(X,Y) \ -{ \ - T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ - T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ - T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ - T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ - Y = ((Y << 1) | (Y >> 31)) & 0xFFFFFFFF; \ - T = (X ^ Y) & 0xAAAAAAAA; Y ^= T; X ^= T; \ - X = ((X << 1) | (X >> 31)) & 0xFFFFFFFF; \ -} - -/* Final Permutation macro */ - -#define DES_FP(X,Y) \ -{ \ - X = ((X << 31) | (X >> 1)) & 0xFFFFFFFF; \ - T = (X ^ Y) & 0xAAAAAAAA; X ^= T; Y ^= T; \ - Y = ((Y << 31) | (Y >> 1)) & 0xFFFFFFFF; \ - T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ - T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ - T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ - T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ -} - -/* DES round macro */ - -#define DES_ROUND(X,Y) \ -{ \ - T = *SK++ ^ X; \ - Y ^= SB8[ (T ) & 0x3F ] ^ \ - SB6[ (T >> 8) & 0x3F ] ^ \ - SB4[ (T >> 16) & 0x3F ] ^ \ - SB2[ (T >> 24) & 0x3F ]; \ - \ - T = *SK++ ^ ((X << 28) | (X >> 4)); \ - Y ^= SB7[ (T ) & 0x3F ] ^ \ - SB5[ (T >> 8) & 0x3F ] ^ \ - SB3[ (T >> 16) & 0x3F ] ^ \ - SB1[ (T >> 24) & 0x3F ]; \ -} - -/* DES key schedule */ - - - - -/* Hash a single 512-bit block. This is the core of the algorithm. */ -static void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]) -{ - uint32_t a, b, c, d, e; - typedef union { - uint8_t c[64]; - uint32_t l[16]; - } CHAR64LONG16; - CHAR64LONG16 block; - - memcpy(&block, buffer, 64); - - /* Copy context->state[] to working vars */ - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - - /* 4 rounds of 20 operations each. Loop unrolled. */ - R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); - R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); - R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); - R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); - R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); - R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); - R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); - R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); - R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); - R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); - R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); - R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); - R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); - R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); - R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); - R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); - R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); - R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); - R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); - R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); - - /* Add the working vars back into context.state[] */ - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - - /* Wipe variables */ - a = b = c = d = e = 0; -} - - -/* SHA1Init - Initialize new context */ -static void sat_SHA1_Init(SHA1_CTX* context) -{ - /* SHA1 initialization constants */ - context->state[0] = 0x67452301; - context->state[1] = 0xEFCDAB89; - context->state[2] = 0x98BADCFE; - context->state[3] = 0x10325476; - context->state[4] = 0xC3D2E1F0; - context->count[0] = context->count[1] = 0; -} - - -/* Run your data through this. */ -static void sat_SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len) -{ - size_t i, j; - -#ifdef VERBOSE - SHAPrintContext(context, "before"); -#endif - - j = (context->count[0] >> 3) & 63; - if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; - context->count[1] += (len >> 29); - if ((j + len) > 63) { - memcpy(&context->buffer[j], data, (i = 64-j)); - SHA1_Transform(context->state, context->buffer); - for ( ; i + 63 < len; i += 64) { - SHA1_Transform(context->state, data + i); - } - j = 0; - } - else i = 0; - memcpy(&context->buffer[j], &data[i], len - i); - -#ifdef VERBOSE - SHAPrintContext(context, "after "); -#endif -} - - -/* Add padding and return the message digest. */ -static void sat_SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE]) -{ - uint32_t i; - uint8_t finalcount[8]; - - for (i = 0; i < 8; i++) { - finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] - >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ - } - sat_SHA1_Update(context, (uint8_t *)"\200", 1); - while ((context->count[0] & 504) != 448) { - sat_SHA1_Update(context, (uint8_t *)"\0", 1); - } - sat_SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */ - for (i = 0; i < SHA1_DIGEST_SIZE; i++) { - digest[i] = (uint8_t) - ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); - } - - /* Wipe variables */ - i = 0; - memset(context->buffer, 0, 64); - memset(context->state, 0, 20); - memset(context->count, 0, 8); - memset(finalcount, 0, 8); /* SWR */ -} - -int -lsha1(lua_State *L) { - size_t sz = 0; - const uint8_t * buffer = (const uint8_t *)luaL_checklstring(L, 1, &sz); - uint8_t digest[SHA1_DIGEST_SIZE]; - SHA1_CTX ctx; - sat_SHA1_Init(&ctx); - sat_SHA1_Update(&ctx, buffer, sz); - sat_SHA1_Final(&ctx, digest); - lua_pushlstring(L, (const char *)digest, SHA1_DIGEST_SIZE); - - return 1; -} - -#define BLOCKSIZE 64 - -static inline void -xor_key(uint8_t key[BLOCKSIZE], uint32_t xor) { - int i; - for (i=0;i BLOCKSIZE) { - SHA1_CTX ctx; - sat_SHA1_Init(&ctx); - sat_SHA1_Update(&ctx, key, key_sz); - sat_SHA1_Final(&ctx, rkey); - key_sz = SHA1_DIGEST_SIZE; - } else { - memcpy(rkey, key, key_sz); - } - - xor_key(rkey, 0x5c5c5c5c); - sat_SHA1_Init(&ctx1); - sat_SHA1_Update(&ctx1, rkey, BLOCKSIZE); - - xor_key(rkey, 0x5c5c5c5c ^ 0x36363636); - sat_SHA1_Init(&ctx2); - sat_SHA1_Update(&ctx2, rkey, BLOCKSIZE); - sat_SHA1_Update(&ctx2, text, text_sz); - sat_SHA1_Final(&ctx2, digest2); - - sat_SHA1_Update(&ctx1, digest2, SHA1_DIGEST_SIZE); - sat_SHA1_Final(&ctx1, digest1); - - lua_pushlstring(L, (const char *)digest1, SHA1_DIGEST_SIZE); - - return 1; -} - -static void -des_main_ks( uint32_t SK[32], const uint8_t key[8] ) { - int i; - uint32_t X, Y, T; - - GET_UINT32( X, key, 0 ); - GET_UINT32( Y, key, 4 ); - - /* Permuted Choice 1 */ - - T = ((Y >> 4) ^ X) & 0x0F0F0F0F; X ^= T; Y ^= (T << 4); - T = ((Y ) ^ X) & 0x10101010; X ^= T; Y ^= (T ); - - X = (LHs[ (X ) & 0xF] << 3) | (LHs[ (X >> 8) & 0xF ] << 2) - | (LHs[ (X >> 16) & 0xF] << 1) | (LHs[ (X >> 24) & 0xF ] ) - | (LHs[ (X >> 5) & 0xF] << 7) | (LHs[ (X >> 13) & 0xF ] << 6) - | (LHs[ (X >> 21) & 0xF] << 5) | (LHs[ (X >> 29) & 0xF ] << 4); - - Y = (RHs[ (Y >> 1) & 0xF] << 3) | (RHs[ (Y >> 9) & 0xF ] << 2) - | (RHs[ (Y >> 17) & 0xF] << 1) | (RHs[ (Y >> 25) & 0xF ] ) - | (RHs[ (Y >> 4) & 0xF] << 7) | (RHs[ (Y >> 12) & 0xF ] << 6) - | (RHs[ (Y >> 20) & 0xF] << 5) | (RHs[ (Y >> 28) & 0xF ] << 4); - - X &= 0x0FFFFFFF; - Y &= 0x0FFFFFFF; - - /* calculate subkeys */ - - for( i = 0; i < 16; i++ ) - { - if( i < 2 || i == 8 || i == 15 ) - { - X = ((X << 1) | (X >> 27)) & 0x0FFFFFFF; - Y = ((Y << 1) | (Y >> 27)) & 0x0FFFFFFF; - } - else - { - X = ((X << 2) | (X >> 26)) & 0x0FFFFFFF; - Y = ((Y << 2) | (Y >> 26)) & 0x0FFFFFFF; - } - - *SK++ = ((X << 4) & 0x24000000) | ((X << 28) & 0x10000000) - | ((X << 14) & 0x08000000) | ((X << 18) & 0x02080000) - | ((X << 6) & 0x01000000) | ((X << 9) & 0x00200000) - | ((X >> 1) & 0x00100000) | ((X << 10) & 0x00040000) - | ((X << 2) & 0x00020000) | ((X >> 10) & 0x00010000) - | ((Y >> 13) & 0x00002000) | ((Y >> 4) & 0x00001000) - | ((Y << 6) & 0x00000800) | ((Y >> 1) & 0x00000400) - | ((Y >> 14) & 0x00000200) | ((Y ) & 0x00000100) - | ((Y >> 5) & 0x00000020) | ((Y >> 10) & 0x00000010) - | ((Y >> 3) & 0x00000008) | ((Y >> 18) & 0x00000004) - | ((Y >> 26) & 0x00000002) | ((Y >> 24) & 0x00000001); - - *SK++ = ((X << 15) & 0x20000000) | ((X << 17) & 0x10000000) - | ((X << 10) & 0x08000000) | ((X << 22) & 0x04000000) - | ((X >> 2) & 0x02000000) | ((X << 1) & 0x01000000) - | ((X << 16) & 0x00200000) | ((X << 11) & 0x00100000) - | ((X << 3) & 0x00080000) | ((X >> 6) & 0x00040000) - | ((X << 15) & 0x00020000) | ((X >> 4) & 0x00010000) - | ((Y >> 2) & 0x00002000) | ((Y << 8) & 0x00001000) - | ((Y >> 14) & 0x00000808) | ((Y >> 9) & 0x00000400) - | ((Y ) & 0x00000200) | ((Y << 7) & 0x00000100) - | ((Y >> 7) & 0x00000020) | ((Y >> 3) & 0x00000011) - | ((Y << 2) & 0x00000004) | ((Y >> 21) & 0x00000002); - } -} - -/* DES 64-bit block encryption/decryption */ - -static void -des_crypt( const uint32_t SK[32], const uint8_t input[8], uint8_t output[8] ) { - uint32_t X, Y, T; - - GET_UINT32( X, input, 0 ); - GET_UINT32( Y, input, 4 ); - - DES_IP( X, Y ); - - DES_ROUND( Y, X ); DES_ROUND( X, Y ); - DES_ROUND( Y, X ); DES_ROUND( X, Y ); - DES_ROUND( Y, X ); DES_ROUND( X, Y ); - DES_ROUND( Y, X ); DES_ROUND( X, Y ); - DES_ROUND( Y, X ); DES_ROUND( X, Y ); - DES_ROUND( Y, X ); DES_ROUND( X, Y ); - DES_ROUND( Y, X ); DES_ROUND( X, Y ); - DES_ROUND( Y, X ); DES_ROUND( X, Y ); - - DES_FP( Y, X ); - - PUT_UINT32( Y, output, 0 ); - PUT_UINT32( X, output, 4 ); -} - -static int -lrandomkey(lua_State *L) { - char tmp[8]; - int i; - char x = 0; - for (i=0;i<8;i++) { - tmp[i] = random() & 0xff; - x ^= tmp[i]; - } - if (x==0) { - tmp[0] |= 1; // avoid 0 - } - lua_pushlstring(L, tmp, 8); - return 1; -} - -static void -des_key(lua_State *L, uint32_t SK[32]) { - size_t keysz = 0; - const void * key = luaL_checklstring(L, 1, &keysz); - if (keysz != 8) { - luaL_error(L, "Invalid key size %d, need 8 bytes", (int)keysz); - } - des_main_ks(SK, key); -} - -static int -ldesencode(lua_State *L) { - uint32_t SK[32]; - des_key(L, SK); - - size_t textsz = 0; - const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 2, &textsz); - size_t chunksz = (textsz + 8) & ~7; - uint8_t tmp[SMALL_CHUNK]; - uint8_t *buffer = tmp; - if (chunksz > SMALL_CHUNK) { - buffer = lua_newuserdata(L, chunksz); - } - int i; - for (i=0;i<(int)textsz-7;i+=8) { - des_crypt(SK, text+i, buffer+i); - } - int bytes = textsz - i; - uint8_t tail[8]; - int j; - for (j=0;j<8;j++) { - if (j < bytes) { - tail[j] = text[i+j]; - } else if (j==bytes) { - tail[j] = 0x80; - } else { - tail[j] = 0; - } - } - des_crypt(SK, tail, buffer+i); - lua_pushlstring(L, (const char *)buffer, chunksz); - - return 1; -} - -static int -ldesdecode(lua_State *L) { - uint32_t ESK[32]; - des_key(L, ESK); - uint32_t SK[32]; - int i; - for( i = 0; i < 32; i += 2 ) { - SK[i] = ESK[30 - i]; - SK[i + 1] = ESK[31 - i]; - } - size_t textsz = 0; - const uint8_t *text = (const uint8_t *)luaL_checklstring(L, 2, &textsz); - if ((textsz & 7) || textsz == 0) { - return luaL_error(L, "Invalid des crypt text length %d", (int)textsz); - } - uint8_t tmp[SMALL_CHUNK]; - uint8_t *buffer = tmp; - if (textsz > SMALL_CHUNK) { - buffer = lua_newuserdata(L, textsz); - } - for (i=0;i=textsz-8;i--) { - if (buffer[i] == 0) { - padding++; - } else if (buffer[i] == 0x80) { - break; - } else { - return luaL_error(L, "Invalid des crypt text"); - } - } - if (padding > 8) { - return luaL_error(L, "Invalid des crypt text"); - } - lua_pushlstring(L, (const char *)buffer, textsz - padding); - return 1; -} - - -static void -Hash(const char * str, int sz, uint8_t key[8]) { - uint32_t djb_hash = 5381L; - uint32_t js_hash = 1315423911L; - - int i; - for (i=0;i> 2)); - } - - key[0] = djb_hash & 0xff; - key[1] = (djb_hash >> 8) & 0xff; - key[2] = (djb_hash >> 16) & 0xff; - key[3] = (djb_hash >> 24) & 0xff; - - key[4] = js_hash & 0xff; - key[5] = (js_hash >> 8) & 0xff; - key[6] = (js_hash >> 16) & 0xff; - key[7] = (js_hash >> 24) & 0xff; -} - -static int -lhashkey(lua_State *L) { - size_t sz = 0; - const char * key = luaL_checklstring(L, 1, &sz); - uint8_t realkey[8]; - Hash(key,(int)sz,realkey); - lua_pushlstring(L, (const char *)realkey, 8); - return 1; -} - -static int -ltohex(lua_State *L) { - static char hex[] = "0123456789abcdef"; - size_t sz = 0; - const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 1, &sz); - char tmp[SMALL_CHUNK]; - char *buffer = tmp; - if (sz > SMALL_CHUNK/2) { - buffer = lua_newuserdata(L, sz * 2); - } - int i; - for (i=0;i> 4]; - buffer[i*2+1] = hex[text[i] & 0xf]; - } - lua_pushlstring(L, buffer, sz * 2); - return 1; -} - -#define HEX(v,c) { char tmp = (char) c; if (tmp >= '0' && tmp <= '9') { v = tmp-'0'; } else { v = tmp - 'a' + 10; } } - -static int -lfromhex(lua_State *L) { - size_t sz = 0; - const char * text = luaL_checklstring(L, 1, &sz); - if (sz & 1) { - return luaL_error(L, "Invalid hex text size %d", (int)sz); - } - char tmp[SMALL_CHUNK]; - char *buffer = tmp; - if (sz > SMALL_CHUNK*2) { - buffer = lua_newuserdata(L, sz / 2); - } - int i; - for (i=0;i 16 || low > 16) { - return luaL_error(L, "Invalid hex text", text); - } - buffer[i/2] = hi<<4 | low; - } - lua_pushlstring(L, buffer, i/2); - return 1; -} - -// Constants are the integer part of the sines of integers (in radians) * 2^32. -static const uint32_t k[64] = { -0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee , -0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501 , -0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be , -0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821 , -0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa , -0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8 , -0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed , -0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a , -0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c , -0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70 , -0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05 , -0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665 , -0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039 , -0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1 , -0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1 , -0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 }; - -// r specifies the per-round shift amounts -static const uint32_t r[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, - 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, - 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, - 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21}; - -// leftrotate function definition -#define LEFTROTATE(x, c) (((x) << (c)) | ((x) >> (32 - (c)))) - -static void -digest_md5(uint32_t w[16], uint32_t result[4]) { - uint32_t a, b, c, d, f, g, temp; - int i; - - a = 0x67452301u; - b = 0xefcdab89u; - c = 0x98badcfeu; - d = 0x10325476u; - - for(i = 0; i<64; i++) { - if (i < 16) { - f = (b & c) | ((~b) & d); - g = i; - } else if (i < 32) { - f = (d & b) | ((~d) & c); - g = (5*i + 1) % 16; - } else if (i < 48) { - f = b ^ c ^ d; - g = (3*i + 5) % 16; - } else { - f = c ^ (b | (~d)); - g = (7*i) % 16; - } - - temp = d; - d = c; - c = b; - b = b + LEFTROTATE((a + f + k[i] + w[g]), r[i]); - a = temp; - } - - result[0] = a; - result[1] = b; - result[2] = c; - result[3] = d; -} - -// hmac64 use md5 algorithm without padding, and the result is (c^d .. a^b) -static void -hmac(uint32_t x[2], uint32_t y[2], uint32_t result[2]) { - uint32_t w[16]; - uint32_t r[4]; - int i; - for (i=0;i<16;i+=4) { - w[i] = x[1]; - w[i+1] = x[0]; - w[i+2] = y[1]; - w[i+3] = y[0]; - } - - digest_md5(w,r); - - result[0] = r[2]^r[3]; - result[1] = r[0]^r[1]; -} - -static void -hmac_md5(uint32_t x[2], uint32_t y[2], uint32_t result[2]) { - uint32_t w[16]; - uint32_t r[4]; - int i; - for (i=0;i<12;i+=4) { - w[i] = x[0]; - w[i+1] = x[1]; - w[i+2] = y[0]; - w[i+3] = y[1]; - } - - w[12] = 0x80; - w[13] = 0; - w[14] = 384; - w[15] = 0; - - digest_md5(w,r); - - result[0] = (r[0] + 0x67452301u) ^ (r[2] + 0x98badcfeu); - result[1] = (r[1] + 0xefcdab89u) ^ (r[3] + 0x10325476u); -} - -static void -read64(lua_State *L, uint32_t xx[2], uint32_t yy[2]) { - size_t sz = 0; - const uint8_t *x = (const uint8_t *)luaL_checklstring(L, 1, &sz); - if (sz != 8) { - luaL_error(L, "Invalid uint64 x"); - } - const uint8_t *y = (const uint8_t *)luaL_checklstring(L, 2, &sz); - if (sz != 8) { - luaL_error(L, "Invalid uint64 y"); - } - xx[0] = x[0] | x[1]<<8 | x[2]<<16 | x[3]<<24; - xx[1] = x[4] | x[5]<<8 | x[6]<<16 | x[7]<<24; - yy[0] = y[0] | y[1]<<8 | y[2]<<16 | y[3]<<24; - yy[1] = y[4] | y[5]<<8 | y[6]<<16 | y[7]<<24; -} - -static int -pushqword(lua_State *L, uint32_t result[2]) { - uint8_t tmp[8]; - tmp[0] = result[0] & 0xff; - tmp[1] = (result[0] >> 8 )& 0xff; - tmp[2] = (result[0] >> 16 )& 0xff; - tmp[3] = (result[0] >> 24 )& 0xff; - tmp[4] = result[1] & 0xff; - tmp[5] = (result[1] >> 8 )& 0xff; - tmp[6] = (result[1] >> 16 )& 0xff; - tmp[7] = (result[1] >> 24 )& 0xff; - - lua_pushlstring(L, (const char *)tmp, 8); - return 1; -} - -static int -lhmac64(lua_State *L) { - uint32_t x[2], y[2]; - read64(L, x, y); - uint32_t result[2]; - hmac(x,y,result); - return pushqword(L, result); -} - -/* - h1 = crypt.hmac64_md5(a,b) - m = md5.sum((a..b):rep(3)) - h2 = crypt.xor_str(m:sub(1,8), m:sub(9,16)) - assert(h1 == h2) - */ -static int -lhmac64_md5(lua_State *L) { - uint32_t x[2], y[2]; - read64(L, x, y); - uint32_t result[2]; - hmac_md5(x,y,result); - return pushqword(L, result); -} - -/* - 8bytes key - string text - */ -static int -lhmac_hash(lua_State *L) { - uint32_t key[2]; - size_t sz = 0; - const uint8_t *x = (const uint8_t *)luaL_checklstring(L, 1, &sz); - if (sz != 8) { - luaL_error(L, "Invalid uint64 key"); - } - key[0] = x[0] | x[1]<<8 | x[2]<<16 | x[3]<<24; - key[1] = x[4] | x[5]<<8 | x[6]<<16 | x[7]<<24; - const char * text = luaL_checklstring(L, 2, &sz); - uint8_t h[8]; - Hash(text,(int)sz,h); - uint32_t htext[2]; - htext[0] = h[0] | h[1]<<8 | h[2]<<16 | h[3]<<24; - htext[1] = h[4] | h[5]<<8 | h[6]<<16 | h[7]<<24; - uint32_t result[2]; - hmac(htext,key,result); - return pushqword(L, result); -} - -// powmodp64 for DH-key exchange - -// The biggest 64bit prime -#define P 0xffffffffffffffc5ull - -static inline uint64_t -mul_mod_p(uint64_t a, uint64_t b) { - uint64_t m = 0; - while(b) { - if(b&1) { - uint64_t t = P-a; - if ( m >= t) { - m -= t; - } else { - m += a; - } - } - if (a >= P - a) { - a = a * 2 - P; - } else { - a = a * 2; - } - b>>=1; - } - return m; -} - -static inline uint64_t -pow_mod_p(uint64_t a, uint64_t b) { - if (b==1) { - return a; - } - uint64_t t = pow_mod_p(a, b>>1); - t = mul_mod_p(t,t); - if (b % 2) { - t = mul_mod_p(t, a); - } - return t; -} - -// calc a^b % p -static uint64_t -powmodp(uint64_t a, uint64_t b) { - if (a > P) - a%=P; - return pow_mod_p(a,b); -} - -static void -push64(lua_State *L, uint64_t r) { - uint8_t tmp[8]; - tmp[0] = r & 0xff; - tmp[1] = (r >> 8 )& 0xff; - tmp[2] = (r >> 16 )& 0xff; - tmp[3] = (r >> 24 )& 0xff; - tmp[4] = (r >> 32 )& 0xff; - tmp[5] = (r >> 40 )& 0xff; - tmp[6] = (r >> 48 )& 0xff; - tmp[7] = (r >> 56 )& 0xff; - - lua_pushlstring(L, (const char *)tmp, 8); -} - -static int -ldhsecret(lua_State *L) { - uint32_t x[2], y[2]; - read64(L, x, y); - uint64_t xx = (uint64_t)x[0] | (uint64_t)x[1]<<32; - uint64_t yy = (uint64_t)y[0] | (uint64_t)y[1]<<32; - if (xx == 0 || yy == 0) - return luaL_error(L, "Can't be 0"); - uint64_t r = powmodp(xx, yy); - - push64(L, r); - - return 1; -} - -#define G 5 - -static int -ldhexchange(lua_State *L) { - size_t sz = 0; - const uint8_t *x = (const uint8_t *)luaL_checklstring(L, 1, &sz); - if (sz != 8) { - luaL_error(L, "Invalid dh uint64 key"); - } - uint32_t xx[2]; - xx[0] = x[0] | x[1]<<8 | x[2]<<16 | x[3]<<24; - xx[1] = x[4] | x[5]<<8 | x[6]<<16 | x[7]<<24; - - uint64_t x64 = (uint64_t)xx[0] | (uint64_t)xx[1]<<32; - if (x64 == 0) - return luaL_error(L, "Can't be 0"); - - uint64_t r = powmodp(G, x64); - push64(L, r); - return 1; -} - -// base64 - -static int -lb64encode(lua_State *L) { - static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - size_t sz = 0; - const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 1, &sz); - int encode_sz = (sz + 2)/3*4; - char tmp[SMALL_CHUNK]; - char *buffer = tmp; - if (encode_sz > SMALL_CHUNK) { - buffer = lua_newuserdata(L, encode_sz); - } - int i,j; - j=0; - for (i=0;i<(int)sz-2;i+=3) { - uint32_t v = text[i] << 16 | text[i+1] << 8 | text[i+2]; - buffer[j] = encoding[v >> 18]; - buffer[j+1] = encoding[(v >> 12) & 0x3f]; - buffer[j+2] = encoding[(v >> 6) & 0x3f]; - buffer[j+3] = encoding[(v) & 0x3f]; - j+=4; - } - int padding = sz-i; - uint32_t v; - switch(padding) { - case 1 : - v = text[i]; - buffer[j] = encoding[v >> 2]; - buffer[j+1] = encoding[(v & 3) << 4]; - buffer[j+2] = '='; - buffer[j+3] = '='; - break; - case 2 : - v = text[i] << 8 | text[i+1]; - buffer[j] = encoding[v >> 10]; - buffer[j+1] = encoding[(v >> 4) & 0x3f]; - buffer[j+2] = encoding[(v & 0xf) << 2]; - buffer[j+3] = '='; - break; - } - lua_pushlstring(L, buffer, encode_sz); - return 1; -} - -static inline int -b64index(uint8_t c) { - static const int decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51}; - int decoding_size = sizeof(decoding)/sizeof(decoding[0]); - if (c<43) { - return -1; - } - c -= 43; - if (c>=decoding_size) - return -1; - return decoding[c]; -} - -static int -lb64decode(lua_State *L) { - size_t sz = 0; - const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 1, &sz); - int decode_sz = (sz+3)/4*3; - char tmp[SMALL_CHUNK]; - char *buffer = tmp; - if (decode_sz > SMALL_CHUNK) { - buffer = lua_newuserdata(L, decode_sz); - } - int i,j; - int output = 0; - for (i=0;i=sz) { - return luaL_error(L, "Invalid base64 text"); - } - c[j] = b64index(text[i]); - if (c[j] == -1) { - ++i; - continue; - } - if (c[j] == -2) { - ++padding; - } - ++i; - ++j; - } - uint32_t v; - switch (padding) { - case 0: - v = (unsigned)c[0] << 18 | c[1] << 12 | c[2] << 6 | c[3]; - buffer[output] = v >> 16; - buffer[output+1] = (v >> 8) & 0xff; - buffer[output+2] = v & 0xff; - output += 3; - break; - case 1: - if (c[3] != -2 || (c[2] & 3)!=0) { - return luaL_error(L, "Invalid base64 text"); - } - v = (unsigned)c[0] << 10 | c[1] << 4 | c[2] >> 2 ; - buffer[output] = v >> 8; - buffer[output+1] = v & 0xff; - output += 2; - break; - case 2: - if (c[3] != -2 || c[2] != -2 || (c[1] & 0xf) !=0) { - return luaL_error(L, "Invalid base64 text"); - } - v = (unsigned)c[0] << 2 | c[1] >> 4; - buffer[output] = v; - ++ output; - break; - default: - return luaL_error(L, "Invalid base64 text"); - } - } - lua_pushlstring(L, buffer, output); - return 1; -} - -static int -lxor_str(lua_State *L) { - size_t len1,len2; - const char *s1 = luaL_checklstring(L,1,&len1); - const char *s2 = luaL_checklstring(L,2,&len2); - if (len2 == 0) { - return luaL_error(L, "Can't xor empty string"); - } - luaL_Buffer b; - char * buffer = luaL_buffinitsize(L, &b, len1); - int i; - for (i=0;i> 8); - lua_pushinteger(L, crc ^ 0xFFFFFFFF); - return 1; -}; - -static int -lcrc64(lua_State *L){ - size_t len; - const char *str = luaL_checklstring(L, 1, &len); - if (len <= 0) return luaL_error(L, "#1 need a string"); - - uint32_t i = 0; - uint64_t crc = 0x0; - for (i = 0; i < len; i++) crc = CRC64[(uint8_t)crc ^ (uint8_t)str[i]] ^ (crc >> 8); - lua_pushnumber(L, crc); - return 1; -}; - -LUAMOD_API int -luaopen_lcrypt(lua_State *L) { - luaL_checkversion(L); - static int init = 0; - if (!init) { - // Don't need call srandom more than once. - init = 1 ; - srandom((random() << 8) ^ (time(NULL) << 16) ^ getpid()); - } - luaL_Reg crypt_libs[] = { - { "hashkey", lhashkey }, - { "randomkey", lrandomkey }, - { "desencode", ldesencode }, - { "desdecode", ldesdecode }, - { "hexencode", ltohex }, - { "hexdecode", lfromhex }, - { "hmac64", lhmac64 }, - { "hmac64_md5", lhmac64_md5 }, - { "dhexchange", ldhexchange }, - { "dhsecret", ldhsecret }, - { "base64encode", lb64encode }, - { "base64decode", lb64decode }, - { "sha1", lsha1 }, - { "hmac_sha1", lhmac_sha1 }, - { "hmac_hash", lhmac_hash }, - { "xor_str", lxor_str }, - { "crc32", lcrc32 }, - { "crc64", lcrc64 }, - { NULL, NULL }, - }; - luaL_newlib(L, crypt_libs); - return 1; -} diff --git a/luaclib/src/lcrypt/Makefile b/luaclib/src/lcrypt/Makefile new file mode 100644 index 00000000..fd6b041d --- /dev/null +++ b/luaclib/src/lcrypt/Makefile @@ -0,0 +1,28 @@ +.PHONY : build rebuild clean + +default : + @echo "=======================================" + @echo "Please use 'make build' command to build it.." + @echo "Please use 'make rebuild' command to build it.." + @echo "Please use 'make clean' command to clean all." + @echo "=======================================" + +CC = cc + +CFLAGS = -O3 -w -shared -fPIC -DNDEBUG + +DLL = -lcore + +INCLUDE = -I/usr/local/include +LIB = -L/usr/local/lib + +build: + $(CC) -o lcrypt.so lcrypt.c crc.c sha1.c sha256.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) + mv lcrypt.so ../../ + +rebuild: + $(CC) -o lcrypt.so lcrypt.c crc.c sha1.c sha256.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) + mv lcrypt.so ../../ + +clean: + rm -rf *.o *.so diff --git a/luaclib/src/lcrypt/crc.c b/luaclib/src/lcrypt/crc.c new file mode 100644 index 00000000..14a065c9 --- /dev/null +++ b/luaclib/src/lcrypt/crc.c @@ -0,0 +1,230 @@ +#define LUA_LIB + +#include "../../../src/core.h" + +/* CRC32 TAB */ +static uint32_t CRC32[] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, + 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, + 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, + 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, + 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, + 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, + 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, + 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, + 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, + 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, + 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, + 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, + 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, + 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, + 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, + 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, + 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, + 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, + 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, + 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, + 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, + 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, + 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, + 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, + 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, + 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, + 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, + 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, + 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, + 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, + 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, + 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, + 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, + 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, + 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, + 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, + 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, + 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, + 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, + 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, + 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, + 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, + 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, + 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, + 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, + 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, + 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, + 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, + 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, + 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, + 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, + 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL +}; + +/* CRC64 TAB */ +static uint64_t CRC64[] = { + 0x0000000000000000, 0x7ad870c830358979, + 0xf5b0e190606b12f2, 0x8f689158505e9b8b, + 0xc038e5739841b68f, 0xbae095bba8743ff6, + 0x358804e3f82aa47d, 0x4f50742bc81f2d04, + 0xab28ecb46814fe75, 0xd1f09c7c5821770c, + 0x5e980d24087fec87, 0x24407dec384a65fe, + 0x6b1009c7f05548fa, 0x11c8790fc060c183, + 0x9ea0e857903e5a08, 0xe478989fa00bd371, + 0x7d08ff3b88be6f81, 0x07d08ff3b88be6f8, + 0x88b81eabe8d57d73, 0xf2606e63d8e0f40a, + 0xbd301a4810ffd90e, 0xc7e86a8020ca5077, + 0x4880fbd87094cbfc, 0x32588b1040a14285, + 0xd620138fe0aa91f4, 0xacf86347d09f188d, + 0x2390f21f80c18306, 0x594882d7b0f40a7f, + 0x1618f6fc78eb277b, 0x6cc0863448deae02, + 0xe3a8176c18803589, 0x997067a428b5bcf0, + 0xfa11fe77117cdf02, 0x80c98ebf2149567b, + 0x0fa11fe77117cdf0, 0x75796f2f41224489, + 0x3a291b04893d698d, 0x40f16bccb908e0f4, + 0xcf99fa94e9567b7f, 0xb5418a5cd963f206, + 0x513912c379682177, 0x2be1620b495da80e, + 0xa489f35319033385, 0xde51839b2936bafc, + 0x9101f7b0e12997f8, 0xebd98778d11c1e81, + 0x64b116208142850a, 0x1e6966e8b1770c73, + 0x8719014c99c2b083, 0xfdc17184a9f739fa, + 0x72a9e0dcf9a9a271, 0x08719014c99c2b08, + 0x4721e43f0183060c, 0x3df994f731b68f75, + 0xb29105af61e814fe, 0xc849756751dd9d87, + 0x2c31edf8f1d64ef6, 0x56e99d30c1e3c78f, + 0xd9810c6891bd5c04, 0xa3597ca0a188d57d, + 0xec09088b6997f879, 0x96d1784359a27100, + 0x19b9e91b09fcea8b, 0x636199d339c963f2, + 0xdf7adabd7a6e2d6f, 0xa5a2aa754a5ba416, + 0x2aca3b2d1a053f9d, 0x50124be52a30b6e4, + 0x1f423fcee22f9be0, 0x659a4f06d21a1299, + 0xeaf2de5e82448912, 0x902aae96b271006b, + 0x74523609127ad31a, 0x0e8a46c1224f5a63, + 0x81e2d7997211c1e8, 0xfb3aa75142244891, + 0xb46ad37a8a3b6595, 0xceb2a3b2ba0eecec, + 0x41da32eaea507767, 0x3b024222da65fe1e, + 0xa2722586f2d042ee, 0xd8aa554ec2e5cb97, + 0x57c2c41692bb501c, 0x2d1ab4dea28ed965, + 0x624ac0f56a91f461, 0x1892b03d5aa47d18, + 0x97fa21650afae693, 0xed2251ad3acf6fea, + 0x095ac9329ac4bc9b, 0x7382b9faaaf135e2, + 0xfcea28a2faafae69, 0x8632586aca9a2710, + 0xc9622c4102850a14, 0xb3ba5c8932b0836d, + 0x3cd2cdd162ee18e6, 0x460abd1952db919f, + 0x256b24ca6b12f26d, 0x5fb354025b277b14, + 0xd0dbc55a0b79e09f, 0xaa03b5923b4c69e6, + 0xe553c1b9f35344e2, 0x9f8bb171c366cd9b, + 0x10e3202993385610, 0x6a3b50e1a30ddf69, + 0x8e43c87e03060c18, 0xf49bb8b633338561, + 0x7bf329ee636d1eea, 0x012b592653589793, + 0x4e7b2d0d9b47ba97, 0x34a35dc5ab7233ee, + 0xbbcbcc9dfb2ca865, 0xc113bc55cb19211c, + 0x5863dbf1e3ac9dec, 0x22bbab39d3991495, + 0xadd33a6183c78f1e, 0xd70b4aa9b3f20667, + 0x985b3e827bed2b63, 0xe2834e4a4bd8a21a, + 0x6debdf121b863991, 0x1733afda2bb3b0e8, + 0xf34b37458bb86399, 0x8993478dbb8deae0, + 0x06fbd6d5ebd3716b, 0x7c23a61ddbe6f812, + 0x3373d23613f9d516, 0x49aba2fe23cc5c6f, + 0xc6c333a67392c7e4, 0xbc1b436e43a74e9d, + 0x95ac9329ac4bc9b5, 0xef74e3e19c7e40cc, + 0x601c72b9cc20db47, 0x1ac40271fc15523e, + 0x5594765a340a7f3a, 0x2f4c0692043ff643, + 0xa02497ca54616dc8, 0xdafce7026454e4b1, + 0x3e847f9dc45f37c0, 0x445c0f55f46abeb9, + 0xcb349e0da4342532, 0xb1eceec59401ac4b, + 0xfebc9aee5c1e814f, 0x8464ea266c2b0836, + 0x0b0c7b7e3c7593bd, 0x71d40bb60c401ac4, + 0xe8a46c1224f5a634, 0x927c1cda14c02f4d, + 0x1d148d82449eb4c6, 0x67ccfd4a74ab3dbf, + 0x289c8961bcb410bb, 0x5244f9a98c8199c2, + 0xdd2c68f1dcdf0249, 0xa7f41839ecea8b30, + 0x438c80a64ce15841, 0x3954f06e7cd4d138, + 0xb63c61362c8a4ab3, 0xcce411fe1cbfc3ca, + 0x83b465d5d4a0eece, 0xf96c151de49567b7, + 0x76048445b4cbfc3c, 0x0cdcf48d84fe7545, + 0x6fbd6d5ebd3716b7, 0x15651d968d029fce, + 0x9a0d8ccedd5c0445, 0xe0d5fc06ed698d3c, + 0xaf85882d2576a038, 0xd55df8e515432941, + 0x5a3569bd451db2ca, 0x20ed197575283bb3, + 0xc49581ead523e8c2, 0xbe4df122e51661bb, + 0x3125607ab548fa30, 0x4bfd10b2857d7349, + 0x04ad64994d625e4d, 0x7e7514517d57d734, + 0xf11d85092d094cbf, 0x8bc5f5c11d3cc5c6, + 0x12b5926535897936, 0x686de2ad05bcf04f, + 0xe70573f555e26bc4, 0x9ddd033d65d7e2bd, + 0xd28d7716adc8cfb9, 0xa85507de9dfd46c0, + 0x273d9686cda3dd4b, 0x5de5e64efd965432, + 0xb99d7ed15d9d8743, 0xc3450e196da80e3a, + 0x4c2d9f413df695b1, 0x36f5ef890dc31cc8, + 0x79a59ba2c5dc31cc, 0x037deb6af5e9b8b5, + 0x8c157a32a5b7233e, 0xf6cd0afa9582aa47, + 0x4ad64994d625e4da, 0x300e395ce6106da3, + 0xbf66a804b64ef628, 0xc5bed8cc867b7f51, + 0x8aeeace74e645255, 0xf036dc2f7e51db2c, + 0x7f5e4d772e0f40a7, 0x05863dbf1e3ac9de, + 0xe1fea520be311aaf, 0x9b26d5e88e0493d6, + 0x144e44b0de5a085d, 0x6e963478ee6f8124, + 0x21c640532670ac20, 0x5b1e309b16452559, + 0xd476a1c3461bbed2, 0xaeaed10b762e37ab, + 0x37deb6af5e9b8b5b, 0x4d06c6676eae0222, + 0xc26e573f3ef099a9, 0xb8b627f70ec510d0, + 0xf7e653dcc6da3dd4, 0x8d3e2314f6efb4ad, + 0x0256b24ca6b12f26, 0x788ec2849684a65f, + 0x9cf65a1b368f752e, 0xe62e2ad306bafc57, + 0x6946bb8b56e467dc, 0x139ecb4366d1eea5, + 0x5ccebf68aecec3a1, 0x2616cfa09efb4ad8, + 0xa97e5ef8cea5d153, 0xd3a62e30fe90582a, + 0xb0c7b7e3c7593bd8, 0xca1fc72bf76cb2a1, + 0x45775673a732292a, 0x3faf26bb9707a053, + 0x70ff52905f188d57, 0x0a2722586f2d042e, + 0x854fb3003f739fa5, 0xff97c3c80f4616dc, + 0x1bef5b57af4dc5ad, 0x61372b9f9f784cd4, + 0xee5fbac7cf26d75f, 0x9487ca0fff135e26, + 0xdbd7be24370c7322, 0xa10fceec0739fa5b, + 0x2e675fb4576761d0, 0x54bf2f7c6752e8a9, + 0xcdcf48d84fe75459, 0xb71738107fd2dd20, + 0x387fa9482f8c46ab, 0x42a7d9801fb9cfd2, + 0x0df7adabd7a6e2d6, 0x772fdd63e7936baf, + 0xf8474c3bb7cdf024, 0x829f3cf387f8795d, + 0x66e7a46c27f3aa2c, 0x1c3fd4a417c62355, + 0x935745fc4798b8de, 0xe98f353477ad31a7, + 0xa6df411fbfb21ca3, 0xdc0731d78f8795da, + 0x536fa08fdfd90e51, 0x29b7d047efec8728, +}; + +LUAMOD_API int +lcrc32(lua_State *L){ + size_t len; + const char *str = luaL_checklstring(L, 1, &len); + if (len <= 0) return luaL_error(L, "crc32 err: #1 need a string"); + + uint32_t i = 0; + uint32_t crc = 0xFFFFFFFF; + + for (i = 0; i < len; i++) crc = CRC32[ (crc ^ str[i]) & 0xff ] ^ (crc >> 8); + lua_pushinteger(L, crc ^ 0xFFFFFFFF); + return 1; +}; + +LUAMOD_API int +lcrc64(lua_State *L){ + size_t len; + const char *str = luaL_checklstring(L, 1, &len); + if (len <= 0) return luaL_error(L, "crc64 err: #1 need a string"); + + uint32_t i = 0; + uint64_t crc = 0x0; + for (i = 0; i < len; i++) crc = CRC64[(uint8_t)crc ^ (uint8_t)str[i]] ^ (crc >> 8); + lua_pushnumber(L, crc); + return 1; +}; diff --git a/luaclib/src/lcrypt/lcrypt.c b/luaclib/src/lcrypt/lcrypt.c new file mode 100644 index 00000000..532f65ec --- /dev/null +++ b/luaclib/src/lcrypt/lcrypt.c @@ -0,0 +1,984 @@ +#define LUA_LIB + +#include "../../../src/core.h" + +#define SMALL_CHUNK 256 + +/* the eight DES S-boxes */ + +static uint32_t SB1[64] = { + 0x01010400, 0x00000000, 0x00010000, 0x01010404, + 0x01010004, 0x00010404, 0x00000004, 0x00010000, + 0x00000400, 0x01010400, 0x01010404, 0x00000400, + 0x01000404, 0x01010004, 0x01000000, 0x00000004, + 0x00000404, 0x01000400, 0x01000400, 0x00010400, + 0x00010400, 0x01010000, 0x01010000, 0x01000404, + 0x00010004, 0x01000004, 0x01000004, 0x00010004, + 0x00000000, 0x00000404, 0x00010404, 0x01000000, + 0x00010000, 0x01010404, 0x00000004, 0x01010000, + 0x01010400, 0x01000000, 0x01000000, 0x00000400, + 0x01010004, 0x00010000, 0x00010400, 0x01000004, + 0x00000400, 0x00000004, 0x01000404, 0x00010404, + 0x01010404, 0x00010004, 0x01010000, 0x01000404, + 0x01000004, 0x00000404, 0x00010404, 0x01010400, + 0x00000404, 0x01000400, 0x01000400, 0x00000000, + 0x00010004, 0x00010400, 0x00000000, 0x01010004 +}; + +static uint32_t SB2[64] = { + 0x80108020, 0x80008000, 0x00008000, 0x00108020, + 0x00100000, 0x00000020, 0x80100020, 0x80008020, + 0x80000020, 0x80108020, 0x80108000, 0x80000000, + 0x80008000, 0x00100000, 0x00000020, 0x80100020, + 0x00108000, 0x00100020, 0x80008020, 0x00000000, + 0x80000000, 0x00008000, 0x00108020, 0x80100000, + 0x00100020, 0x80000020, 0x00000000, 0x00108000, + 0x00008020, 0x80108000, 0x80100000, 0x00008020, + 0x00000000, 0x00108020, 0x80100020, 0x00100000, + 0x80008020, 0x80100000, 0x80108000, 0x00008000, + 0x80100000, 0x80008000, 0x00000020, 0x80108020, + 0x00108020, 0x00000020, 0x00008000, 0x80000000, + 0x00008020, 0x80108000, 0x00100000, 0x80000020, + 0x00100020, 0x80008020, 0x80000020, 0x00100020, + 0x00108000, 0x00000000, 0x80008000, 0x00008020, + 0x80000000, 0x80100020, 0x80108020, 0x00108000 +}; + +static uint32_t SB3[64] = { + 0x00000208, 0x08020200, 0x00000000, 0x08020008, + 0x08000200, 0x00000000, 0x00020208, 0x08000200, + 0x00020008, 0x08000008, 0x08000008, 0x00020000, + 0x08020208, 0x00020008, 0x08020000, 0x00000208, + 0x08000000, 0x00000008, 0x08020200, 0x00000200, + 0x00020200, 0x08020000, 0x08020008, 0x00020208, + 0x08000208, 0x00020200, 0x00020000, 0x08000208, + 0x00000008, 0x08020208, 0x00000200, 0x08000000, + 0x08020200, 0x08000000, 0x00020008, 0x00000208, + 0x00020000, 0x08020200, 0x08000200, 0x00000000, + 0x00000200, 0x00020008, 0x08020208, 0x08000200, + 0x08000008, 0x00000200, 0x00000000, 0x08020008, + 0x08000208, 0x00020000, 0x08000000, 0x08020208, + 0x00000008, 0x00020208, 0x00020200, 0x08000008, + 0x08020000, 0x08000208, 0x00000208, 0x08020000, + 0x00020208, 0x00000008, 0x08020008, 0x00020200 +}; + +static uint32_t SB4[64] = { + 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802080, 0x00800081, 0x00800001, 0x00002001, + 0x00000000, 0x00802000, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00800080, 0x00800001, + 0x00000001, 0x00002000, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002001, 0x00002080, + 0x00800081, 0x00000001, 0x00002080, 0x00800080, + 0x00002000, 0x00802080, 0x00802081, 0x00000081, + 0x00800080, 0x00800001, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00000000, 0x00802000, + 0x00002080, 0x00800080, 0x00800081, 0x00000001, + 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802081, 0x00000081, 0x00000001, 0x00002000, + 0x00800001, 0x00002001, 0x00802080, 0x00800081, + 0x00002001, 0x00002080, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002000, 0x00802080 +}; + +static uint32_t SB5[64] = { + 0x00000100, 0x02080100, 0x02080000, 0x42000100, + 0x00080000, 0x00000100, 0x40000000, 0x02080000, + 0x40080100, 0x00080000, 0x02000100, 0x40080100, + 0x42000100, 0x42080000, 0x00080100, 0x40000000, + 0x02000000, 0x40080000, 0x40080000, 0x00000000, + 0x40000100, 0x42080100, 0x42080100, 0x02000100, + 0x42080000, 0x40000100, 0x00000000, 0x42000000, + 0x02080100, 0x02000000, 0x42000000, 0x00080100, + 0x00080000, 0x42000100, 0x00000100, 0x02000000, + 0x40000000, 0x02080000, 0x42000100, 0x40080100, + 0x02000100, 0x40000000, 0x42080000, 0x02080100, + 0x40080100, 0x00000100, 0x02000000, 0x42080000, + 0x42080100, 0x00080100, 0x42000000, 0x42080100, + 0x02080000, 0x00000000, 0x40080000, 0x42000000, + 0x00080100, 0x02000100, 0x40000100, 0x00080000, + 0x00000000, 0x40080000, 0x02080100, 0x40000100 +}; + +static uint32_t SB6[64] = { + 0x20000010, 0x20400000, 0x00004000, 0x20404010, + 0x20400000, 0x00000010, 0x20404010, 0x00400000, + 0x20004000, 0x00404010, 0x00400000, 0x20000010, + 0x00400010, 0x20004000, 0x20000000, 0x00004010, + 0x00000000, 0x00400010, 0x20004010, 0x00004000, + 0x00404000, 0x20004010, 0x00000010, 0x20400010, + 0x20400010, 0x00000000, 0x00404010, 0x20404000, + 0x00004010, 0x00404000, 0x20404000, 0x20000000, + 0x20004000, 0x00000010, 0x20400010, 0x00404000, + 0x20404010, 0x00400000, 0x00004010, 0x20000010, + 0x00400000, 0x20004000, 0x20000000, 0x00004010, + 0x20000010, 0x20404010, 0x00404000, 0x20400000, + 0x00404010, 0x20404000, 0x00000000, 0x20400010, + 0x00000010, 0x00004000, 0x20400000, 0x00404010, + 0x00004000, 0x00400010, 0x20004010, 0x00000000, + 0x20404000, 0x20000000, 0x00400010, 0x20004010 +}; + +static uint32_t SB7[64] = { + 0x00200000, 0x04200002, 0x04000802, 0x00000000, + 0x00000800, 0x04000802, 0x00200802, 0x04200800, + 0x04200802, 0x00200000, 0x00000000, 0x04000002, + 0x00000002, 0x04000000, 0x04200002, 0x00000802, + 0x04000800, 0x00200802, 0x00200002, 0x04000800, + 0x04000002, 0x04200000, 0x04200800, 0x00200002, + 0x04200000, 0x00000800, 0x00000802, 0x04200802, + 0x00200800, 0x00000002, 0x04000000, 0x00200800, + 0x04000000, 0x00200800, 0x00200000, 0x04000802, + 0x04000802, 0x04200002, 0x04200002, 0x00000002, + 0x00200002, 0x04000000, 0x04000800, 0x00200000, + 0x04200800, 0x00000802, 0x00200802, 0x04200800, + 0x00000802, 0x04000002, 0x04200802, 0x04200000, + 0x00200800, 0x00000000, 0x00000002, 0x04200802, + 0x00000000, 0x00200802, 0x04200000, 0x00000800, + 0x04000002, 0x04000800, 0x00000800, 0x00200002 +}; + +static uint32_t SB8[64] = { + 0x10001040, 0x00001000, 0x00040000, 0x10041040, + 0x10000000, 0x10001040, 0x00000040, 0x10000000, + 0x00040040, 0x10040000, 0x10041040, 0x00041000, + 0x10041000, 0x00041040, 0x00001000, 0x00000040, + 0x10040000, 0x10000040, 0x10001000, 0x00001040, + 0x00041000, 0x00040040, 0x10040040, 0x10041000, + 0x00001040, 0x00000000, 0x00000000, 0x10040040, + 0x10000040, 0x10001000, 0x00041040, 0x00040000, + 0x00041040, 0x00040000, 0x10041000, 0x00001000, + 0x00000040, 0x10040040, 0x00001000, 0x00041040, + 0x10001000, 0x00000040, 0x10000040, 0x10040000, + 0x10040040, 0x10000000, 0x00040000, 0x10001040, + 0x00000000, 0x10041040, 0x00040040, 0x10000040, + 0x10040000, 0x10001000, 0x10001040, 0x00000000, + 0x10041040, 0x00041000, 0x00041000, 0x00001040, + 0x00001040, 0x00040040, 0x10000000, 0x10041000 +}; + +/* PC1: left and right halves bit-swap */ + +static uint32_t LHs[16] = { + 0x00000000, 0x00000001, 0x00000100, 0x00000101, + 0x00010000, 0x00010001, 0x00010100, 0x00010101, + 0x01000000, 0x01000001, 0x01000100, 0x01000101, + 0x01010000, 0x01010001, 0x01010100, 0x01010101 +}; + +static uint32_t RHs[16] = { + 0x00000000, 0x01000000, 0x00010000, 0x01010000, + 0x00000100, 0x01000100, 0x00010100, 0x01010100, + 0x00000001, 0x01000001, 0x00010001, 0x01010001, + 0x00000101, 0x01000101, 0x00010101, 0x01010101, +}; + +/* platform-independant 32-bit integer manipulation macros */ + +#define GET_UINT32(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ + | ( (uint32_t) (b)[(i) + 1] << 16 ) \ + | ( (uint32_t) (b)[(i) + 2] << 8 ) \ + | ( (uint32_t) (b)[(i) + 3] ); \ +} + +#define PUT_UINT32(n,b,i) \ +{ \ + (b)[(i) ] = (uint8_t) ( (n) >> 24 ); \ + (b)[(i) + 1] = (uint8_t) ( (n) >> 16 ); \ + (b)[(i) + 2] = (uint8_t) ( (n) >> 8 ); \ + (b)[(i) + 3] = (uint8_t) ( (n) ); \ +} + +/* Initial Permutation macro */ + +#define DES_IP(X,Y) \ +{ \ + T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ + T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ + T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ + T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ + Y = ((Y << 1) | (Y >> 31)) & 0xFFFFFFFF; \ + T = (X ^ Y) & 0xAAAAAAAA; Y ^= T; X ^= T; \ + X = ((X << 1) | (X >> 31)) & 0xFFFFFFFF; \ +} + +/* Final Permutation macro */ + +#define DES_FP(X,Y) \ +{ \ + X = ((X << 31) | (X >> 1)) & 0xFFFFFFFF; \ + T = (X ^ Y) & 0xAAAAAAAA; X ^= T; Y ^= T; \ + Y = ((Y << 31) | (Y >> 1)) & 0xFFFFFFFF; \ + T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ + T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ + T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ + T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ +} + +/* DES round macro */ + +#define DES_ROUND(X,Y) \ +{ \ + T = *SK++ ^ X; \ + Y ^= SB8[ (T ) & 0x3F ] ^ \ + SB6[ (T >> 8) & 0x3F ] ^ \ + SB4[ (T >> 16) & 0x3F ] ^ \ + SB2[ (T >> 24) & 0x3F ]; \ + \ + T = *SK++ ^ ((X << 28) | (X >> 4)); \ + Y ^= SB7[ (T ) & 0x3F ] ^ \ + SB5[ (T >> 8) & 0x3F ] ^ \ + SB3[ (T >> 16) & 0x3F ] ^ \ + SB1[ (T >> 24) & 0x3F ]; \ +} + +/* DES key schedule */ + +static void +des_main_ks( uint32_t SK[32], const uint8_t key[8] ) { + int i; + uint32_t X, Y, T; + + GET_UINT32( X, key, 0 ); + GET_UINT32( Y, key, 4 ); + + /* Permuted Choice 1 */ + + T = ((Y >> 4) ^ X) & 0x0F0F0F0F; X ^= T; Y ^= (T << 4); + T = ((Y ) ^ X) & 0x10101010; X ^= T; Y ^= (T ); + + X = (LHs[ (X ) & 0xF] << 3) | (LHs[ (X >> 8) & 0xF ] << 2) + | (LHs[ (X >> 16) & 0xF] << 1) | (LHs[ (X >> 24) & 0xF ] ) + | (LHs[ (X >> 5) & 0xF] << 7) | (LHs[ (X >> 13) & 0xF ] << 6) + | (LHs[ (X >> 21) & 0xF] << 5) | (LHs[ (X >> 29) & 0xF ] << 4); + + Y = (RHs[ (Y >> 1) & 0xF] << 3) | (RHs[ (Y >> 9) & 0xF ] << 2) + | (RHs[ (Y >> 17) & 0xF] << 1) | (RHs[ (Y >> 25) & 0xF ] ) + | (RHs[ (Y >> 4) & 0xF] << 7) | (RHs[ (Y >> 12) & 0xF ] << 6) + | (RHs[ (Y >> 20) & 0xF] << 5) | (RHs[ (Y >> 28) & 0xF ] << 4); + + X &= 0x0FFFFFFF; + Y &= 0x0FFFFFFF; + + /* calculate subkeys */ + + for( i = 0; i < 16; i++ ) + { + if( i < 2 || i == 8 || i == 15 ) + { + X = ((X << 1) | (X >> 27)) & 0x0FFFFFFF; + Y = ((Y << 1) | (Y >> 27)) & 0x0FFFFFFF; + } + else + { + X = ((X << 2) | (X >> 26)) & 0x0FFFFFFF; + Y = ((Y << 2) | (Y >> 26)) & 0x0FFFFFFF; + } + + *SK++ = ((X << 4) & 0x24000000) | ((X << 28) & 0x10000000) + | ((X << 14) & 0x08000000) | ((X << 18) & 0x02080000) + | ((X << 6) & 0x01000000) | ((X << 9) & 0x00200000) + | ((X >> 1) & 0x00100000) | ((X << 10) & 0x00040000) + | ((X << 2) & 0x00020000) | ((X >> 10) & 0x00010000) + | ((Y >> 13) & 0x00002000) | ((Y >> 4) & 0x00001000) + | ((Y << 6) & 0x00000800) | ((Y >> 1) & 0x00000400) + | ((Y >> 14) & 0x00000200) | ((Y ) & 0x00000100) + | ((Y >> 5) & 0x00000020) | ((Y >> 10) & 0x00000010) + | ((Y >> 3) & 0x00000008) | ((Y >> 18) & 0x00000004) + | ((Y >> 26) & 0x00000002) | ((Y >> 24) & 0x00000001); + + *SK++ = ((X << 15) & 0x20000000) | ((X << 17) & 0x10000000) + | ((X << 10) & 0x08000000) | ((X << 22) & 0x04000000) + | ((X >> 2) & 0x02000000) | ((X << 1) & 0x01000000) + | ((X << 16) & 0x00200000) | ((X << 11) & 0x00100000) + | ((X << 3) & 0x00080000) | ((X >> 6) & 0x00040000) + | ((X << 15) & 0x00020000) | ((X >> 4) & 0x00010000) + | ((Y >> 2) & 0x00002000) | ((Y << 8) & 0x00001000) + | ((Y >> 14) & 0x00000808) | ((Y >> 9) & 0x00000400) + | ((Y ) & 0x00000200) | ((Y << 7) & 0x00000100) + | ((Y >> 7) & 0x00000020) | ((Y >> 3) & 0x00000011) + | ((Y << 2) & 0x00000004) | ((Y >> 21) & 0x00000002); + } +} + +/* DES 64-bit block encryption/decryption */ + +static void +des_crypt( const uint32_t SK[32], const uint8_t input[8], uint8_t output[8] ) { + uint32_t X, Y, T; + + GET_UINT32( X, input, 0 ); + GET_UINT32( Y, input, 4 ); + + DES_IP( X, Y ); + + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + + DES_FP( Y, X ); + + PUT_UINT32( Y, output, 0 ); + PUT_UINT32( X, output, 4 ); +} + +static int +lrandomkey(lua_State *L) { + char tmp[8]; + int i; + char x = 0; + for (i=0;i<8;i++) { + tmp[i] = random() & 0xff; + x ^= tmp[i]; + } + if (x==0) { + tmp[0] |= 1; // avoid 0 + } + lua_pushlstring(L, tmp, 8); + return 1; +} + +static void +des_key(lua_State *L, uint32_t SK[32]) { + size_t keysz = 0; + const void * key = luaL_checklstring(L, 1, &keysz); + if (keysz != 8) { + luaL_error(L, "Invalid key size %d, need 8 bytes", (int)keysz); + } + des_main_ks(SK, key); +} + +static int +ldesencode(lua_State *L) { + uint32_t SK[32]; + des_key(L, SK); + + size_t textsz = 0; + const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 2, &textsz); + size_t chunksz = (textsz + 8) & ~7; + uint8_t tmp[SMALL_CHUNK]; + uint8_t *buffer = tmp; + if (chunksz > SMALL_CHUNK) { + buffer = lua_newuserdata(L, chunksz); + } + int i; + for (i=0;i<(int)textsz-7;i+=8) { + des_crypt(SK, text+i, buffer+i); + } + int bytes = textsz - i; + uint8_t tail[8]; + int j; + for (j=0;j<8;j++) { + if (j < bytes) { + tail[j] = text[i+j]; + } else if (j==bytes) { + tail[j] = 0x80; + } else { + tail[j] = 0; + } + } + des_crypt(SK, tail, buffer+i); + lua_pushlstring(L, (const char *)buffer, chunksz); + + return 1; +} + +static int +ldesdecode(lua_State *L) { + uint32_t ESK[32]; + des_key(L, ESK); + uint32_t SK[32]; + int i; + for( i = 0; i < 32; i += 2 ) { + SK[i] = ESK[30 - i]; + SK[i + 1] = ESK[31 - i]; + } + size_t textsz = 0; + const uint8_t *text = (const uint8_t *)luaL_checklstring(L, 2, &textsz); + if ((textsz & 7) || textsz == 0) { + return luaL_error(L, "Invalid des crypt text length %d", (int)textsz); + } + uint8_t tmp[SMALL_CHUNK]; + uint8_t *buffer = tmp; + if (textsz > SMALL_CHUNK) { + buffer = lua_newuserdata(L, textsz); + } + for (i=0;i=textsz-8;i--) { + if (buffer[i] == 0) { + padding++; + } else if (buffer[i] == 0x80) { + break; + } else { + return luaL_error(L, "Invalid des crypt text"); + } + } + if (padding > 8) { + return luaL_error(L, "Invalid des crypt text"); + } + lua_pushlstring(L, (const char *)buffer, textsz - padding); + return 1; +} + + +static void +Hash(const char * str, int sz, uint8_t key[8]) { + uint32_t djb_hash = 5381L; + uint32_t js_hash = 1315423911L; + + int i; + for (i=0;i> 2)); + } + + key[0] = djb_hash & 0xff; + key[1] = (djb_hash >> 8) & 0xff; + key[2] = (djb_hash >> 16) & 0xff; + key[3] = (djb_hash >> 24) & 0xff; + + key[4] = js_hash & 0xff; + key[5] = (js_hash >> 8) & 0xff; + key[6] = (js_hash >> 16) & 0xff; + key[7] = (js_hash >> 24) & 0xff; +} + +static int +lhashkey(lua_State *L) { + size_t sz = 0; + const char * key = luaL_checklstring(L, 1, &sz); + uint8_t realkey[8]; + Hash(key,(int)sz,realkey); + lua_pushlstring(L, (const char *)realkey, 8); + return 1; +} + +static int +ltohex(lua_State *L) { + static char hex[] = "0123456789abcdef"; + size_t sz = 0; + const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 1, &sz); + char tmp[SMALL_CHUNK]; + char *buffer = tmp; + if (sz > SMALL_CHUNK/2) { + buffer = lua_newuserdata(L, sz * 2); + } + int i; + for (i=0;i> 4]; + buffer[i*2+1] = hex[text[i] & 0xf]; + } + lua_pushlstring(L, buffer, sz * 2); + return 1; +} + +#define HEX(v,c) { char tmp = (char) c; if (tmp >= '0' && tmp <= '9') { v = tmp-'0'; } else { v = tmp - 'a' + 10; } } + +static int +lfromhex(lua_State *L) { + size_t sz = 0; + const char * text = luaL_checklstring(L, 1, &sz); + if (sz & 1) { + return luaL_error(L, "Invalid hex text size %d", (int)sz); + } + char tmp[SMALL_CHUNK]; + char *buffer = tmp; + if (sz > SMALL_CHUNK*2) { + buffer = lua_newuserdata(L, sz / 2); + } + int i; + for (i=0;i 16 || low > 16) { + return luaL_error(L, "Invalid hex text", text); + } + buffer[i/2] = hi<<4 | low; + } + lua_pushlstring(L, buffer, i/2); + return 1; +} + +// Constants are the integer part of the sines of integers (in radians) * 2^32. +static const uint32_t k[64] = { +0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee , +0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501 , +0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be , +0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821 , +0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa , +0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8 , +0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed , +0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a , +0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c , +0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70 , +0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05 , +0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665 , +0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039 , +0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1 , +0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1 , +0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 }; + +// r specifies the per-round shift amounts +static const uint32_t r[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21}; + +// leftrotate function definition +#define LEFTROTATE(x, c) (((x) << (c)) | ((x) >> (32 - (c)))) + +static void +digest_md5(uint32_t w[16], uint32_t result[4]) { + uint32_t a, b, c, d, f, g, temp; + int i; + + a = 0x67452301u; + b = 0xefcdab89u; + c = 0x98badcfeu; + d = 0x10325476u; + + for(i = 0; i<64; i++) { + if (i < 16) { + f = (b & c) | ((~b) & d); + g = i; + } else if (i < 32) { + f = (d & b) | ((~d) & c); + g = (5*i + 1) % 16; + } else if (i < 48) { + f = b ^ c ^ d; + g = (3*i + 5) % 16; + } else { + f = c ^ (b | (~d)); + g = (7*i) % 16; + } + + temp = d; + d = c; + c = b; + b = b + LEFTROTATE((a + f + k[i] + w[g]), r[i]); + a = temp; + } + + result[0] = a; + result[1] = b; + result[2] = c; + result[3] = d; +} + +// hmac64 use md5 algorithm without padding, and the result is (c^d .. a^b) +static void +hmac(uint32_t x[2], uint32_t y[2], uint32_t result[2]) { + uint32_t w[16]; + uint32_t r[4]; + int i; + for (i=0;i<16;i+=4) { + w[i] = x[1]; + w[i+1] = x[0]; + w[i+2] = y[1]; + w[i+3] = y[0]; + } + + digest_md5(w,r); + + result[0] = r[2]^r[3]; + result[1] = r[0]^r[1]; +} + +static void +hmac_md5(uint32_t x[2], uint32_t y[2], uint32_t result[2]) { + uint32_t w[16]; + uint32_t r[4]; + int i; + for (i=0;i<12;i+=4) { + w[i] = x[0]; + w[i+1] = x[1]; + w[i+2] = y[0]; + w[i+3] = y[1]; + } + + w[12] = 0x80; + w[13] = 0; + w[14] = 384; + w[15] = 0; + + digest_md5(w,r); + + result[0] = (r[0] + 0x67452301u) ^ (r[2] + 0x98badcfeu); + result[1] = (r[1] + 0xefcdab89u) ^ (r[3] + 0x10325476u); +} + +static void +read64(lua_State *L, uint32_t xx[2], uint32_t yy[2]) { + size_t sz = 0; + const uint8_t *x = (const uint8_t *)luaL_checklstring(L, 1, &sz); + if (sz != 8) { + luaL_error(L, "Invalid uint64 x"); + } + const uint8_t *y = (const uint8_t *)luaL_checklstring(L, 2, &sz); + if (sz != 8) { + luaL_error(L, "Invalid uint64 y"); + } + xx[0] = x[0] | x[1]<<8 | x[2]<<16 | x[3]<<24; + xx[1] = x[4] | x[5]<<8 | x[6]<<16 | x[7]<<24; + yy[0] = y[0] | y[1]<<8 | y[2]<<16 | y[3]<<24; + yy[1] = y[4] | y[5]<<8 | y[6]<<16 | y[7]<<24; +} + +static int +pushqword(lua_State *L, uint32_t result[2]) { + uint8_t tmp[8]; + tmp[0] = result[0] & 0xff; + tmp[1] = (result[0] >> 8 )& 0xff; + tmp[2] = (result[0] >> 16 )& 0xff; + tmp[3] = (result[0] >> 24 )& 0xff; + tmp[4] = result[1] & 0xff; + tmp[5] = (result[1] >> 8 )& 0xff; + tmp[6] = (result[1] >> 16 )& 0xff; + tmp[7] = (result[1] >> 24 )& 0xff; + + lua_pushlstring(L, (const char *)tmp, 8); + return 1; +} + +static int +lhmac64(lua_State *L) { + uint32_t x[2], y[2]; + read64(L, x, y); + uint32_t result[2]; + hmac(x,y,result); + return pushqword(L, result); +} + +/* + h1 = crypt.hmac64_md5(a,b) + m = md5.sum((a..b):rep(3)) + h2 = crypt.xor_str(m:sub(1,8), m:sub(9,16)) + assert(h1 == h2) + */ +static int +lhmac64_md5(lua_State *L) { + uint32_t x[2], y[2]; + read64(L, x, y); + uint32_t result[2]; + hmac_md5(x,y,result); + return pushqword(L, result); +} + +/* + 8bytes key + string text + */ +static int +lhmac_hash(lua_State *L) { + uint32_t key[2]; + size_t sz = 0; + const uint8_t *x = (const uint8_t *)luaL_checklstring(L, 1, &sz); + if (sz != 8) { + luaL_error(L, "Invalid uint64 key"); + } + key[0] = x[0] | x[1]<<8 | x[2]<<16 | x[3]<<24; + key[1] = x[4] | x[5]<<8 | x[6]<<16 | x[7]<<24; + const char * text = luaL_checklstring(L, 2, &sz); + uint8_t h[8]; + Hash(text,(int)sz,h); + uint32_t htext[2]; + htext[0] = h[0] | h[1]<<8 | h[2]<<16 | h[3]<<24; + htext[1] = h[4] | h[5]<<8 | h[6]<<16 | h[7]<<24; + uint32_t result[2]; + hmac(htext,key,result); + return pushqword(L, result); +} + +// powmodp64 for DH-key exchange + +// The biggest 64bit prime +#define P 0xffffffffffffffc5ull + +static inline uint64_t +mul_mod_p(uint64_t a, uint64_t b) { + uint64_t m = 0; + while(b) { + if(b&1) { + uint64_t t = P-a; + if ( m >= t) { + m -= t; + } else { + m += a; + } + } + if (a >= P - a) { + a = a * 2 - P; + } else { + a = a * 2; + } + b>>=1; + } + return m; +} + +static inline uint64_t +pow_mod_p(uint64_t a, uint64_t b) { + if (b==1) { + return a; + } + uint64_t t = pow_mod_p(a, b>>1); + t = mul_mod_p(t,t); + if (b % 2) { + t = mul_mod_p(t, a); + } + return t; +} + +// calc a^b % p +static uint64_t +powmodp(uint64_t a, uint64_t b) { + if (a > P) + a%=P; + return pow_mod_p(a,b); +} + +static void +push64(lua_State *L, uint64_t r) { + uint8_t tmp[8]; + tmp[0] = r & 0xff; + tmp[1] = (r >> 8 )& 0xff; + tmp[2] = (r >> 16 )& 0xff; + tmp[3] = (r >> 24 )& 0xff; + tmp[4] = (r >> 32 )& 0xff; + tmp[5] = (r >> 40 )& 0xff; + tmp[6] = (r >> 48 )& 0xff; + tmp[7] = (r >> 56 )& 0xff; + + lua_pushlstring(L, (const char *)tmp, 8); +} + +static int +ldhsecret(lua_State *L) { + uint32_t x[2], y[2]; + read64(L, x, y); + uint64_t xx = (uint64_t)x[0] | (uint64_t)x[1]<<32; + uint64_t yy = (uint64_t)y[0] | (uint64_t)y[1]<<32; + if (xx == 0 || yy == 0) + return luaL_error(L, "Can't be 0"); + uint64_t r = powmodp(xx, yy); + + push64(L, r); + + return 1; +} + +#define G 5 + +static int +ldhexchange(lua_State *L) { + size_t sz = 0; + const uint8_t *x = (const uint8_t *)luaL_checklstring(L, 1, &sz); + if (sz != 8) { + luaL_error(L, "Invalid dh uint64 key"); + } + uint32_t xx[2]; + xx[0] = x[0] | x[1]<<8 | x[2]<<16 | x[3]<<24; + xx[1] = x[4] | x[5]<<8 | x[6]<<16 | x[7]<<24; + + uint64_t x64 = (uint64_t)xx[0] | (uint64_t)xx[1]<<32; + if (x64 == 0) + return luaL_error(L, "Can't be 0"); + + uint64_t r = powmodp(G, x64); + push64(L, r); + return 1; +} + +// base64 + +static int +lb64encode(lua_State *L) { + static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + size_t sz = 0; + const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 1, &sz); + int encode_sz = (sz + 2)/3*4; + char tmp[SMALL_CHUNK]; + char *buffer = tmp; + if (encode_sz > SMALL_CHUNK) { + buffer = lua_newuserdata(L, encode_sz); + } + int i,j; + j=0; + for (i=0;i<(int)sz-2;i+=3) { + uint32_t v = text[i] << 16 | text[i+1] << 8 | text[i+2]; + buffer[j] = encoding[v >> 18]; + buffer[j+1] = encoding[(v >> 12) & 0x3f]; + buffer[j+2] = encoding[(v >> 6) & 0x3f]; + buffer[j+3] = encoding[(v) & 0x3f]; + j+=4; + } + int padding = sz-i; + uint32_t v; + switch(padding) { + case 1 : + v = text[i]; + buffer[j] = encoding[v >> 2]; + buffer[j+1] = encoding[(v & 3) << 4]; + buffer[j+2] = '='; + buffer[j+3] = '='; + break; + case 2 : + v = text[i] << 8 | text[i+1]; + buffer[j] = encoding[v >> 10]; + buffer[j+1] = encoding[(v >> 4) & 0x3f]; + buffer[j+2] = encoding[(v & 0xf) << 2]; + buffer[j+3] = '='; + break; + } + lua_pushlstring(L, buffer, encode_sz); + return 1; +} + +static inline int +b64index(uint8_t c) { + static const int decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51}; + int decoding_size = sizeof(decoding)/sizeof(decoding[0]); + if (c<43) { + return -1; + } + c -= 43; + if (c>=decoding_size) + return -1; + return decoding[c]; +} + +static int +lb64decode(lua_State *L) { + size_t sz = 0; + const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 1, &sz); + int decode_sz = (sz+3)/4*3; + char tmp[SMALL_CHUNK]; + char *buffer = tmp; + if (decode_sz > SMALL_CHUNK) { + buffer = lua_newuserdata(L, decode_sz); + } + int i,j; + int output = 0; + for (i=0;i=sz) { + return luaL_error(L, "Invalid base64 text"); + } + c[j] = b64index(text[i]); + if (c[j] == -1) { + ++i; + continue; + } + if (c[j] == -2) { + ++padding; + } + ++i; + ++j; + } + uint32_t v; + switch (padding) { + case 0: + v = (unsigned)c[0] << 18 | c[1] << 12 | c[2] << 6 | c[3]; + buffer[output] = v >> 16; + buffer[output+1] = (v >> 8) & 0xff; + buffer[output+2] = v & 0xff; + output += 3; + break; + case 1: + if (c[3] != -2 || (c[2] & 3)!=0) { + return luaL_error(L, "Invalid base64 text"); + } + v = (unsigned)c[0] << 10 | c[1] << 4 | c[2] >> 2 ; + buffer[output] = v >> 8; + buffer[output+1] = v & 0xff; + output += 2; + break; + case 2: + if (c[3] != -2 || c[2] != -2 || (c[1] & 0xf) !=0) { + return luaL_error(L, "Invalid base64 text"); + } + v = (unsigned)c[0] << 2 | c[1] >> 4; + buffer[output] = v; + ++ output; + break; + default: + return luaL_error(L, "Invalid base64 text"); + } + } + lua_pushlstring(L, buffer, output); + return 1; +} + +static int +lxor_str(lua_State *L) { + size_t len1,len2; + const char *s1 = luaL_checklstring(L,1,&len1); + const char *s2 = luaL_checklstring(L,2,&len2); + if (len2 == 0) { + return luaL_error(L, "Can't xor empty string"); + } + luaL_Buffer b; + char * buffer = luaL_buffinitsize(L, &b, len1); + int i; + for (i=0;i> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +/* FIXME: can we do this in an endian-proof way? */ +#ifdef WORDS_BIGENDIAN +#define blk0(i) block.l[i] +#else +#define blk0(i) (block.l[i] = (rol(block.l[i],24)&0xFF00FF00) \ + |(rol(block.l[i],8)&0x00FF00FF)) +#endif +#define blk(i) (block.l[i&15] = rol(block.l[(i+13)&15]^block.l[(i+8)&15] \ + ^block.l[(i+2)&15]^block.l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + +/* Hash a single 512-bit block. This is the core of the algorithm. */ +static void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]) +{ + uint32_t a, b, c, d, e; + typedef union { + uint8_t c[64]; + uint32_t l[16]; + } CHAR64LONG16; + CHAR64LONG16 block; + + memcpy(&block, buffer, 64); + + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + + /* Wipe variables */ + a = b = c = d = e = 0; +} + + +/* SHA1Init - Initialize new context */ +static void sat_SHA1_Init(SHA1_CTX* context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ +static void sat_SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len) +{ + size_t i, j; + +#ifdef VERBOSE + SHAPrintContext(context, "before"); +#endif + + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; + context->count[1] += (len >> 29); + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1_Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1_Transform(context->state, data + i); + } + j = 0; + } + else i = 0; + memcpy(&context->buffer[j], &data[i], len - i); + +#ifdef VERBOSE + SHAPrintContext(context, "after "); +#endif +} + + +/* Add padding and return the message digest. */ +static void sat_SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE]) +{ + uint32_t i; + uint8_t finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + sat_SHA1_Update(context, (uint8_t *)"\200", 1); + while ((context->count[0] & 504) != 448) { + sat_SHA1_Update(context, (uint8_t *)"\0", 1); + } + sat_SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */ + for (i = 0; i < SHA1_DIGEST_SIZE; i++) { + digest[i] = (uint8_t) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + + /* Wipe variables */ + i = 0; + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(finalcount, 0, 8); /* SWR */ +} + +int +lsha1(lua_State *L) { + size_t sz = 0; + const uint8_t * buffer = (const uint8_t *)luaL_checklstring(L, 1, &sz); + uint8_t digest[SHA1_DIGEST_SIZE]; + SHA1_CTX ctx; + sat_SHA1_Init(&ctx); + sat_SHA1_Update(&ctx, buffer, sz); + sat_SHA1_Final(&ctx, digest); + lua_pushlstring(L, (const char *)digest, SHA1_DIGEST_SIZE); + + return 1; +} + +#define BLOCKSIZE 64 + +static inline void +xor_key(uint8_t key[BLOCKSIZE], uint32_t xor) { + int i; + for (i=0;i BLOCKSIZE) { + SHA1_CTX ctx; + sat_SHA1_Init(&ctx); + sat_SHA1_Update(&ctx, key, key_sz); + sat_SHA1_Final(&ctx, rkey); + key_sz = SHA1_DIGEST_SIZE; + } else { + memcpy(rkey, key, key_sz); + } + + xor_key(rkey, 0x5c5c5c5c); + sat_SHA1_Init(&ctx1); + sat_SHA1_Update(&ctx1, rkey, BLOCKSIZE); + + xor_key(rkey, 0x5c5c5c5c ^ 0x36363636); + sat_SHA1_Init(&ctx2); + sat_SHA1_Update(&ctx2, rkey, BLOCKSIZE); + sat_SHA1_Update(&ctx2, text, text_sz); + sat_SHA1_Final(&ctx2, digest2); + + sat_SHA1_Update(&ctx1, digest2, SHA1_DIGEST_SIZE); + sat_SHA1_Final(&ctx1, digest1); + + lua_pushlstring(L, (const char *)digest1, SHA1_DIGEST_SIZE); + + return 1; +} diff --git a/luaclib/src/lcrypt/sha256.c b/luaclib/src/lcrypt/sha256.c new file mode 100644 index 00000000..31b2a689 --- /dev/null +++ b/luaclib/src/lcrypt/sha256.c @@ -0,0 +1,168 @@ +#include "../../../src/core.h" + +#define SHA256_HASH_SIZE 32 + +typedef unsigned char BYTE; +typedef unsigned int WORD; + +typedef struct { + BYTE data[64]; // current 512-bit chunk of message data, just like a buffer + WORD datalen; // sign the data length of current chunk + unsigned long long bitlen; // the bit length of the total message + WORD state[8]; // store the middle state of hash abstract +} SHA256_CTX; + + +#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) +#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) + +#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) +#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) +#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) +#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) +#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) + +static const WORD K[64] = { + 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, + 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, + 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, + 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, + 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, + 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, + 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, + 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 +}; + + +void sha256_transform(SHA256_CTX *ctx, const BYTE data[]) +{ + WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; + + // initialization + for (i = 0, j = 0; i < 16; ++i, j += 4) + m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); + for ( ; i < 64; ++i) + m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; + + a = ctx->state[0]; + b = ctx->state[1]; + c = ctx->state[2]; + d = ctx->state[3]; + e = ctx->state[4]; + f = ctx->state[5]; + g = ctx->state[6]; + h = ctx->state[7]; + + for (i = 0; i < 64; ++i) { + t1 = h + EP1(e) + CH(e,f,g) + K[i] + m[i]; + t2 = EP0(a) + MAJ(a,b,c); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + ctx->state[0] += a; + ctx->state[1] += b; + ctx->state[2] += c; + ctx->state[3] += d; + ctx->state[4] += e; + ctx->state[5] += f; + ctx->state[6] += g; + ctx->state[7] += h; +} + +void sha256_init(SHA256_CTX *ctx) +{ + ctx->datalen = 0; + ctx->bitlen = 0; + ctx->state[0] = 0x6a09e667; + ctx->state[1] = 0xbb67ae85; + ctx->state[2] = 0x3c6ef372; + ctx->state[3] = 0xa54ff53a; + ctx->state[4] = 0x510e527f; + ctx->state[5] = 0x9b05688c; + ctx->state[6] = 0x1f83d9ab; + ctx->state[7] = 0x5be0cd19; +} + +void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len) +{ + WORD i; + + for (i = 0; i < len; ++i) { + ctx->data[ctx->datalen] = data[i]; + ctx->datalen++; + if (ctx->datalen == 64) { + // 64 byte = 512 bit means the buffer ctx->data has fully stored one chunk of message + // so do the sha256 hash map for the current chunk + sha256_transform(ctx, ctx->data); + ctx->bitlen += 512; + ctx->datalen = 0; + } + } +} + +void sha256_final(SHA256_CTX *ctx, BYTE hash[]) +{ + WORD i; + + i = ctx->datalen; + + // Pad whatever data is left in the buffer. + if (ctx->datalen < 56) { + ctx->data[i++] = 0x80; // pad 10000000 = 0x80 + while (i < 56) + ctx->data[i++] = 0x00; + } + else { + ctx->data[i++] = 0x80; + while (i < 64) + ctx->data[i++] = 0x00; + sha256_transform(ctx, ctx->data); + memset(ctx->data, 0, 56); + } + + // Append to the padding the total message's length in bits and transform. + ctx->bitlen += ctx->datalen * 8; + ctx->data[63] = ctx->bitlen; + ctx->data[62] = ctx->bitlen >> 8; + ctx->data[61] = ctx->bitlen >> 16; + ctx->data[60] = ctx->bitlen >> 24; + ctx->data[59] = ctx->bitlen >> 32; + ctx->data[58] = ctx->bitlen >> 40; + ctx->data[57] = ctx->bitlen >> 48; + ctx->data[56] = ctx->bitlen >> 56; + sha256_transform(ctx, ctx->data); + + // copying the final state to the output hash(use big endian). + for (i = 0; i < 4; ++i) { + hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; + hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; + hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; + hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; + hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; + hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; + hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; + hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; + } + +} + +LUAMOD_API int +lsha256(lua_State *L) { + size_t sz = 0; + const uint8_t * buffer = (const uint8_t *)luaL_checklstring(L, 1, &sz); + uint8_t hash[SHA256_HASH_SIZE]; + SHA256_CTX ctx; + sha256_init(&ctx); + sha256_update(&ctx, buffer, sz); + sha256_final(&ctx, hash); + lua_pushlstring(L, (const char *)hash, SHA256_HASH_SIZE); + return 1; +} diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index deb267e6..a3865db1 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -1,7 +1,16 @@ local CRYPT = require "lcrypt" +local fmt = string.format +local byte = string.byte +local match = string.match +local concat = table.concat + local sha1 = CRYPT.sha1 + +local sha256 = CRYPT.sha256 + local xor_str = CRYPT.xor_str + local crc32 = CRYPT.crc32 local crc64 = CRYPT.crc64 @@ -29,8 +38,28 @@ local dhexchange = CRYPT.dhexchange local crypt = {} -function crypt.sha1(...) - return sha1(...) +function crypt.sha1(str, hex) + local hash = sha1(str) + if hash and hex then + local tab = {} + for i = 1, #hash do + tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) + end + return concat(tab) + end + return hash +end + +function crypt.sha256 (str, hex) + local hash = sha256(str) + if hash and hex then + local tab = {} + for i = 1, #hash do + tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) + end + return concat(tab) + end + return hash end function crypt.xor_str (...) From 4cb1b133e0fd9d114f5fecccf8e03eea14ddf9b5 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 7 Jun 2019 22:18:13 +0800 Subject: [PATCH 136/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0hmac=5Fsha1=E4=B8=8Eh?= =?UTF-8?q?mac=5Fsha256=E6=96=B9=E6=B3=95,=20=E4=BC=A0=E5=85=A5=E7=AC=AC?= =?UTF-8?q?=E4=B8=89=E4=B8=AA=E5=8F=82=E6=95=B0=E5=88=99hex=E5=88=99?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E4=B8=BA=E5=8F=AF=E6=89=93=E5=8D=B0=E5=AD=97?= =?UTF-8?q?=E7=AC=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/lcrypt.c | 2 ++ luaclib/src/lcrypt/sha256.c | 60 +++++++++++++++++++++++++++++++++++-- lualib/crypt/init.lua | 32 ++++++++++++++++---- 3 files changed, 86 insertions(+), 8 deletions(-) diff --git a/luaclib/src/lcrypt/lcrypt.c b/luaclib/src/lcrypt/lcrypt.c index 532f65ec..5a2b21ed 100644 --- a/luaclib/src/lcrypt/lcrypt.c +++ b/luaclib/src/lcrypt/lcrypt.c @@ -953,6 +953,7 @@ int lhmac_sha1(lua_State *L); // defined sha256.c int lsha256(lua_State *L); +int lhmac_sha256(lua_State *L); LUAMOD_API int luaopen_lcrypt(lua_State *L) { @@ -973,6 +974,7 @@ luaopen_lcrypt(lua_State *L) { { "sha1", lsha1 }, { "hmac_sha1", lhmac_sha1 }, { "sha256", lsha256 }, + { "hmac_sha256", lhmac_sha256 }, { "crc32", lcrc32 }, { "crc64", lcrc64 }, { "hmac_hash", lhmac_hash }, diff --git a/luaclib/src/lcrypt/sha256.c b/luaclib/src/lcrypt/sha256.c index 31b2a689..807f6822 100644 --- a/luaclib/src/lcrypt/sha256.c +++ b/luaclib/src/lcrypt/sha256.c @@ -154,15 +154,69 @@ void sha256_final(SHA256_CTX *ctx, BYTE hash[]) } +static inline +void sha256(SHA256_CTX *ctx, const char *buffer, size_t sz, char* hash){ + sha256_init(&ctx); + sha256_update(&ctx, buffer, sz); + sha256_final(&ctx, hash); +} + LUAMOD_API int lsha256(lua_State *L) { size_t sz = 0; const uint8_t * buffer = (const uint8_t *)luaL_checklstring(L, 1, &sz); uint8_t hash[SHA256_HASH_SIZE]; SHA256_CTX ctx; - sha256_init(&ctx); - sha256_update(&ctx, buffer, sz); - sha256_final(&ctx, hash); + sha256(&ctx, buffer, sz, hash); lua_pushlstring(L, (const char *)hash, SHA256_HASH_SIZE); return 1; } + +static inline +void xor_buf(uint8_t buf1[SHA256_HASH_SIZE], uint8_t buf2[SHA256_HASH_SIZE], size_t key_size) { + for(int i = 0; i< key_size; i++){ + *(buf1 + i) = *(buf1 + i) ^ 0x5c; + *(buf2 + i) = *(buf2 + i) ^ 0x36; + } +} + +LUAMOD_API int +lhmac_sha256(lua_State *L) { + size_t len_key = 0; + const uint8_t * key = (const uint8_t *)luaL_checklstring(L, 1, &len_key); + size_t len = 0; + const uint8_t * data = (const uint8_t *)luaL_checklstring(L, 2, &len); + int block_size = 64; + int hash_size = 32; + int key_size = block_size; + unsigned char buf[block_size]; + unsigned char buf2[block_size]; + bzero(buf, block_size); + bzero(buf2, block_size); + if (len_key > block_size) { + key_size = hash_size; + SHA256_CTX ctx; + sha256(&ctx, data, len, buf); + memcpy(buf2, buf, key_size); + } else { + memcpy(buf, key, len_key); + memcpy(buf2, key, len_key); + } + xor_buf(buf, buf2, key_size); + size_t hash_buf_size = key_size + (len < hash_size ? hash_size : len); + unsigned char hash_buf[hash_buf_size]; + memcpy(hash_buf, buf2, key_size); + memcpy(hash_buf + key_size, data, len); + + SHA256_CTX ctx1, ctx2; + unsigned char hash_out[SHA256_HASH_SIZE]; + sha256(&ctx1, hash_buf, key_size + len, hash_out); + memcpy(hash_buf, buf, key_size); + memcpy(hash_buf + key_size, hash_out, hash_size); + + unsigned char hash_buffer[SHA256_HASH_SIZE]; + sha256(&ctx2, hash_buf, key_size + hash_size, hash_buffer); + + lua_pushlstring(L, (const char *)hash_buffer, SHA256_HASH_SIZE); + return 1; +} diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index a3865db1..360b2276 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -1,4 +1,5 @@ local CRYPT = require "lcrypt" +local new_tab = require("sys").new_tab local fmt = string.format local byte = string.byte @@ -6,8 +7,10 @@ local match = string.match local concat = table.concat local sha1 = CRYPT.sha1 +local hmac_sha1 = CRYPT.hmac_sha1 local sha256 = CRYPT.sha256 +local hmac_sha256 = CRYPT.hmac_sha256 local xor_str = CRYPT.xor_str @@ -17,7 +20,6 @@ local crc64 = CRYPT.crc64 local randomkey = CRYPT.randomkey local hashkey = CRYPT.hashkey -local hmac_sha1 = CRYPT.hmac_sha1 local hmac_hash = CRYPT.hmac_hash local hmac64 = CRYPT.hmac64 @@ -41,7 +43,7 @@ local crypt = {} function crypt.sha1(str, hex) local hash = sha1(str) if hash and hex then - local tab = {} + local tab = new_tab(#hash, 0) for i = 1, #hash do tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) end @@ -53,7 +55,7 @@ end function crypt.sha256 (str, hex) local hash = sha256(str) if hash and hex then - local tab = {} + local tab = new_tab(#hash, 0) for i = 1, #hash do tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) end @@ -74,8 +76,28 @@ function crypt.hashkey (...) return hashkey(...) end -function crypt.hmac_sha1 (...) - return hmac_sha1(...) +function crypt.hmac_sha1 (key, text, hex) + local hash = hmac_sha1(key, text) + if hash and hex then + local tab = new_tab(#hash, 0) + for i = 1, #hash do + tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) + end + return concat(tab) + end + return hash +end + +function crypt.hmac_sha256 (key, text, hex) + local hash = hmac_sha256(key, text) + if hash and hex then + local tab = new_tab(#hash, 0) + for i = 1, #hash do + tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) + end + return concat(tab) + end + return hash end function crypt.hmac_hash (...) From 4c091d4072fe5459eaf10bcdd1c44b100744be89 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 7 Jun 2019 22:30:08 +0800 Subject: [PATCH 137/956] =?UTF-8?q?=E9=80=82=E9=85=8D=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E7=BC=96=E8=AF=91=E5=99=A8=E7=89=88=E6=9C=AC=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/sha256.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/luaclib/src/lcrypt/sha256.c b/luaclib/src/lcrypt/sha256.c index 807f6822..b172ab05 100644 --- a/luaclib/src/lcrypt/sha256.c +++ b/luaclib/src/lcrypt/sha256.c @@ -174,7 +174,8 @@ lsha256(lua_State *L) { static inline void xor_buf(uint8_t buf1[SHA256_HASH_SIZE], uint8_t buf2[SHA256_HASH_SIZE], size_t key_size) { - for(int i = 0; i< key_size; i++){ + int i; + for(i = 0; i< key_size; i++){ *(buf1 + i) = *(buf1 + i) ^ 0x5c; *(buf2 + i) = *(buf2 + i) ^ 0x36; } From de8dba45cc15d6b2796430f86d7d9ddad86f591e Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 8 Jun 2019 07:29:38 +0800 Subject: [PATCH 138/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AAmqt?= =?UTF-8?q?t=E8=AE=A2=E9=98=85=E7=9A=84=E6=97=B6=E5=80=99,=20=E5=8F=91?= =?UTF-8?q?=E9=80=81ping=E5=91=BD=E4=BB=A4=E7=9A=84but?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/mqtt/init.lua | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lualib/protocol/mqtt/init.lua b/lualib/protocol/mqtt/init.lua index d4e2e2de..93bb8280 100644 --- a/lualib/protocol/mqtt/init.lua +++ b/lualib/protocol/mqtt/init.lua @@ -56,21 +56,22 @@ function client:ctor(opt) end function client:subscribe(opt, func) - local args = { type = packet_type.SUBSCRIBE, subscriptions = {opt} } self.handle = func - self:_send_packet(args) + local args = { type = packet_type.SUBSCRIBE, subscriptions = {opt} } + local ok, err = self:_send_packet(args) + if not ok then + return false, 'SUBSCRIBE send failed' + end local ok, err = self:_wait_packet_exact{type=packet_type.SUBACK, packet_id=args.packet_id} if not ok then return false, 'SUBSCRIBE wait for SUBACK failed' end co_spwan(function ( ... ) - local co_current = Co.self() local time = os_time() local timer = Timer.at(self.keep_alive, function ( ... ) if os_time() >= time + self.keep_alive then - return co.wakeup(co_current) + return self:ping() end - return self:ping() end) while 1 do local packet, perr = self:_wait_packet_queue() @@ -95,10 +96,11 @@ function client:subscribe(opt, func) elseif packet.type == packet_type.PUBACK then self:acknowledge(packet) -- elseif packet.type == packet_type.PINGRESP then - -- -- pass + -- pass -- else -- return false, "unexpected packet received: "..tostring(packet) end + end end) return true From 985f1954c2662a7ef9ff37f58632e26b23c15db7 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 8 Jun 2019 12:14:46 +0800 Subject: [PATCH 139/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0MQ=E5=AE=9E=E7=8E=B0,?= =?UTF-8?q?=20=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=BA=9B=E4=B8=8D=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/MQ/init.lua | 193 --------------------- lualib/MQ/mqtt.lua | 134 +++++++++++++++ lualib/MQ/redis.lua | 126 ++++++++++++++ lualib/MQ/stomp.lua | 126 ++++++++++++++ lualib/protocol/stomp/init.lua | 140 +++++++++++++++ lualib/protocol/stomp/protocol.lua | 268 +++++++++++++++++++++++++++++ 6 files changed, 794 insertions(+), 193 deletions(-) delete mode 100644 lualib/MQ/init.lua create mode 100644 lualib/MQ/mqtt.lua create mode 100644 lualib/MQ/redis.lua create mode 100644 lualib/MQ/stomp.lua create mode 100644 lualib/protocol/stomp/init.lua create mode 100644 lualib/protocol/stomp/protocol.lua diff --git a/lualib/MQ/init.lua b/lualib/MQ/init.lua deleted file mode 100644 index 0e00c984..00000000 --- a/lualib/MQ/init.lua +++ /dev/null @@ -1,193 +0,0 @@ -local log = require "logging" -local class = require "class" -local Timer = require "internal.Timer" -local mqtt = require "protocol.mqtt" -local redis = require "protocol.redis" - -local Log = log:new({ dump = true, path = 'MQ' }) - -local type = type -local math = math -local random = math.random -local stirng = string -local fmt = string.format -local assert = assert - -local mq = class("mq") - -function mq:ctor(opt) - self.id = opt.id -- mqtt需要id可以指定 - self.host = opt.host -- 地址 - self.port = opt.port -- 端口 - self.type = opt.type -- 消息队列种类 - self.auth = opt.auth -- redis需要auth可以指定 - self.clean = opt.clean or true -- mqtt 默认清除会话 - self.username = opt.username -- mqtt只支持用户名+密码认证 - self.password = opt.password -- mqtt只支持用户名+密码认证 -end - -local function mq_login(self) - local times = 1 - while 1 do - if self.type == 'redis' then - local rds = redis:new {auth = self.auth, host = self.host, port = self.port} - local ok, err = rds:connect() - if ok then - return rds - end - rds:close() - Log:ERROR('连接mq(redis)失败:'..(err or "unknow")..'.正在尝试重连') - Timer.sleep(3) - times = times + 1 - elseif self.type == 'mqtt' then - local mqtt = mqtt:new { - host = self.host, - port = self.port, - auth = { - username = self.username, - password = self.password - }, - id = self.id or fmt('luamqtt-cf-v1-%X', random(1, 0xFFFFFFFF)), - } - local ok, err = mqtt:connect() - if ok then - return mqtt - end - mqtt:close() - Log:ERROR('连接mq(mqtt)失败:'..(err or "unknow")..'.正在尝试重连') - Timer.sleep(3) - times = times + 1 - else - error("未知的mq类型.") - end - end -end - -local function redis_subscribe(self) - local sub_mq, err = mq_login(self) - if not sub_mq then - return nil, err - end - self.sub_mq = sub_mq - return sub_mq:subscribe(self.pattern, self.func) -end - -local function mqtt_subscribe(self) - local sub_mq, err = mq_login(self) - if not sub_mq then - return nil, err - end - self.sub_mq = sub_mq - return sub_mq:subscribe({qos = 2, topic = self.pattern, clean = self.clean}, self.func) -end - -local function redis_publish(self) - local index = 1 - while 1 do - if not self.pub_mq then - local pub_mq, err = mq_login(self) - if not pub_mq then - return nil, err - end - self.pub_mq = pub_mq - end - while 1 do - local msg = self.queue[index] - if not msg then - self.queue = {} - return true - end - local ok, err = self.pub_mq:publish(msg.pattern, msg.payload) - if not ok then - break - end - index = index + 1 - end - if self.pub_mq then - self.pub_mq:close() - self.pub_mq = nil - end - end -end - -local function mqtt_publish(self) - local index = 1 - while 1 do - if not self.pub_mq then - local pub_mq, err = mq_login(self) - if not pub_mq then - return nil, err - end - self.pub_mq = pub_mq - end - while 1 do - local msg = self.queue[index] - if not msg then - self.queue = {} - return true - end - local ok = self.pub_mq:publish{topic = msg.pattern, payload = msg.payload, qos = 2} - if not ok then - break - end - index = index + 1 - end - if self.pub_mq then - self.pub_mq:close() - self.pub_mq = nil - end - end -end - --- 发布消息 -function mq:emit(pattern, data) - if type(pattern) ~= 'string' or pattern == '' then - return nil, "publish pattern error." - end - if type(data) ~= 'string' or data == '' then - return nil, "publish string error." - end - if not self.queue then - self.queue = {{pattern = pattern, payload = data}} - else - self.queue[#self.queue + 1] = {pattern = pattern, payload = data} - end - if self.type == 'redis' then - return redis_publish(self) - elseif self.type == 'mqtt' then - return mqtt_publish(self) - end - return error("mq publish error: 目前仅支持redis/mqtt协议.") -end - --- 订阅消息 -function mq:on(pattern, func) - if type(pattern) ~= 'string' or pattern == '' then - return nil, "subscribe pattern error." - end - if type(func) ~= 'function' then - return nil, "subscribe func error." - end - self.func = func -- 回调处理函数 - self.pattern = pattern -- 监听规则 - if self.type == 'redis' then - return redis_subscribe(self) - elseif self.type == 'mqtt' then - return mqtt_subscribe(self) - end - return error("mq subscribe error: 目前仅支持redis/mqtt协议.") -end - --- 关闭消息队列监听 -function mq:close() - if self.sub_mq then - self.sub_mq:close() - self.sub_mq = nil - end - if self.pub_mq then - self.pub_mq:close() - self.pub_mq = nil - end -end - -return mq diff --git a/lualib/MQ/mqtt.lua b/lualib/MQ/mqtt.lua new file mode 100644 index 00000000..153af41f --- /dev/null +++ b/lualib/MQ/mqtt.lua @@ -0,0 +1,134 @@ +local class = require "class" +local mqtt = require "protocol.mqtt" + +local logging = require "logging" +local Log = logging:new{dump = true, path = 'MQ-mqtt'} + +local cf = require "cf" +local cf_self = cf.self +local cf_fork = cf.fork +local cf_sleep = cf.sleep +local cf_wait = cf.wait +local cf_wakeup = cf.wakeup + +local ipairs = ipairs +local type = type +local insert = table.insert +local random = math.random +local fmt = string.format + +local mq = class('mqtt-mq') + +function mq:ctor (opt) + self.host = opt.host + self.port = opt.port + self.username = opt.username + self.password = opt.password + self.keepalive = opt.keepalive + self.patterns = {} + self.subsribes = {} + self.queue = {} +end + +local function _login (opt) + local times = 1 + while 1 do + local mq = mqtt:new { + id = opt.id or fmt('luamqtt-cf-v1-%X', random(1, 0xFFFFFFFF)), + host = opt.host, + port = opt.port, + clean = true, + keep_alive = opt.keepalive, + auth = { + username = opt.username, + password = opt.password, + }, + } + local ok, err = mq:connect() + if ok then + return mq + end + Log:WARN("第"..times.."次连接MQ(mqtt)失败:"..(err or "未知错误.")) + times = times + 1 + mq:close() + cf_sleep(3) + end +end + +-- 订阅事件循环 +local function subscribe (self, pattern, func) + local mq = _login(self) + self.subsribes[#self.subsribes+1] = mq + return mq:subscribe({qos = 2, topic = pattern}, func) +end + +-- 发布事件循环 +local function publish (self, pattern, data) + if #self.queue == 0 then + self.queue[#self.queue + 1] = {pattern = pattern, data = data, co = cf_self()} + cf_fork(function (...) + for _, msg in ipairs(self.queue) do + if not self.closed and self.emiter then + cf_wakeup(msg.co, self.emiter:publish{topic = msg.pattern, payload = msg.data, qos = 2}) + end + end + self.queue = {} + end) + return cf_wait() + end + insert(self.queue, {pattern = pattern, data = data, co = cf_self()}) + return cf_wait() +end + +-- 订阅 +function mq:on (pattern, func) + if type(pattern) ~= 'string' or pattern == '' then + return nil, "订阅消息失败: 错误的pattern类型" + end + if type(func) ~= 'function' then + return nil, "订阅消息失败: 错误的func类型" + end + for _, patt in ipairs(self.patterns) do + if patt == pattern then + return nil, '禁止重复订阅相同的频道' + end + end + self.patterns[#self.patterns + 1] = pattern + return subscribe(self, pattern, func) +end + +-- 发布 +function mq:emit (pattern, data) + if type(pattern) ~= 'string' or pattern == '' then + return nil, "推送消息失败: 错误的pattern类型" + end + if type(data) ~= 'string' or data == '' then + return nil, "推送消息失败: 错误的data类型" + end + if not self.emiter then + self.emiter = _login(self) + end + return publish(self, pattern, data) +end + +-- 启动MQ服务 +function mq:start() + return cf.wait() +end + +-- 关闭MQ服务 +function mq:close () + self.closed = true + if self.emiter then + self.emiter:close() + self.emiter = nil + end + if self.subsribes then + for _, sub in ipairs(self.subsribes) do + sub:close() + end + self.subsribes = nil + end +end + +return mq diff --git a/lualib/MQ/redis.lua b/lualib/MQ/redis.lua new file mode 100644 index 00000000..b25f2949 --- /dev/null +++ b/lualib/MQ/redis.lua @@ -0,0 +1,126 @@ +local class = require "class" +local redis = require "protocol.redis" + +local logging = require "logging" +local Log = logging:new{dump = true, path = 'MQ-redis'} + +local cf = require "cf" +local cf_self = cf.self +local cf_fork = cf.fork +local cf_sleep = cf.sleep +local cf_wait = cf.wait +local cf_wakeup = cf.wakeup + +local ipairs = ipairs +local type = type +local insert = table.insert + +local mq = class('redis-mq') + +function mq:ctor (opt) + self.host = opt.host + self.port = opt.port + self.auth = opt.auth + self.db = opt.db + self.patterns = {} + self.subsribes = {} + self.queue = {} +end + +local function _login (opt) + local times = 1 + while 1 do + local mq = redis:new { + host = opt.host, + port = opt.port, + auth = opt.auth, + db = opt.db, + } + local ok, err = mq:connect() + if ok then + return mq + end + Log:WARN("第"..times.."次连接MQ(redis)失败:"..(err or "未知错误.")) + times = times + 1 + mq:close() + cf_sleep(3) + end +end + +-- 订阅事件循环 +local function subscribe (self, pattern, func) + local mq = _login(self) + self.subsribes[#self.subsribes+1] = mq + return mq:subscribe(pattern, func) +end + +-- 发布事件循环 +local function publish (self, pattern, data) + if #self.queue == 0 then + self.queue[#self.queue + 1] = {pattern = pattern, data = data, co = cf_self()} + cf_fork(function (...) + for _, msg in ipairs(self.queue) do + if not self.closed and self.emiter then + cf_wakeup(msg.co, self.emiter:publish(msg.pattern, msg.data)) + end + end + self.queue = {} + end) + return cf_wait() + end + insert(self.queue, {pattern = pattern, data = data, co = cf_self()}) + return cf_wait() +end + +-- 订阅 +function mq:on (pattern, func) + if type(pattern) ~= 'string' or pattern == '' then + return nil, "订阅消息失败: 错误的pattern类型" + end + if type(func) ~= 'function' then + return nil, "订阅消息失败: 错误的func类型" + end + for _, patt in ipairs(self.patterns) do + if patt == pattern then + return nil, '禁止重复订阅相同的频道' + end + end + self.patterns[#self.patterns + 1] = pattern + return subscribe(self, pattern, func) +end + +-- 发布 +function mq:emit (pattern, data) + if type(pattern) ~= 'string' or pattern == '' then + return nil, "推送消息失败: 错误的pattern类型" + end + if type(data) ~= 'string' or data == '' then + return nil, "推送消息失败: 错误的data类型" + end + if not self.emiter then + self.emiter = _login(self) + end + return publish(self, pattern, data) +end + +-- 启动MQ服务 +function mq:start() + return cf.wait() +end + +-- 关闭MQ服务 +function mq:close () + self.closed = true + if self.emiter then + self.emiter:close() + self.emiter = nil + end + if self.subsribes then + for _, sub in ipairs(self.subsribes) do + sub:close() + end + self.subsribes = nil + end +end + +return mq diff --git a/lualib/MQ/stomp.lua b/lualib/MQ/stomp.lua new file mode 100644 index 00000000..fae846e7 --- /dev/null +++ b/lualib/MQ/stomp.lua @@ -0,0 +1,126 @@ +local class = require "class" +local stomp = require "protocol.stomp" + +local logging = require "logging" +local Log = logging:new{dump = true, path = 'MQ-stomp'} + +local cf = require "cf" +local cf_self = cf.self +local cf_fork = cf.fork +local cf_sleep = cf.sleep +local cf_wait = cf.wait +local cf_wakeup = cf.wakeup + +local ipairs = ipairs +local type = type +local insert = table.insert + +local mq = class('stomp-mq') + +function mq:ctor (opt) + self.host = opt.host + self.port = opt.port + self.auth = opt.auth + self.vhost = opt.vhost + self.patterns = {} + self.subsribes = {} + self.queue = {} +end + +local function _login (opt) + local times = 1 + while 1 do + local mq = stomp:new { + host = opt.host, + port = opt.port, + auth = opt.auth, + vhost = opt.vhost, + } + local ok, err = mq:connect() + if ok then + return mq + end + Log:WARN("第"..times.."次连接MQ(stomp)失败:"..(err or "未知错误.")) + times = times + 1 + mq:close() + cf_sleep(3) + end +end + +-- 订阅事件循环 +local function subscribe (self, pattern, func) + local mq = _login(self) + self.subsribes[#self.subsribes+1] = mq + return mq:subscribe(pattern, func) +end + +-- 发布事件循环 +local function publish (self, pattern, data) + if #self.queue == 0 then + self.queue[#self.queue + 1] = {pattern = pattern, data = data, co = cf_self()} + cf_fork(function (...) + for _, msg in ipairs(self.queue) do + if not self.closed and self.emiter then + cf_wakeup(msg.co, self.emiter:publish(msg.pattern, msg.data)) + end + end + self.queue = {} + end) + return cf_wait() + end + insert(self.queue, {pattern = pattern, data = data, co = cf_self()}) + return cf_wait() +end + +-- 订阅 +function mq:on (pattern, func) + if type(pattern) ~= 'string' or pattern == '' then + return nil, "订阅消息失败: 错误的pattern类型" + end + if type(func) ~= 'function' then + return nil, "订阅消息失败: 错误的func类型" + end + for _, patt in ipairs(self.patterns) do + if patt == pattern then + return nil, '禁止重复订阅相同的频道' + end + end + self.patterns[#self.patterns + 1] = pattern + return subscribe(self, pattern, func) +end + +-- 发布 +function mq:emit (pattern, data) + if type(pattern) ~= 'string' or pattern == '' then + return nil, "推送消息失败: 错误的pattern类型" + end + if type(data) ~= 'string' or data == '' then + return nil, "推送消息失败: 错误的data类型" + end + if not self.emiter then + self.emiter = _login(self) + end + return publish(self, pattern, data) +end + +-- 启动MQ服务 +function mq:start() + return cf.wait() +end + +-- 关闭MQ服务 +function mq:close () + self.closed = true + if self.emiter then + self.emiter:close() + self.emiter = nil + end + if self.subsribes then + for _, sub in ipairs(self.subsribes) do + sub:close() + end + self.subsribes = nil + end +end + +return mq diff --git a/lualib/protocol/stomp/init.lua b/lualib/protocol/stomp/init.lua new file mode 100644 index 00000000..2fbc30d3 --- /dev/null +++ b/lualib/protocol/stomp/init.lua @@ -0,0 +1,140 @@ +local class = require "class" +local tcp = require "internal.TCP" + +local cf = require "cf" +local cf_fork = cf.fork +local cf_wait = cf.wait +local cf_self = cf.self +local cf_wakeup = cf.wakeup + +local logging = require "logging" +local Log = logging:new{ dump = true, path = "protocol-stomp"} + +local protocol = require "protocol.stomp.protocol" +local protocol_send = protocol.send +local protocol_ack = protocol.ack +local protocol_subscribe = protocol.subscribe +local protocol_unsubscribe = protocol.unsubscribe +local protocol_disconnect = protocol.disconnect +local connect_and_login = protocol.connect + +local type = type +local ipairs = ipairs +local fmt = string.format +local random = math.random + +local stomp = class("stomp") + +function stomp:ctor (opt) + self.id = opt.id or fmt('luastomp-cf-v1-0x%08X', random(0xFFFFFFFF)) + self.ssl = opt.ssl + self.sock = tcp:new() + self.host = opt.host + self.port = opt.port + self.vhost = opt.vhost or "" + self.username = opt.username + self.password = opt.password +end + +-- 连接 +function stomp:connect () + local ok, data = connect_and_login(self, { + ['id'] = self.id, + ['vhost'] = self.vhost, + ['login'] = self.username, + ['username'] = self.username, + ['passcode'] = self.password, + ['password'] = self.password, + }) + if not ok then + return ok, data + end + self.session = data.session + self.state = true + return true +end + +function stomp:send (...) + return self:publish(...) +end + +function stomp:publish (topic, data) + if not self.state then + return nil, "stomp未连接" + end + if type(topic) ~= 'string' or type(data) ~= 'string' then + return nil, "错误的topic或data" + end + return protocol_send(self, { + topic = topic, + data = data + }) +end + +function stomp:subscribe (topic, func) + if not self.state then + return nil, "stomp未连接" + end + if type(topic) ~= 'string' then + return nil, '错误的stopic订阅参数' + end + local co = cf_self() + cf_fork(function () + local ok, pack = protocol_subscribe(self, topic, self.topic) + if not ok then + self.state = nil + return cf_wakeup(co, ok, pack) + end + cf_wakeup(co, ok, pack) + while 1 do + local ok, msg = protocol_subscribe(self, topic, self.topic) + if not ok then + local ok, err = pcall(func, msg) + if not ok then + Log:ERROR(err) + end + return + end + local ok, err = pcall(func, { + len = msg['content-length'], + id = msg['message-id'], + session = msg['session'], + payload = msg['body'], + pattern = msg['destination'], + }) + if not ok then + Log:ERROR(err) + end + end + end) + return cf_wait() +end + +function stomp:unsubscribe (...) + if not self.state then + return nil, "stomp未连接" + end + if not self.topic then + return nil, '没有需要取消订阅的topic' + end + return protocol_unsubscribe(self, self.topic) +end + +function stomp:disconnect () + if self.state then + self.state = nil + protocol_disconnect(self) + self:close() + end +end + +-- 关闭 +function stomp:close () + if self.sock then + self.state = nil + self.sock:close() + self.sock = nil + end +end + +return stomp diff --git a/lualib/protocol/stomp/protocol.lua b/lualib/protocol/stomp/protocol.lua new file mode 100644 index 00000000..25c92233 --- /dev/null +++ b/lualib/protocol/stomp/protocol.lua @@ -0,0 +1,268 @@ +local system = require "system" + +local is_array_member = system.is_array_member + +local type = type +local pairs = pairs +local ipairs = ipairs +local tonumber = tonumber +local toint = math.tointeger + +local find = string.find +local split = string.sub +local splite = string.gmatch + +local concat = table.concat + +local LF = '\x0a' +local LF2 = '\x0a\x0a' +local NULL = '\x00' +local CRLF = "\x0d\x0a" +local CRLF2 = "\x0d\x0a\x0d\x0a" +local NULL_LF = "\x00\x0a" + +-- 支持的版本列表 +local versions = { 1.2, 1.1, 1.0 } + + +local CMDS = { + ["CONNECTED"] = true, + ["SEND"] = true, + ["MESSAGE"] = true, + ["SUBSCRIBE"] = true, + ["UNSUBSCRIBE"] = true, + ["ABORT"] = true, + ["DISCONNECT"] = true, + ["ERROR"] = true, +} + + +-- 支持版本 +local function version_support (ver) + if type(ver) == 'string' or type(ver) == 'number' then + ver = tonumber(ver) + if not ver then + return nil, "1. 不支持的版本" + end + for _, version in ipairs(versions) do + if ver == version then + return true + end + end + end + if type(ver) == 'table' then + for _, v in ipairs(ver) do + if is_array_member(versions, tonumber(v)) then + return true + end + end + end + return false, '2. 不支持的版本.' +end + +local function sock_send (sock, data) + if sock.ssl then + return sock:ssl_send(data) + end + return sock:send(data) +end + +local function sock_read (sock, byte) + if sock.ssl then + return sock:ssl_recv(byte) + end + return sock:recv(byte) +end + +local function sock_connect (sock, ssl, host, port) + if ssl then + return sock:ssl_connect(host, port) + end + return sock:connect(host, port) +end + +local function parser_cmd (data) + local _, Pos = find(data, '\x0a') + if not Pos then + return nil, "Command Parse Error." + end + local cmd = split(data, 1, Pos - 1) + local exists = CMDS[cmd] + if not exists then + return nil, "不受支持的命令:"..(data or cmd or data) + end + return cmd, Pos + 1 +end + +local function parser_header (data) + local HEADERS = {} + for key, value in splite(data, "([^:]+):([^\x0a]+)[\x0d]?\x0a") do + if key:lower() == 'version' then + local tab = {} + for ver in splite(value, '([^,]+)') do + tab[#tab+1] = ver + end + value = tab + end + if key:lower() == 'heart-beat' then + local tab = {} + for num in splite(value, '([^,]+)') do + tab[#tab+1] = num + end + value = tab + end + HEADERS[key] = value + end + return HEADERS +end + +local function build_frame (CMD, opt, body) + local req = { CMD, "version:"..concat(versions, ',') } + for key, value in pairs(opt) do + req[#req+1] = key..":"..value + end + if body then + req[#req+1] = "content-type:text/plain;charset=utf-8" + req[#req+1] = "content-length:"..#body + end + return concat(req, CRLF)..CRLF2..(body or '')..NULL_LF +end + +function read_response (sock) + local buffers = {} + while 1 do + local data, len = sock_read(sock, 1) + if not data then + return nil, "服务端断开了连接" + end + buffers[#buffers + 1] = data + local response = concat(buffers) + if find(response, NULL_LF) then + local cmd, pos = parser_cmd(response) + if not cmd then + return cmd, pos + end + local headers, pos = parser_header(split(response, pos, find(response, LF, -2) - 1)) + local ver = headers['version'] or headers['Version'] + if ver then + local ok, err = version_support(ver) + if not ok then + return nil, err + end + end + local body_len = toint(headers['content-length'] or headers['Content-length']) + if body_len then + headers['body'] = split(response, #response - body_len - 1, #response - 2) + end + headers["COMMAND"] = cmd + if cmd == 'ERROR' then + return nil, (headers["message"] or headers["Message"] or cmd)..','..(headers['body'] or "ERROR") + end + return true, headers + end + if #response > 10240 then + return nil, '错误的server回应' + end + end +end + + +local protocol = {} + + +-- 连接 +function protocol.connect (self, opt) + local ok, err = sock_connect(self.sock, self.ssl, self.host, self.port) + if not ok then + self.state = nil + return nil, "连接到stomp服务器失败" + end + local ok = sock_send(self.sock, build_frame("CONNECT", opt)) + if not ok then + self.state = nil + return nil, '发送CONNECT失败.' + end + return read_response(self.sock) +end + +-- 发布消息 +function protocol.send (self, opt) + local ok = sock_send(self.sock, build_frame("SEND", { + ['id'] = self.id, + ['session'] = self.session, + ['destination'] = self.vhost..opt.topic, + }, opt.data)) + if not ok then + self.state = nil + return nil, 'SEND数据失败.' + end + return true +end + +-- 订阅消息 +function protocol.subscribe (self, topic, already) + if not already then + local ok = sock_send(self.sock, build_frame("SUBSCRIBE", { + ['id'] = self.id, + ['session'] = self.session, + ['destination'] = self.vhost..topic, + })) + if not ok then + self.state = nil + return nil, 'SUBSCRIBE 失败.' + end + self.topic = topic + return true + end + local ok, pack = read_response(self.sock) + if not ok then + self.state = nil + return nil, pack + end + self.topic = topic + return true, pack +end + +-- 取消订阅 +function protocol.unsubscribe (self, topic) + local ok = sock_send(self.sock, build_frame("UNSUBSCRIBE", { + ['id'] = self.id, + ['session'] = self.session, + ['destination'] = self.vhost..topic, + })) + self.topic = nil + if not ok then + self.state = nil + return nil, 'UNSUBSCRIBE 失败.' + end + return read_response(self.sock) +end + +-- 回应 +function protocol.ack (self, opt) + local ok = sock_send(self.sock, build_frame("ACK", { + ['id'] = self.id, + ['session'] = self.session, + ['message-id'] = opt['message-id'], + ['transaction'] = opt['transaction'], + })) + if not ok then + self.state = nil + return nil, pack + end + return true +end + +-- 断开连接 +function protocol.disconnect (self) + self.state = nil + local ok = sock_send(self.sock, build_frame("DISCONNECT", { + ['receipt'] = 1, + })) + if not ok then + return nil, pack + end + return true +end + +return protocol From ce4d299b9f8e2e23d45fedceebbc711b60acc9c1 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 9 Jun 2019 18:22:22 +0800 Subject: [PATCH 140/956] =?UTF-8?q?=E4=BC=98=E5=8C=96logging?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/logging/init.lua | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua index 7c406212..9eda91fe 100644 --- a/lualib/logging/init.lua +++ b/lualib/logging/init.lua @@ -1,8 +1,13 @@ -- logging 核心配置 local class = require "class" + +local os_date = require("sys").date + local system = require "system" local now = system.now + local type = type +local select = select local assert = assert local pairs = pairs local tostring = tostring @@ -10,19 +15,17 @@ local getmetatable = getmetatable local modf = math.modf local debug_getinfo = debug.getinfo -local os_date = require("sys").date local io_open = io.open local io_write = io.write local io_type = io.type -local format = string.format +local fmt = string.format local concat = table.concat -local modf = math.modf -- 格式化时间: [年-月-日 时:分:秒,毫秒] local function fmt_Y_m_d_H_M_S() local ts, f = modf(now()) - return concat({'[', os_date('%Y-%m-%d %H:%M:%S'), ',', format("%0.3f", f * 1e3), ']'}) + return concat({'[', os_date('%Y-%m-%d %H:%M:%S'), ',', fmt("%0.3f", f * 1e3), ']'}) end -- 格式化时间: [年-月-日 时:分:秒] From 14c2359a02eec40ffc862e619d8070d375acf180 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 9 Jun 2019 18:51:44 +0800 Subject: [PATCH 141/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0httpd=E7=9A=84?= =?UTF-8?q?=E4=B8=80=E4=BA=9B=E5=AE=89=E5=85=A8=E8=BF=87=E6=BB=A4,=20?= =?UTF-8?q?=E9=98=B2=E6=AD=A2=E6=81=B6=E6=84=8F=E8=B7=AF=E5=BE=84=E7=9A=84?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E8=AF=BB=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/protocol.lua | 8 ++++---- lualib/httpd/Router.lua | 3 +++ lualib/protocol/http.lua | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua index 0d716899..47c2251e 100644 --- a/lualib/httpc/protocol.lua +++ b/lualib/httpc/protocol.lua @@ -147,12 +147,12 @@ local function httpc_response(sock, SSL) if not CODE or not HEADER then return nil, SSL.." can't resolvable protocol." end - local Content_Length = toint(HEADER['Content-Length'] or HEADER['content-length']) - local chunked = HEADER['Transfer-Encoding'] or HEADER['Transfer-encoding'] + local Content_Length = toint(HEADER['Content-Length'] or HEADER['Content-length'] or HEADER['content-length']) + local chunked = HEADER['Transfer-Encoding'] or HEADER['Transfer-encoding'] or HEADER['transfer-encoding'] if not chunked and not Content_Length then return nil, "不支持的请求体解析方式:"..( - (HEADER['Content-Length'] or HEADER['content-length']) or - (HEADER['Transfer-Encoding'] or HEADER['Transfer-encoding']) or + (HEADER['Content-Length'] or HEADER['Content-length'] or HEADER['content-length']) or + (HEADER['Transfer-Encoding'] or HEADER['Transfer-encoding'] or HEADER['transfer-encoding']) or "未知的解析方式") end if Content_Length then diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index 3465ad70..4f3baa8e 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -81,6 +81,9 @@ end -- 查找路由 function Router.find(path) + if find(path, '^/%.%./') or find(path, '/%./%.%.') or find(path, '/%.%./%.%.') then + return -- 过滤恶意文件读取. + end return find_route(path) end diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index f4a6365d..125fbae9 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -336,6 +336,7 @@ local function ERROR_RESPONSE(http, code, path, ip, forword, method, speed) 'Origin: *', 'Allow: GET, POST, PUT, HEAD, OPTIONS', 'Connection: close', + 'Content-length: 0', 'server: ' .. (http.__server or SERVER), }, CRLF), CRLF2}) end From 8f64b876f350682b3a7c52329864b5ba8ba97bb9 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 9 Jun 2019 20:02:19 +0800 Subject: [PATCH 142/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dcf.timeout=E7=9A=84?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/Timer.lua | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lualib/internal/Timer.lua b/lualib/internal/Timer.lua index ad8b567a..fcd685d4 100644 --- a/lualib/internal/Timer.lua +++ b/lualib/internal/Timer.lua @@ -66,10 +66,7 @@ function Timer.timeout(timeout, cb) return end Timer_release(t) - local ok, err = pcall(cb) - if not ok then - Log:ERROR('timeout error:', err) - end + co_spwan(cb) if timer.STOP then return end From 1266f0897d5fed06b6345c75039b0b86e13886b8 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 10 Jun 2019 03:28:36 +0800 Subject: [PATCH 143/956] =?UTF-8?q?=E5=AE=8C=E5=96=84httpd=E9=9D=99?= =?UTF-8?q?=E6=80=81=E6=96=87=E4=BB=B6=E8=AF=BB=E5=8F=96=E5=88=A4=E6=96=AD?= =?UTF-8?q?,=20=E8=A7=A3=E5=86=B3=E9=9D=99=E6=80=81=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E8=B6=85=E5=87=BA=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Router.lua | 55 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index 4f3baa8e..71862c3f 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -1,14 +1,18 @@ local log = require "logging" local Log = log:new({dump = true, path = 'httpd-Router'}) +local new_tab = require("sys").new_tab + local math = math local string = string local split = string.sub local find = string.find local match = string.match local splite = string.gmatch +local spliter = string.gsub local type = type +local next = next local assert = assert local ipairs = ipairs local tonumber = tonumber @@ -36,11 +40,44 @@ local typ = { -- 分割路径后进行hex, 得到key后一次查表即可完成 local function hex_route(route) - local tab = {} + local tab = new_tab(32, 0) for r in splite(route, '/([^/%?]+)') do tab[#tab + 1] = r end - return concat(tab) + return tab +end + +-- 检查是路径回退是否超出静态文件根目录 +local function check_path_deep (paths) + -- 判断是否/.. + local head = paths[1] + if head == '..' then + return + end + -- 判断是否/./.. + local second = paths[2] + if second == '..' and head == '.' then + return + end + -- 结尾是特殊文件[夹]直接返回. + local tail = paths[#paths] + if tail == '.' or tail == '..' then + return + end + local deep = 1 + for index, path in ipairs(paths) do + if path == '..' then + deep = deep - 1 + elseif path == '.' then + deep = deep + 0 + else + deep = deep + 1 + end + if deep <= 0 then + return true + end + end + return false end local function registery_static (prefix, route_type) @@ -54,11 +91,12 @@ end local load_file local function registery_router (route, class, route_type) - routes[hex_route(route)] = {class = class, type = route_type} + routes[concat(hex_route(route))] = {class = class, type = route_type} end local function find_route (path) - local hex = hex_route(split(path, 1, (find(path, '?') or 0) - 1)) + local tab = hex_route(split(path, 1, (find(path, '?') or 0) - 1)) + local hex = concat(tab) local t = routes[hex] if t then return t.class, t.type @@ -67,6 +105,10 @@ local function find_route (path) if not prefix and not type then return end + -- 凡是找到'../'并且检查路径回退已经超出静态文件根目录返回404 + if find(path, '%.%./') and check_path_deep(tab) then + return + end load_file = load_file or function (path) local f, error = io_open(prefix..path, 'rb') if not f then @@ -81,8 +123,9 @@ end -- 查找路由 function Router.find(path) - if find(path, '^/%.%./') or find(path, '/%./%.%.') or find(path, '/%.%./%.%.') then - return -- 过滤恶意文件读取. + -- 凡是不以'/'开头的path都返回404 + if not find(path, '^(/)') then + return end return find_route(path) end From 78d72d030d431ef93253f0afbc4c32dc12141b97 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 10 Jun 2019 03:31:07 +0800 Subject: [PATCH 144/956] =?UTF-8?q?=E5=AE=8C=E5=96=84httpd=E9=9D=99?= =?UTF-8?q?=E6=80=81=E6=96=87=E4=BB=B6=E8=AF=BB=E5=8F=96=E5=88=A4=E6=96=AD?= =?UTF-8?q?,=20=E8=A7=A3=E5=86=B3=E9=9D=99=E6=80=81=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E8=B6=85=E5=87=BA=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Router.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index 71862c3f..1f3cbab1 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -52,17 +52,17 @@ local function check_path_deep (paths) -- 判断是否/.. local head = paths[1] if head == '..' then - return + return true end -- 判断是否/./.. local second = paths[2] if second == '..' and head == '.' then - return + return true end -- 结尾是特殊文件[夹]直接返回. local tail = paths[#paths] if tail == '.' or tail == '..' then - return + return true end local deep = 1 for index, path in ipairs(paths) do From af479a859dbe3a0ef00d196bb0fb2c1dced77bbd Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 10 Jun 2019 03:43:48 +0800 Subject: [PATCH 145/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E9=9D=9EGET=E6=96=B9=E6=B3=95=E4=BC=9A=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E9=9D=99=E6=80=81=E6=96=87=E4=BB=B6=E7=9A=84BUG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Router.lua | 10 +++++++--- lualib/protocol/http.lua | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index 1f3cbab1..d42ce920 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -94,7 +94,7 @@ local function registery_router (route, class, route_type) routes[concat(hex_route(route))] = {class = class, type = route_type} end -local function find_route (path) +local function find_route (method, path) local tab = hex_route(split(path, 1, (find(path, '?') or 0) - 1)) local hex = concat(tab) local t = routes[hex] @@ -105,6 +105,10 @@ local function find_route (path) if not prefix and not type then return end + -- 非GET方法不查找静态文件 + if method ~= 'GET' then + return + end -- 凡是找到'../'并且检查路径回退已经超出静态文件根目录返回404 if find(path, '%.%./') and check_path_deep(tab) then return @@ -122,12 +126,12 @@ local function find_route (path) end -- 查找路由 -function Router.find(path) +function Router.find(method, path) -- 凡是不以'/'开头的path都返回404 if not find(path, '^(/)') then return end - return find_route(path) + return find_route(method, path) end -- 注册静态文件查找路径 diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index 125fbae9..60371003 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -448,7 +448,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) return sock:close() end -- 这里根据PATH先查找路由, 如果没有直接返回404. - local cls, typ = ROUTE_FIND(PATH) + local cls, typ = ROUTE_FIND(METHOD, PATH) if not cls or not typ then sock:send(ERROR_RESPONSE(http, 404, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) return sock:close() From 12e3b8c4a3310507ce4908fd0bd5428c81529cd0 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 10 Jun 2019 03:56:21 +0800 Subject: [PATCH 146/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=94=AF=E6=8C=81htt?= =?UTF-8?q?p=20urlencode=20args=E8=87=AA=E5=8A=A8=E8=BD=AC=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Form.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lualib/httpd/Form.lua b/lualib/httpd/Form.lua index 0c42f980..d167b668 100644 --- a/lualib/httpd/Form.lua +++ b/lualib/httpd/Form.lua @@ -1,3 +1,7 @@ +local url = require "url" +local urlencode = url.encode +local urldecode = url.decode + local type = type local string = string local table = table @@ -17,7 +21,7 @@ function form.urlencode(body) end local ARGS = {} for key, value in splite(body, "([^%?&]-)=([^%?&]+)") do - ARGS[key] = value + ARGS[urldecode(key)] = urldecode(value) end return ARGS end @@ -54,4 +58,4 @@ function form.multipart(body, BOUNDARY) end -return form \ No newline at end of file +return form From c8932c3d19c21967223990e0668e888280cb7a6e Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 10 Jun 2019 04:23:23 +0800 Subject: [PATCH 147/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dsys.date=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lsys.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/luaclib/src/lsys.c b/luaclib/src/lsys.c index f9cae344..1c2853ea 100644 --- a/luaclib/src/lsys.c +++ b/luaclib/src/lsys.c @@ -36,9 +36,9 @@ ldate(lua_State *L){ const char *fmt = lua_tostring(L, 1); if (!fmt) return luaL_error(L, "Date: 错误的格式化方法"); time_t timestamp = lua_tointeger(L, 2); - char fmttime[64]; - time_t t = time(×tamp); - strftime(fmttime, 64, fmt, localtime(&t)); + if (0 >= timestamp) timestamp = time(NULL); + char fmttime[256]; + strftime(fmttime, 256, fmt, localtime(×tamp)); lua_pushstring(L, fmttime); return 1; } From 677bc0ccf4eea44b4b82df81cae82c686907861c Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 10 Jun 2019 12:54:11 +0800 Subject: [PATCH 148/956] =?UTF-8?q?=E4=BC=98=E5=8C=96redis.lua=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/redis.lua | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lualib/protocol/redis.lua b/lualib/protocol/redis.lua index 78630035..2e2df8f6 100644 --- a/lualib/protocol/redis.lua +++ b/lualib/protocol/redis.lua @@ -3,12 +3,19 @@ local class = require "class" local Co = require "internal.Co" local tcp = require "internal.TCP" -local concat = table.concat -local unpack = table.unpack local Log = log:new({ dump = true, path = 'protocol-redis' }) local co_spwan = Co.spwan +local type = type +local pcall = pcall +local ipairs = ipairs +local tonumber = tonumber +local tostring = tostring + +local concat = table.concat +local unpack = table.unpack + local sub = string.sub local string = string local match = string.match @@ -78,7 +85,7 @@ end -- 格式化命令为redis protocol local function CMD(...) local tab = {...} - local lines = { "*"..#tab} + local lines = { "*"..#tab } for index = 1, #tab do lines[#lines+1] = "$"..#tostring(tab[index]) lines[#lines+1] = tab[index] From d22e66c06f8695804452e97fea17ea27d0521d82 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 10 Jun 2019 12:55:37 +0800 Subject: [PATCH 149/956] =?UTF-8?q?=E5=AE=8C=E5=96=84test=5Fcache.lua?= =?UTF-8?q?=E7=A4=BA=E4=BE=8B=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_Cache.lua | 70 +++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/script/test_Cache.lua b/script/test_Cache.lua index 690f7daf..1056b947 100644 --- a/script/test_Cache.lua +++ b/script/test_Cache.lua @@ -1,7 +1,7 @@ -- 测试redis local Cache = require "Cache" local cf = require "cf" -require "utils" +local Log = require("logging"):new() local opt = { host = "localhost", @@ -19,115 +19,115 @@ cf.fork(function ( ... ) end -- 测试 GET/SET/DEL 命令示例 local ok, ret = Cache:set("test", 1) - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:get("test") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:del("test") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) -- 测试哈希表命令示例 local ok, ret = Cache:hmset("website", "google", "www.google.com", "baidu", "www.baidu.com") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:hlen("website") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:hkeys("website") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:hgetall("website") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:hmget("website", "google", "baidu") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:hdel("website", "google", "baidu") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) -- 测试列表命令示例 local ok, ret = Cache:lpush("language", "lua", "python", "C", "C++") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:rpush("language", "golang", "java", "ruby", "javascript") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:lrange("language", 0, -1) - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:ltrim("language" , -1, 0) - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:llen("language") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) -- 测试有序集合命令示例 local ok, ret = Cache:smembers("bbs") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:sadd("bbs", "discuz.cn", "group.google.com", "oschina.net", "csdn.net") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:sismember("bbs", "oschina.net") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:srem("bbs", "discuz.cn", "group.google.com", "oschina.net", "csdn.net") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:sismember("bbs", "oschina.net") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:sadd("book1", "宝宝的C++", "宝宝的HTML", "宝宝的CSS", "C程序设计") local ok, ret = Cache:sadd("book2", "宝宝的C++", "宝宝的HTML", "宝宝的CSS", "C++从入门到放弃") local ok, ret = Cache:sadd("book3", "宝宝的C++", "宝宝的HTML", "宝宝的CSS", "MySQL从入门到删库跑路") local ok, ret = Cache:sdiff("book1", "book2", "book3") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:del("book1", "book2", "book3") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) -- 测试有序集合命令示例 local ok, ret = Cache:zadd("scores", 10, "admin", 20, "Candy", 30, "QQ", 40, "Guest") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:zrange("scores", 0, -1) -- local ok, ret = Cache:zrange("scores", 0, -1, "WITHSCORES") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:zcount("scores", 10, 100) - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:zscore("scores", "QQ") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:zrank("scores", "Candy") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:zrem("scores", "admin", "Candy", "QQ", "Guest") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:del("scores") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) -- 脚本支持 local ok, ret = Cache:script_load("return 10086") - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local sha = ret local ok, ret = Cache:script_exists(sha) - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:evalsha(sha, 0) - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) local ok, ret = Cache:script_flush() - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) -- 其它一些特殊方法支持 -- type, move, rename, keys, randomkey等等 - print(Cache:count()) + Log:DEBUG(Cache:count()) -- 管道命令支持 local ok, ret = Cache:pipeline { @@ -135,5 +135,5 @@ cf.fork(function ( ... ) {"HGET", "USER_INFO", "email"}, {"HGET", "USER_INFO", "phone"}, } - print(ok); var_dump(ret) + Log:DEBUG(ok, ret) end) From c25e62a54a1d2dd3a3fb594f2bff14f1d77e461d Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 10 Jun 2019 13:15:49 +0800 Subject: [PATCH 150/956] =?UTF-8?q?=E5=B0=86=E6=97=A5=E5=BF=97=E6=8E=A7?= =?UTF-8?q?=E5=88=B6=E8=BD=AC=E7=A7=BB=E5=88=B0logging=E5=86=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/init.lua | 12 +++--------- lualib/internal/Co.lua | 9 ++------- lualib/internal/Timer.lua | 9 +++------ lualib/logging/init.lua | 9 +++++++++ 4 files changed, 17 insertions(+), 22 deletions(-) diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua index 3264c6e0..cbfe4680 100644 --- a/lualib/httpd/init.lua +++ b/lualib/httpd/init.lua @@ -11,12 +11,11 @@ local ipairs = ipairs local fmt = string.format local match = string.match -local io_open = io.open local io_write = io.write -local io_flush = io.flush -local os_date = os.date +local os_date = require("sys").date local toint = math.tointeger + -- 请求解析 local EVENT_DISPATCH = HTTP.EVENT_DISPATCH @@ -161,13 +160,8 @@ end -- 正确的运行方式 function httpd:run() - local output = io.output() - if io.type(output) == 'file' then + if io.type(io.output()) == 'file' then self.output = true - output:setvbuf("full", 1 << 20) - cf.at(0.2, function () - return io_flush() -- 定期刷新缓冲, 减少日志缓冲频繁导致的性能问题 - end) end return cf.wait() end diff --git a/lualib/internal/Co.lua b/lualib/internal/Co.lua index c8a9a136..27fa7442 100644 --- a/lualib/internal/Co.lua +++ b/lualib/internal/Co.lua @@ -1,8 +1,5 @@ -local log = require "logging" local task = require "task" -local Log = log:new({ dump = true, path = 'internal-Co' }) - local type = type local assert = assert local error = error @@ -57,7 +54,7 @@ local function f() while 1 do local ok, msg = pcall(co_wait()) if not ok then - Log:ERROR(msg) + print(msg) end local co, main = co_self() if not main then @@ -100,10 +97,8 @@ end -- 唤醒 function Co.wakeup(co, ...) assert(type(co) == 'thread', "试图传递一个非协程的类型的参数到wakeup内部.") + assert(co ~= co_self(), "不能唤醒当前正在执行的协程") local status = co_status(co) - if co == co_self() then - return Log:ERROR("不能唤醒当前正在执行的协程") - end if main_co == co and status == "suspended" then return task_start(main_task, main_co, ...) end diff --git a/lualib/internal/Timer.lua b/lualib/internal/Timer.lua index fcd685d4..5fb7bd3c 100644 --- a/lualib/internal/Timer.lua +++ b/lualib/internal/Timer.lua @@ -1,10 +1,7 @@ local co = require "internal.Co" local ti = require "timer" -local log = require "logging" -local Log = log:new({ dump = true, path = 'internal-Timer' }) local type = type -local pcall = pcall local ti_new = ti.new local ti_start = ti.start local ti_stop = ti.stop @@ -49,7 +46,7 @@ function Timer.timeout(timeout, cb) end local t = Timer_new() if not t then - return Log:ERROR("timeout error: Create timer class error! memory maybe not enough...") + return end local timer = {STOP = false} timer.stop = function (...) @@ -89,7 +86,7 @@ function Timer.at(repeats, cb) end local t = Timer_new() if not t then - return Log:ERROR("timeout error: Create timer class error! memory maybe not enough...") + return end local timer = { STOP = false } timer.stop = function (...) @@ -126,7 +123,7 @@ function Timer.sleep(repeats) end local t = Timer_new() if not t then - return Log:ERROR("timeout error: Create timer class error! memory maybe not enough...") + return end local timer = {} timer.current_co = co_self() diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua index 9eda91fe..4a51dc67 100644 --- a/lualib/logging/init.lua +++ b/lualib/logging/init.lua @@ -17,10 +17,19 @@ local modf = math.modf local debug_getinfo = debug.getinfo local io_open = io.open local io_write = io.write +local io_flush = io.flush local io_type = io.type local fmt = string.format local concat = table.concat +local cf = require "cf" + +if io_type(io.output()) == 'file' then + io.output():setvbuf("full", 1 << 20) + cf.at(0.2, function () + return io_flush() -- 定期刷新缓冲, 减少日志缓冲频繁导致的性能问题 + end) +end -- 格式化时间: [年-月-日 时:分:秒,毫秒] local function fmt_Y_m_d_H_M_S() From 667948151597b2f37fe3729374d59dab665b3128 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 10 Jun 2019 13:40:08 +0800 Subject: [PATCH 151/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dxml=E7=9A=84=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/xml2lua/init.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lualib/xml2lua/init.lua b/lualib/xml2lua/init.lua index 95fc44bc..0126c212 100644 --- a/lualib/xml2lua/init.lua +++ b/lualib/xml2lua/init.lua @@ -11,11 +11,11 @@ local xml = {} function xml.parser(xml_data) local cls = xml2lua.parser(xml2lua_handler:new()) -- cls:parse(xml_data) - local ok, data = pcall(cls.parse, cls,xml_data) + local ok, err = pcall(cls.parse, cls, xml_data) if not ok then - return data + return err end - return data + return cls.handler.root end -- xml文件读取 From 835488efbfa164e0e340ab8320cc723c8ca5d417 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 10 Jun 2019 13:53:15 +0800 Subject: [PATCH 152/956] =?UTF-8?q?yum=20install=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E4=B8=80=E4=B8=AAfile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 55ab9ff1..38269742 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -11,7 +11,7 @@ COPY ./lua-5.3.5.tar.gz /root/download/lua.tar.gz COPY ./libev-4.25.tar.gz /root/download/libev.tar.gz RUN rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 \ - && yum install nc gcc vim autoconf make git readline-devel openssl-devel -y \ + && yum install nc gcc file vim autoconf make git readline-devel openssl-devel -y \ && tar zxvf lua.tar.gz && cd lua* && make linux MYCFLAGS=-fPIC && make install && cd .. \ && tar zxvf libev.tar.gz && cd libev* && ./configure --prefix=/usr/local && make && make install \ && rm -rf /roo/download /var/cache/yum \ From 31c3d47c8f2634bec78b6c1d5815689308ad4710 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 11 Jun 2019 09:54:06 +0800 Subject: [PATCH 153/956] =?UTF-8?q?=E8=A7=A3=E5=86=B3http=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE=E7=9A=84=E4=B8=80=E4=B8=AA=E9=80=82=E9=85=8D=E9=97=AE?= =?UTF-8?q?=E9=A2=98,=20=E5=8F=AF=E4=BB=A5=E5=9C=A8=E5=91=BD=E4=BB=A4?= =?UTF-8?q?=E8=A1=8C=E7=9B=B4=E6=8E=A5=E6=89=8B=E5=86=99=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http.lua | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index 60371003..7c31df06 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -57,6 +57,7 @@ local concat = table.concat local CRLF = '\x0d\x0a' local CRLF2 = '\x0d\x0a\x0d\x0a' +local RE_CRLF2 = '[\x0d]?\x0a[\x0d]?\x0a' local SERVER = 'cf web/0.1' @@ -423,28 +424,28 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) end buffers[#buffers+1] = buf local buffer = concat(buffers) - local CRLF_START, CRLF_END = find(buffer, CRLF2) + local CRLF_START, CRLF_END = find(buffer, RE_CRLF2) if CRLF_START and CRLF_END then local start = now() -- 协议有问题返回400 local METHOD, PATH, VERSION, HEADER = PARSER_HTTP_REQUEST(buffer) if not METHOD or not PATH or not VERSION then - sock:send(ERROR_RESPONSE(http, 400, PATH, ipaddr, METHOD or "GET", now() - start)) + sock:send(ERROR_RESPONSE(http, 400, PATH, HEADER and HEADER['X-Real-IP'] or ipaddr, HEADER and HEADER['X-Forwarded-For'] or ipaddr, METHOD or "GET", now() - start)) return sock:close() end -- 超过自定义最大PATH长度限制 if PATH and #PATH > (max_path_size or 1024) then - sock:send(ERROR_RESPONSE(http, 414, PATH, ipaddr, METHOD, now() - start)) + sock:send(ERROR_RESPONSE(http, 414, PATH, HEADER and HEADER['X-Real-IP'] or ipaddr, HEADER and HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) return sock:close() end -- 没有HEADER返回400 if not HEADER or not next(HEADER) then - sock:send(ERROR_RESPONSE(http, 400, PATH, ipaddr, METHOD, now() - start)) + sock:send(ERROR_RESPONSE(http, 400, PATH, HEADER and HEADER['X-Real-IP'] or ipaddr, HEADER and HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) return sock:close() end -- 超过自定义最大HEADER长度限制 if #buffer - CRLF_START > (max_header_size or 65535) then - sock:send(ERROR_RESPONSE(http, 431, PATH, ipaddr, METHOD, now() - start)) + sock:send(ERROR_RESPONSE(http, 431, PATH, HEADER and HEADER['X-Real-IP'] or ipaddr, HEADER and HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) return sock:close() end -- 这里根据PATH先查找路由, 如果没有直接返回404. From df5034d08feceae0b68c6c8d139929ec30e8b461 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 12 Jun 2019 00:10:57 +0800 Subject: [PATCH 154/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E7=9A=84=E6=AF=AB=E7=A7=92=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/logging/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua index 4a51dc67..b6c74142 100644 --- a/lualib/logging/init.lua +++ b/lualib/logging/init.lua @@ -34,7 +34,7 @@ end -- 格式化时间: [年-月-日 时:分:秒,毫秒] local function fmt_Y_m_d_H_M_S() local ts, f = modf(now()) - return concat({'[', os_date('%Y-%m-%d %H:%M:%S'), ',', fmt("%0.3f", f * 1e3), ']'}) + return concat({'[', os_date('%Y-%m-%d %H:%M:%S'), ',', fmt("%003d", modf(f * 1e3)), ']'}) end -- 格式化时间: [年-月-日 时:分:秒] From 0a6d24cf878c98fa18fe82a0ac6c65763a35d9f3 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 12 Jun 2019 07:52:34 +0800 Subject: [PATCH 155/956] =?UTF-8?q?=E4=BC=98=E5=8C=96websocket=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E7=A8=B3=E5=AE=9A=E6=80=A7=E4=B8=8E=E5=86=99=E5=85=A5?= =?UTF-8?q?=E9=98=9F=E5=88=97=E6=95=88=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/websocket/client.lua | 25 +++---- lualib/protocol/websocket/server.lua | 101 ++++++++++----------------- 2 files changed, 48 insertions(+), 78 deletions(-) diff --git a/lualib/protocol/websocket/client.lua b/lualib/protocol/websocket/client.lua index f3629b74..992fd3c2 100644 --- a/lualib/protocol/websocket/client.lua +++ b/lualib/protocol/websocket/client.lua @@ -1,5 +1,4 @@ local class = require "class" -local log = require "logging" local tcp = require "internal.TCP" @@ -7,15 +6,15 @@ local wbproto = require "protocol.websocket.protocol" local _recv_frame = wbproto.recv_frame local _send_frame = wbproto.send_frame -local httpparser = require "httpparser" -local RESPONSE_PROTOCOL_PARSER = httpparser.parser_response_protocol -local RESPONSE_HEADER_PARSER = httpparser.parser_response_header +local HTTP = require "protocol.http" +local PARSER_HTTP_RESPONSE = HTTP.PARSER_HTTP_RESPONSE local crypt = require "crypt" local sha1 = crypt.sha1 local base64encode = crypt.base64encode local type = type +local next = next local setmetatable = setmetatable local random = math.random @@ -31,7 +30,7 @@ local match = string.match local CRLF = '\x0d\x0a' local CRLF2 = '\x0d\x0a\x0d\x0a' - +local RE_CRLF2 = '[\x0d]?\x0a[\x0d]?\x0a' local function rshift(a, b) return a >> b @@ -79,16 +78,15 @@ local function check_response (self, secure) end buffers[#buffers + 1] = data local buffer = concat(buffers) - if find(buffer, CRLF2) then - local version, code, msg = RESPONSE_PROTOCOL_PARSER(buffer) - if code ~= 101 then + if find(buffer, RE_CRLF2) then + local version, code, msg, headers = PARSER_HTTP_RESPONSE(buffer) + if not version or not code or not msg or not headers then sock_close(self) - return nil, '协议升级失败' + return nil, "错误: 协议升级失败" end - local headers = RESPONSE_HEADER_PARSER(buffer) - if not headers then + if not next(headers) then sock_close(self) - return nil, '错误: ws升级协议' + return nil, "错误: 不支持的响应头部" end local sec_key = headers['Sec-WebSocket-Accept'] local connection = headers['Connection'] @@ -261,8 +259,7 @@ end -- 清理连接 function websocket:close () if self.sock then - self.sock:close() - self.sock = nil + sock_close(self) end end diff --git a/lualib/protocol/websocket/server.lua b/lualib/protocol/websocket/server.lua index 6f2d5ab7..9349d698 100644 --- a/lualib/protocol/websocket/server.lua +++ b/lualib/protocol/websocket/server.lua @@ -1,4 +1,6 @@ -local class = require "class" +local cf = require "cf" +local cf_fork = cf.fork +local cf_sleep = cf.sleep local wbproto = require "protocol.websocket.protocol" local _recv_frame = wbproto.recv_frame @@ -7,30 +9,13 @@ local _send_frame = wbproto.send_frame local log = require "logging" local Log = log:new({ dump = true, path = 'protocol-websocket-server'}) -local co = require "internal.Co" -local co_self = co.self -local co_wait = co.wait -local co_spwan = co.spwan -local co_wakeup = co.wakeup - local type = type -local next = next local pcall = pcall local ipairs = ipairs -local assert = assert -local setmetatable = setmetatable -local tostring = tostring +local insert = table.insert local char = string.char --- 将回调函数写入到队列内 -local function add_to_queue(queue, f) - queue[#queue + 1] = f -end - --- 唤醒write queue -local function wakeup(co, ...) - return co and co_wakeup(co, ...) -end +local class = require "class" local websocket = class("websocket-server") @@ -38,35 +23,37 @@ function websocket:ctor(opt) self._VERSION = '0.07' self.cls = opt.cls self.sock = opt.sock - self.co = co_self() - self.write_co = nil self.closed = nil self.sock._timeout = nil - self.queue = {} - self.write_state = 'work' - co_spwan(function () - self.write_co = co_self() - while 1 do - self.write_state = 'work' - for _, f in ipairs(self.queue) do - local ok, err = pcall(f) - if not ok then - Log:ERROR(err) +end + +function websocket:add_to_queue (f) + if not self.queue then + self.queue = {f} + return cf_fork(function (...) + while 1 do + for index, func in ipairs(self.queue) do + if not self.closed then + local ok, writeable = pcall(func) + if not ok then + Log:ERROR(writeable) + end + if not writeable then + self.queue = nil + return -- Log:WARN("断开连接或写入失败, 不再警告:", index) + end + end + end + self.queue = {} + cf_sleep(0) -- 让出协程执行权 + if self.closed or #self.queue == 0 then + self.queue = nil + return end end - if self.closed then - self.write_state = 'quit' - return - end - self.queue = {} - self.write_state = 'wait' - local continue = co_wait() - if not continue then - self.write_state = 'quit' - return - end - end - end) + end) + end + return self.queue and insert(self.queue, f) end -- send_text、send_binary @@ -74,13 +61,10 @@ function websocket:send (data, binary) if self.closed then return end - if data and type(data) == 'string' then - add_to_queue(self.queue, function () + if type(data) == 'string' and data ~= '' then + self:add_to_queue(function () return _send_frame(self.sock, true, binary and 0x2 or 0x1, data, self.max_payload_len, self.send_masked) end) - if self.write_state == 'wait' then - return wakeup(self.write_co, true) - end end end @@ -91,14 +75,10 @@ function websocket:close (data) end self.closed = true if type(data) == 'string' and data ~= '' then - add_to_queue(self.queue, function () + self:add_to_queue(function () return _send_frame(self.sock, true, 0x8, char(((1000 >> 8) & 0xff), (1000 & 0xff))..data, self.max_payload_len, self.send_masked) end) end - if self.write_state == 'wait' then - wakeup(self.write_co) - end - wakeup(self.co) end -- Websocket Server 事件循环 @@ -122,9 +102,6 @@ function websocket:start() local data, typ, err = _recv_frame(sock, self.max_payload_len, self.send_masked) if (not data and not typ) or typ == 'close' then self.closed = true - if self.write_state == 'wait' then - wakeup(self.write_co) - end if err and err ~= 'read timeout' then local ok, err = pcall(on_error, cls, err) if not ok then @@ -135,17 +112,13 @@ function websocket:start() if not ok then Log:ERROR(err) end - self.sock = nil return end if typ == 'ping' then - add_to_queue(self.queue, function () return _send_frame(sock, true, 0xA, data or '', self.max_payload_len, self.send_masked) end) - if self.write_state == 'wait' then - wakeup(self.write_co, true) - end + self:add_to_queue(function () return _send_frame(sock, true, 0xA, data or '', self.max_payload_len, self.send_masked) end) end if typ == 'text' or typ == 'binary' then - co_spwan(on_message, cls, data, typ == 'binary') + cf_fork(on_message, cls, data, typ == 'binary') end end end From a85c9f33626b71c2f4c6b519bca00a83de2f51e3 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 12 Jun 2019 11:01:05 +0800 Subject: [PATCH 156/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0wsclient=E5=BC=82?= =?UTF-8?q?=E6=AD=A5=E5=86=99=E5=85=A5=E9=98=9F=E5=88=97=E5=B9=B6=E9=80=82?= =?UTF-8?q?=E9=85=8D=E4=B8=80=E4=BA=9B=E4=B8=8D=E8=A7=84=E8=8C=83=E7=9A=84?= =?UTF-8?q?ws=20server=E5=AE=9E=E7=8E=B0(shit)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/websocket/client.lua | 89 +++++++++++++++++++++----- lualib/protocol/websocket/protocol.lua | 26 +++++++- 2 files changed, 96 insertions(+), 19 deletions(-) diff --git a/lualib/protocol/websocket/client.lua b/lualib/protocol/websocket/client.lua index 992fd3c2..660f8f59 100644 --- a/lualib/protocol/websocket/client.lua +++ b/lualib/protocol/websocket/client.lua @@ -1,7 +1,13 @@ local class = require "class" +local log = require "logging" +local Log = log:new{ dump = true, path = "protocol-wsclient"} + local tcp = require "internal.TCP" +local cf = require "cf" +local cf_fork = cf.fork + local wbproto = require "protocol.websocket.protocol" local _recv_frame = wbproto.recv_frame local _send_frame = wbproto.send_frame @@ -15,12 +21,15 @@ local base64encode = crypt.base64encode local type = type local next = next +local pcall = pcall +local ipairs = ipairs local setmetatable = setmetatable local random = math.random local toint = math.tointeger local os_date = os.date local concat = table.concat +local insert = table.insert local char = string.char local byte = string.byte @@ -66,7 +75,6 @@ end local function sock_close (self) self.sock:close() - self.sock = nil end local function check_response (self, secure) @@ -80,7 +88,7 @@ local function check_response (self, secure) local buffer = concat(buffers) if find(buffer, RE_CRLF2) then local version, code, msg, headers = PARSER_HTTP_RESPONSE(buffer) - if not version or not code or not msg or not headers then + if tonumber(version) ~= 1.1 or tonumber(code) ~= 101 or not headers then sock_close(self) return nil, "错误: 协议升级失败" end @@ -90,7 +98,7 @@ local function check_response (self, secure) end local sec_key = headers['Sec-WebSocket-Accept'] local connection = headers['Connection'] - if connection ~= 'Upgrade' then + if not connection or connection:lower() ~= 'upgrade' then sock_close(self) return nil, '错误: 不支持的ws协议版本' end @@ -124,11 +132,13 @@ local function do_handshake (self) local req = { fmt('GET %s HTTP/1.1', self.path), fmt('Data: %s', os_date("Date: %a, %d %b %Y %X GMT")), - fmt('host: %s:%s', self.domain, self.port), + fmt('Host: %s:%s', self.domain, self.port), fmt('Sec-WebSocket-Key: %s', sec_key), + 'Origin: http://'..self.domain, 'Sec-WebSocket-Version: 13', 'Upgrade: websocket', 'Connection: Upgrade', + 'User-Agent: cf-websocket/0.1', CRLF } local ok, err = sock_send(self, concat(req, CRLF)) @@ -189,7 +199,7 @@ function websocket:ctor (opt) self.url = opt.url self.sock = tcp:new() self.sock._timeout = opt.timeout - self.send_masked = opt.send_masked + self.send_masked = opt.send_masked or true self.max_payload_len = opt.max_payload_len or 65535 end @@ -216,20 +226,12 @@ function websocket:connect () return true, err end --- 发送 text/binary -function websocket:send (data, is_binary) - if not self.state then - return nil, '未连接' - end - return _send_frame(self.sock, true, binary and 0x2 or 0x1, data, self.max_payload_len, self.send_masked) -end - -- 接受数据 function websocket:recv() if not self.state then return nil, '未连接' end - local data, typ, err = _recv_frame(self.sock, self.max_payload_len, self.send_masked) + local data, typ, err = _recv_frame(self.sock, self.max_payload_len, not self.send_masked) if typ == 'close' or not typ then self.state = nil if type == 'close' then @@ -240,12 +242,50 @@ function websocket:recv() return data, typ end +-- 发送 text/binary +function websocket:send (data, is_binary) + if not self.state then + return nil, '未连接' + end + local func = function (...) + return _send_frame(self.sock, true, is_binary and 0x2 or 0x1, data, self.max_payload_len, self.send_masked) + end + if not self.queue then + self.queue = { func } + return cf_fork(function (...) + for _, f in ipairs(self.queue) do + local ok, err = pcall(f) + if not ok then + Log:ERROR(err) + end + end + self.queue = nil + end) + end + return insert(self.queue, func) +end + -- 发送ping -function websocket:ping( data) +function websocket:ping(data) if not self.state then return nil, '未连接' end - return _send_frame(self.sock, true, 0x9, data, self.max_payload_len, self.send_masked) + local func = function (...) + return _send_frame(self.sock, true, 0x9, data, self.max_payload_len, self.send_masked) + end + if not self.queue then + self.queue = { func } + return cf_fork(function (...) + for _, f in ipairs(self.queue) do + local ok, err = pcall(f) + if not ok then + Log:ERROR(err) + end + end + self.queue = nil + end) + end + return insert(self.queue, func) end -- 发送pong @@ -253,7 +293,22 @@ function websocket:pong(data) if not self.state then return nil, '未连接' end - return _send_frame(self.sock, true, 0xA, data, self.max_payload_len, self.send_masked) + local func = function (...) + return _send_frame(self.sock, true, 0xA, data, self.max_payload_len, self.send_masked) + end + if not self.queue then + self.queue = { func } + return cf_fork(function (...) + for _, f in ipairs(self.queue) do + local ok, err = pcall(f) + if not ok then + Log:ERROR(err) + end + end + self.queue = nil + end) + end + return insert(self.queue, func) end -- 清理连接 diff --git a/lualib/protocol/websocket/protocol.lua b/lualib/protocol/websocket/protocol.lua index cdd142b3..70d6bd65 100644 --- a/lualib/protocol/websocket/protocol.lua +++ b/lualib/protocol/websocket/protocol.lua @@ -30,9 +30,31 @@ local types = { local function sock_recv (sock, byte) if sock.ssl then - return sock:ssl_recv(byte) + local tab = {} + while 1 do + local data, len = sock:ssl_recv(byte) + if not data then + return nil + end + tab[#tab+1] = data + if len >= byte then + return concat(tab) + end + byte = byte - len + end + end + local tab = {} + while 1 do + local data, len = sock:recv(byte) + if not data then + return nil + end + tab[#tab+1] = data + if len >= byte then + return concat(tab) + end + byte = byte - len end - return sock:recv(byte) end local function sock_send (sock, data) From 06154d4a95ab5ba292f7d76bd2a608a0f8f0a9b2 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 12 Jun 2019 12:24:48 +0800 Subject: [PATCH 157/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9Websocket=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E5=93=8D=E5=BA=94=E7=A0=81=E4=B8=BA101?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index 7c31df06..12c0da8f 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -384,10 +384,10 @@ local function Switch_Protocol(http, cls, sock, header, method, version, path, i 'Sec-WebSocket-Accept: '..base64(sha1(sec_key..'258EAFA5-E914-47DA-95CA-C5AB0DC85B11')) } local protocol = header['Sec-Websocket-Protocol'] - if protocol then -- 仅支持协议回传, 具体实现由用户实现 + if protocol then -- 仅支持协议回传 response[#response+1] = "Sec-Websocket-Protocol: "..tostring(protocol) end - http:tolog(200, path, header['X-Real-IP'] or ip, X_Forwarded_FORMAT(header['X-Forwarded-For'] or ip), method, now() - start_time) + http:tolog(101, path, header['X-Real-IP'] or ip, X_Forwarded_FORMAT(header['X-Forwarded-For'] or ip), method, now() - start_time) local ok = sock:send(concat(response, CRLF)..CRLF2) if not ok then return sock:close() @@ -397,8 +397,7 @@ end local response = {nil, CRLF2, nil} local function send_response (sock, headers, body) - response[1] = concat(headers, CRLF) - response[3] = body + response[1], response[3] = concat(headers, CRLF), body local ok = sock:send(concat(response)) response[1], response[3] = nil, nil return ok From 965545c20f19f3d0fdc78cfae981221634db11f6 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 12 Jun 2019 12:26:14 +0800 Subject: [PATCH 158/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0traefik=E4=B8=8ENginx?= =?UTF-8?q?=E9=83=A8=E7=BD=B2=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6,=20?= =?UTF-8?q?=E5=90=88=E7=90=86=E5=88=A9=E7=94=A8=E8=B4=9F=E8=BD=BD=E5=9D=87?= =?UTF-8?q?=E8=A1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose-with-nginx.yaml | 58 +++++++++++++++++++++---- docker/docker-compose-with-traefik.yaml | 17 +++++++- 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/docker/docker-compose-with-nginx.yaml b/docker/docker-compose-with-nginx.yaml index 97236900..c090e723 100644 --- a/docker/docker-compose-with-nginx.yaml +++ b/docker/docker-compose-with-nginx.yaml @@ -7,29 +7,69 @@ services: ports: - 80:80 volumes: - - ./nginx.conf:/etc/nginx/nginx.conf - networks: - - local + - ./conf.d/nginx.conf:/etc/nginx/nginx.conf links: - WebApp1:webapp1 - WebApp2:webapp2 - WebApp3:webapp3 + networks: + - local + WebApp1: - image: candymi/cfweb:latest + image: candymi/cfweb + restart: always volumes: - - ./script:/app/script + - ./script/:/app/script/ + # 如需设置时区, 建议请将localtime提取到home目录下再挂在使用 + # - /etc/localtime:/etc/localtime + depends_on: + - WebDB + links: + - WebDB:WebDB networks: - local + + WebApp2: - image: candymi/cfweb:latest + image: candymi/cfweb + restart: always volumes: - - ./script:/app/script + - ./script/:/app/script/ + # 如需设置时区, 建议请将localtime提取到home目录下再挂在使用 + # - /etc/localtime:/etc/localtime + depends_on: + - WebDB + links: + - WebDB:WebDB networks: - local + WebApp3: - image: candymi/cfweb:latest + image: candymi/cfweb + restart: always + volumes: + - ./script/:/app/script/ + # 如需设置时区, 建议请将localtime提取到home目录下再挂在使用 + # - /etc/localtime:/etc/localtime + depends_on: + - WebDB + links: + - WebDB:WebDB + networks: + - local + + WebDB: + image: mysql:5.6 + restart: always + environment: + - MYSQL_ROOT_PASSWORD=123456789 + - MYSQL_DATABASE=cfadmin volumes: - - ./script:/app/script + - ./db/:/docker-entrypoint-initdb.d/ + # 如需设置时区, 建议请将localtime提取到home目录下再挂在使用 + # - /etc/localtime:/etc/localtime + ports: + - 3306:3306 networks: - local diff --git a/docker/docker-compose-with-traefik.yaml b/docker/docker-compose-with-traefik.yaml index 219f0689..84f78fd7 100644 --- a/docker/docker-compose-with-traefik.yaml +++ b/docker/docker-compose-with-traefik.yaml @@ -23,7 +23,22 @@ services: - "traefik.domain=localhost" - "traefik.frontend.rule=Host:localhost" volumes: - - ./script:/app/script + - ./script:/app/script/ + networks: + - local + + WebDB: + image: mysql:5.6 + restart: always + environment: + - MYSQL_ROOT_PASSWORD=123456789 + - MYSQL_DATABASE=cfadmin + volumes: + - ./db/:/docker-entrypoint-initdb.d/ + # 如需设置时区, 建议请将localtime提取到home目录下再挂在使用 + # - /etc/localtime:/etc/localtime + ports: + - 3306:3306 networks: - local From 680ec9073462ddabcfb482c5c20e88928172b44b Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 12 Jun 2019 13:44:17 +0800 Subject: [PATCH 159/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=88=B0conf.d=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/nginx.conf | 66 ----------------------------------------------- 1 file changed, 66 deletions(-) delete mode 100644 docker/nginx.conf diff --git a/docker/nginx.conf b/docker/nginx.conf deleted file mode 100644 index 469b57cb..00000000 --- a/docker/nginx.conf +++ /dev/null @@ -1,66 +0,0 @@ -worker_processes 1; - -error_log /var/log/nginx/error.log warn; -pid /var/run/nginx.pid; - - -events { - worker_connections 65535; -} - - -http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - - log_format main '$remote_addr - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "req_time:[$request_time/Sec]" "upstream_time:[$upstream_response_time/Sec]" '; - - access_log /var/log/nginx/access.log main; - - #优化文件发送 - sendfile on; - #tcp_nopush on; - #关闭Nagle算法 - tcp_nodelay on; - - keepalive_timeout 65; - - gzip on; - gzip_vary on; - gzip_min_length 1k; - gzip_comp_level 9; - gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png; - gzip_disable "MSIE [1-6]\."; - - - upstream myweb { - server webapp1:8080; - server webapp2:8080; - server webapp3:8080; - } - - server { - listen 80; - #access_log /var/log/nginx/8080.log main; - - location /ws { - proxy_pass http://myweb/ws; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - } - - location / { - proxy_pass http://myweb; - proxy_http_version 1.1; - proxy_ignore_client_abort on; - proxy_set_header Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } - - } - -} From c0e2d3b4ae0902908fcabe6a2a1b20ffc0852c17 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 13 Jun 2019 03:55:55 +0800 Subject: [PATCH 160/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=AF=A6=E7=BB=86?= =?UTF-8?q?=E7=9A=84=E8=BF=90=E8=A1=8C=E6=97=B6=E6=97=A5=E5=BF=97=E6=89=93?= =?UTF-8?q?=E5=8D=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/Co.lua | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/lualib/internal/Co.lua b/lualib/internal/Co.lua index 27fa7442..d7b8c8b2 100644 --- a/lualib/internal/Co.lua +++ b/lualib/internal/Co.lua @@ -1,9 +1,5 @@ local task = require "task" -local type = type -local assert = assert -local error = error - local task_new = task.new local task_stop = task.stop local task_start = task.start @@ -14,6 +10,11 @@ local co_wait = coroutine.yield local co_status = coroutine.status local co_self = coroutine.running +local type = type +local assert = assert +local xpcall = xpcall +local error = error + local insert = table.insert local remove = table.remove @@ -50,12 +51,14 @@ local function co_push(co) return insert(CO_POOL, co) end +local function dbg (info) + return print(string.format("[%s] %s", os.date("%Y/%m/%d %H:%M:%S"), debug.traceback(co_self(), info, 2))) +end + local function f() while 1 do - local ok, msg = pcall(co_wait()) - if not ok then - print(msg) - end + local f, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 = co_wait() + xpcall(f, dbg, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) local co, main = co_self() if not main then task_push(cos[co]) @@ -86,7 +89,7 @@ end -- 启动 function Co.spwan(func, ...) - if func and type(func) == "function" then + if type(func) == "function" then local co = co_pop(f) cos[co] = task_pop() return task_start(cos[co], co, func, ...) @@ -98,12 +101,14 @@ end function Co.wakeup(co, ...) assert(type(co) == 'thread', "试图传递一个非协程的类型的参数到wakeup内部.") assert(co ~= co_self(), "不能唤醒当前正在执行的协程") - local status = co_status(co) - if main_co == co and status == "suspended" then + if main_co == co then + local status = co_status(co) + if status ~= 'suspended' then + return error('试图唤醒一个状态异常的协程') + end return task_start(main_task, main_co, ...) end - local t = cos[co] - assert(t, "非cf创建的协程不能由cf来唤醒") + local t = assert(cos[co], "非cf创建的协程不能由cf来唤醒") return task_start(t, co, ...) end From c4a136b6363c7aa804904e1c9bea2b7a3c7871a1 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 13 Jun 2019 04:52:10 +0800 Subject: [PATCH 161/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0httpd=E5=90=AF?= =?UTF-8?q?=E5=8A=A8=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/init.lua | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua index cbfe4680..28964464 100644 --- a/lualib/httpd/init.lua +++ b/lualib/httpd/init.lua @@ -2,17 +2,18 @@ local HTTP = require "protocol.http" local HTTPD = require "httpd.Router" local tcp = require "internal.TCP" local class = require "class" -local sys = require "system" local log = require "logging" local cf = require "cf" +local sys = require("sys") +local os_date = sys.date + local type = type local ipairs = ipairs local fmt = string.format local match = string.match local io_write = io.write -local os_date = require("sys").date local toint = math.tointeger @@ -29,7 +30,7 @@ local httpd = class("httpd") function httpd:ctor(opt) self.API = HTTPD.API self.USE = HTTPD.USE - self.IO = tcp:new() + self.sock = tcp:new() end -- 用来注册WebSocket对象 @@ -138,21 +139,29 @@ function httpd:log(path) end end +local log_fmt = "[%s] - %s - %s - %s - %s - %d - req_time: %0.6f/Sec\n" + function httpd:tolog(code, path, ip, ip_list, method, speed) if self.logging then - self.logging:dump(fmt("[%s] - %s - %s - %s - %s - %d - req_time: %0.6f/Sec\n", os_date("%Y/%m/%d %H:%M:%S"), ip, ip_list, path, method, code, speed)) + self.logging:dump(fmt(log_fmt, os_date("%Y/%m/%d %H:%M:%S"), ip, ip_list, path, method, code, speed)) end if self.output then - io_write(fmt("[%s] - %s - %s - %s - %s - %d - req_time: %0.6f/Sec\n", os_date("%Y/%m/%d %H:%M:%S"), ip, ip_list, path, method, code, speed)) + io_write(fmt(log_fmt, os_date("%Y/%m/%d %H:%M:%S"), ip, ip_list, path, method, code, speed)) end end -- 监听请求 function httpd:listen(ip, port, backlog) + if type(ip) == 'string' and type(port) == 'number' then + io_write(fmt('\27[32m[%s] [INFO]\27[0m httpd正在监听: %s:%s \n', os_date("%Y/%m/%d %H:%M:%S"), "0.0.0.0", port)) + if self.logging then + self.logging:dump(fmt('[%s] [INFO] httpd正在监听: %s:%s\n', os_date("%Y/%m/%d %H:%M:%S"), "0.0.0.0", port)) + end + end if type(backlog) == 'number' then - self.IO:set_backlog(toint(backlog)) + self.sock:set_backlog(toint(backlog)) end - self.IO:listen(ip, port, function (fd, ipaddr) + self.sock:listen(ip or "0.0.0.0", toint(port), function (fd, ipaddr) return EVENT_DISPATCH(fd, match(ipaddr, '^::[f]+:(.+)') or ipaddr, self) end) return self @@ -162,6 +171,10 @@ end function httpd:run() if io.type(io.output()) == 'file' then self.output = true + io_write(fmt('\27[32m[%s] [INFO]\27[0m httpd正在运行Web Server服务...\n', os_date("%Y/%m/%d %H:%M:%S"))) + end + if self.logging then + self.logging:dump(fmt('[%s] [INFO] httpd正在运行Web Server服务...\n', os_date("%Y/%m/%d %H:%M:%S"))) end return cf.wait() end From 3a52dd149cafc3672f5bebf03ea5760ad034e949 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 13 Jun 2019 04:56:53 +0800 Subject: [PATCH 162/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=B8=AAstd?= =?UTF-8?q?out=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/init.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua index 28964464..055df85a 100644 --- a/lualib/httpd/init.lua +++ b/lualib/httpd/init.lua @@ -153,7 +153,9 @@ end -- 监听请求 function httpd:listen(ip, port, backlog) if type(ip) == 'string' and type(port) == 'number' then - io_write(fmt('\27[32m[%s] [INFO]\27[0m httpd正在监听: %s:%s \n', os_date("%Y/%m/%d %H:%M:%S"), "0.0.0.0", port)) + if io.type(io.output()) == 'file' then + io_write(fmt('\27[32m[%s] [INFO]\27[0m httpd正在监听: %s:%s \n', os_date("%Y/%m/%d %H:%M:%S"), "0.0.0.0", port)) + end if self.logging then self.logging:dump(fmt('[%s] [INFO] httpd正在监听: %s:%s\n', os_date("%Y/%m/%d %H:%M:%S"), "0.0.0.0", port)) end From 305a88535eb2530ec614949eacb0897c6aeeea69 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 13 Jun 2019 05:18:06 +0800 Subject: [PATCH 163/956] =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E5=A4=A7=E5=B0=8F=E5=86=99=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http.lua | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index 12c0da8f..da929f73 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -338,7 +338,7 @@ local function ERROR_RESPONSE(http, code, path, ip, forword, method, speed) 'Allow: GET, POST, PUT, HEAD, OPTIONS', 'Connection: close', 'Content-length: 0', - 'server: ' .. (http.__server or SERVER), + 'Server: ' .. (http.__server or SERVER), }, CRLF), CRLF2}) end @@ -474,7 +474,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) 'Access-Control-Allow-Credentials: true', 'Access-Control-Max-Age: 86400', 'Connection: keep-alive', - 'server: ' .. (server or SERVER), + 'Server: ' .. (server or SERVER), }, CRLF)..CRLF2) return sock:close() end @@ -499,7 +499,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) sock:send(concat({ REQUEST_STATUCODE_RESPONSE(code), HTTP_DATE(), 'Connection: close', - 'server: ' .. (server or SERVER), + 'Server: ' .. (server or SERVER), 'Location: ' .. (data or "https://github.com/CandyMi/core_framework"), }, CRLF)..CRLF2) return sock:close() @@ -511,7 +511,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) REQUEST_STATUCODE_RESPONSE(code), HTTP_DATE(), 'Origin: *', 'Allow: GET, POST, PUT, HEAD, OPTIONS', - 'server: ' .. (server or SERVER), + 'Server: ' .. (server or SERVER), 'Connection: close', 'Content-Type: ' .. REQUEST_MIME_RESPONSE('html'), 'Content-Length: '..tostring(#data), @@ -607,7 +607,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) header[#header+1] = HTTP_DATE() header[#header+1] = 'Origin: *' header[#header+1] = 'Allow: GET, POST, PUT, HEAD, OPTIONS' - header[#header+1] = 'server: ' .. (server or SERVER) + header[#header+1] = 'Server: ' .. (server or SERVER) local Connection = 'Connection: keep-alive' if not HEADER['Connection'] or lower(HEADER['Connection']) == 'close' then Connection = 'Connection: close' @@ -615,7 +615,6 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) header[#header+1] = Connection if body then if type(body) == 'string' then - header[#header+1] = 'Transfer-Encoding: identity' header[#header+1] = 'Content-Length: '.. #body else Log:WARN('response body not a string type.'..'('..tostring(body)..')') From e92be0b66b2bd0a21844ab34ddaefa032a188028 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 13 Jun 2019 21:01:58 +0800 Subject: [PATCH 164/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dview=E7=9A=84?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E6=B2=A1=E6=9C=89=E6=B8=85=E9=99=A4=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/view.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lualib/admin/view.lua b/lualib/admin/view.lua index fd78aae5..a8fb31ac 100644 --- a/lualib/admin/view.lua +++ b/lualib/admin/view.lua @@ -39,6 +39,9 @@ function view.use (path, f) if not ok then return utils.redirect(url) end + if not config.cache then + template.cache = {} + end local ok, html = pcall(f, httpctx:new{content = content}, db) return html end) From 881719dbed8df3174b44d6d096fefcc66b578cfe Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 16 Jun 2019 18:04:45 +0800 Subject: [PATCH 165/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/httpc/init.lua b/lualib/httpc/init.lua index 9284bdad..db96c9f3 100644 --- a/lualib/httpc/init.lua +++ b/lualib/httpc/init.lua @@ -201,7 +201,7 @@ local function multi_request (opt) response[index] = {code, msg, now() - t} if #response >= len and not wakeuped then wakeuped = true - cf_wakeup(co, nil, response) + cf_wakeup(co, true, response) end return end) From 8bb832c7ae27922113c3c99cf1b22e4ba4fca015 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 21 Jun 2019 08:06:30 +0800 Subject: [PATCH 166/956] =?UTF-8?q?=E6=8F=90=E5=8F=96core=5Fev=E5=88=86?= =?UTF-8?q?=E7=A6=BB=E5=88=B0=E5=8D=95=E7=8B=AC=E7=9A=84=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=B8=AD.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Makefile | 8 ++-- src/core.c | 116 ++------------------------------------------------ src/core.h | 55 ------------------------ src/core_ev.h | 62 ++++++++++++++++++++++++++- 4 files changed, 68 insertions(+), 173 deletions(-) diff --git a/src/Makefile b/src/Makefile index e804a6cf..51142951 100644 --- a/src/Makefile +++ b/src/Makefile @@ -29,11 +29,11 @@ MACRO += -w -Os build : - $(CC) -o libcore.so core.c core_memory.c core_sys.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) + $(CC) -o libcore.so core.c core_ev.c core_memory.c core_sys.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) mv *.so /usr/local/lib - $(CC) core_start.c -o cfadmin $(MACRO) -lcore -ldl + $(CC) -o cfadmin core_start.c $(MACRO) $(INCLUDES) $(LIBS) -lcore -ldl mv cfadmin ../ @@ -41,11 +41,11 @@ rebuild: rm -rf *.o *.so - $(CC) -o libcore.so core.c core_memory.c core_sys.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) + $(CC) -o libcore.so core.c core_ev.c core_memory.c core_sys.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) mv *.so /usr/local/lib - $(CC) core_start.c -o cfadmin $(MACRO) -lcore -ldl + $(CC) -o cfadmin core_start.c core.c $(MACRO) $(INCLUDES) $(LIBS) -lcore -ldl mv cfadmin ../ diff --git a/src/core.c b/src/core.c index e562ac14..d8fdd111 100644 --- a/src/core.c +++ b/src/core.c @@ -1,115 +1,5 @@ #include "core.h" -/* =========== Timer =========== */ -void -core_timer_init(core_timer *timer, _TIMER_CB cb){ - - timer->repeat = timer->at = 0x0; - - ev_init(timer, cb); - -} - -void -core_timer_start(core_loop *loop, core_timer *timer, ev_tstamp timeout){ - - timer->repeat = timeout; - - ev_timer_again(loop ? loop : CORE_LOOP, timer); - -} - -void -core_timer_stop(core_loop *loop, core_timer *timer){ - - timer->repeat = timer->at = 0x0; - - ev_timer_again(loop ? loop : CORE_LOOP, timer); - -} -/* =========== Timer =========== */ - - - - -/* =========== IO =========== */ -void -core_io_init(core_io *io, _IO_CB cb, int fd, int events){ - - ev_io_init(io, cb, fd, events); - -} - -void -core_io_start(core_loop *loop, core_io *io){ - - ev_io_start(loop ? loop : CORE_LOOP, io); - -} - -void -core_io_stop(core_loop *loop, core_io *io){ - - if (io->events || io->fd){ - - ev_io_stop(loop ? loop : CORE_LOOP, io); - - io->fd = io->events = 0x0; - - } - -} -/* =========== IO =========== */ - - -/* =========== TASK =========== */ - -void -core_task_init(core_task *task, _TASK_CB cb){ - - ev_idle_init(task, cb); - -} - -void -core_task_start(core_loop *loop, core_task *task){ - - ev_idle_start(loop ? loop : CORE_LOOP, task); - -} - -void -core_task_stop(core_loop *loop, core_task *task){ - - ev_idle_stop(loop ? loop : CORE_LOOP, task); - -} - -/* =========== TASK =========== */ - - -core_loop * -core_default_loop(){ - // ev_supported_backends() & EVBACKEND_EPOLL || // Linux 使用 epoll - // ev_supported_backends() & EVBACKEND_KQUEUE || // mac|BSD 使用 kqueue - // ev_supported_backends() & EVBACKEND_SELECT || // other 使用 select - // EVFLAG_AUTO // select 都没有就自动选择 - return ev_default_loop(ev_embeddable_backends() & ev_supported_backends() || EVFLAG_AUTO); -} - -void -core_break(core_loop *loop, int mode){ - return ev_break(loop ? loop : CORE_LOOP, mode); -} - - -int -core_start(core_loop *loop, int mode){ - - return ev_run(loop ? loop : CORE_LOOP, mode); - -} - const char *signame[]= { "INVALID", "SIGHUP", @@ -318,10 +208,10 @@ void core_sys_init(){ /* hook libev 内存分配 */ - ev_set_allocator(EV_ALLOC); + core_ev_set_allocator(EV_ALLOC); /* hook 事件循环错误信息 */ - ev_set_syserr_cb(ERROR_CB); + core_ev_set_syserr_cb(ERROR_CB); /* 初始化Lua脚本 */ init_main(); @@ -330,5 +220,5 @@ core_sys_init(){ int core_sys_run(){ - return core_start(CORE_LOOP_ 0); + return core_start(core_default_loop(), 0); } diff --git a/src/core.h b/src/core.h index cee828f1..f3a80523 100644 --- a/src/core.h +++ b/src/core.h @@ -5,61 +5,6 @@ #include "core_memory.h" #include "core_ev.h" -#define CORE_LOOP core_default_loop() - -#define CORE_LOOP_ CORE_LOOP, - -#define CORE_P core_loop *loop - -#define CORE_P_ core_loop *loop, - -/* 获取用户数据 */ -#define core_get_watcher_userdata(watcher) ((watcher)->data ? (watcher)->data: NULL) - -/* 设置用户数据 */ -#define core_set_watcher_userdata(watcher, userdata) ((watcher)->data = (userdata)) - - -typedef ev_io core_io; -typedef ev_idle core_task; -typedef ev_timer core_timer; -typedef ev_signal core_signal; -typedef struct ev_loop core_loop; - -typedef void (*_IO_CB)(core_loop *loop, core_io *io, int revents); -typedef void (*_TASK_CB)(core_loop *loop, core_task *task, int revents); -typedef void (*_TIMER_CB)(core_loop *loop, core_timer *timer, int revents); - -/* =========== Timer =========== */ -void core_timer_init(core_timer *timer, _TIMER_CB cb); - -void core_timer_start(core_loop *loop, core_timer *timer, ev_tstamp timeout); - -void core_timer_stop(core_loop *loop, core_timer *timer); -/* =========== Timer =========== */ - -/* =========== IO =========== */ -void core_io_init(core_io *io, _IO_CB cb, int fd, int events); - -void core_io_start(core_loop *loop, core_io *io); - -void core_io_stop(core_loop *loop, core_io *io); -/* =========== IO =========== */ - -/* =========== TASK =========== */ -void core_task_init(core_task *task, _TASK_CB cb); - -void core_task_start(core_loop *loop, core_task *task); - -void core_task_stop(core_loop *loop, core_task *task); -/* =========== TASK =========== */ - -void core_break(core_loop *loop, int mode); - -int core_start(core_loop *loop, int mode); - -core_loop* core_default_loop(); - void core_sys_init(); int core_sys_run(); diff --git a/src/core_ev.h b/src/core_ev.h index 0bc299a4..e2a32912 100644 --- a/src/core_ev.h +++ b/src/core_ev.h @@ -10,7 +10,9 @@ #define EV_VERIFY 0 -#define EV_USE_FLOOR 1 +#ifndef NO_USE_FLOOR + #define EV_USE_FLOOR 1 // 如果没有math.h库, 可以定义NO_USE_FLOOR +#endif #define EV_USE_REALTIME 1 @@ -40,4 +42,62 @@ #include +#define CORE_LOOP core_default_loop() + +#define CORE_LOOP_ CORE_LOOP, + +#define CORE_P core_loop *loop + +#define CORE_P_ core_loop *loop, + +/* 获取用户数据 */ +#define core_get_watcher_userdata(watcher) ((watcher)->data ? (watcher)->data: NULL) + +/* 设置用户数据 */ +#define core_set_watcher_userdata(watcher, userdata) ((watcher)->data = (userdata)) + +void core_ev_set_allocator (void *(*cb)(void *ptr, long size)); + +void core_ev_set_syserr_cb (void (*cb)(const char *msg)); + +typedef ev_io core_io; +typedef ev_idle core_task; +typedef ev_timer core_timer; +typedef ev_signal core_signal; +typedef struct ev_loop core_loop; + +typedef void (*_IO_CB)(core_loop *loop, core_io *io, int revents); +typedef void (*_TASK_CB)(core_loop *loop, core_task *task, int revents); +typedef void (*_TIMER_CB)(core_loop *loop, core_timer *timer, int revents); + +/* =========== Timer =========== */ +void core_timer_init(core_timer *timer, _TIMER_CB cb); + +void core_timer_start(core_loop *loop, core_timer *timer, ev_tstamp timeout); + +void core_timer_stop(core_loop *loop, core_timer *timer); +/* =========== Timer =========== */ + +/* =========== IO =========== */ +void core_io_init(core_io *io, _IO_CB cb, int fd, int events); + +void core_io_start(core_loop *loop, core_io *io); + +void core_io_stop(core_loop *loop, core_io *io); +/* =========== IO =========== */ + +/* =========== TASK =========== */ +void core_task_init(core_task *task, _TASK_CB cb); + +void core_task_start(core_loop *loop, core_task *task); + +void core_task_stop(core_loop *loop, core_task *task); +/* =========== TASK =========== */ + +void core_break(core_loop *loop, int mode); + +int core_start(core_loop *loop, int mode); + +core_loop* core_default_loop(); + #endif From a5a3b69bb54d9402a6a293a7cf43b110b5bf1c85 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 21 Jun 2019 08:07:02 +0800 Subject: [PATCH 167/956] =?UTF-8?q?=E8=A1=A5=E5=85=85core=5Fev.c?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_ev.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 src/core_ev.c diff --git a/src/core_ev.c b/src/core_ev.c new file mode 100644 index 00000000..2a0030ef --- /dev/null +++ b/src/core_ev.c @@ -0,0 +1,120 @@ +#include "core_ev.h" + +/* =========== Timer =========== */ +void +core_timer_init(core_timer *timer, _TIMER_CB cb){ + + timer->repeat = timer->at = 0x0; + + ev_init(timer, cb); + +} + +void +core_timer_start(core_loop *loop, core_timer *timer, ev_tstamp timeout){ + + timer->repeat = timeout; + + ev_timer_again(loop ? loop : CORE_LOOP, timer); + +} + +void +core_timer_stop(core_loop *loop, core_timer *timer){ + + timer->repeat = timer->at = 0x0; + + ev_timer_again(loop ? loop : CORE_LOOP, timer); + +} +/* =========== Timer =========== */ + + + + +/* =========== IO =========== */ +void +core_io_init(core_io *io, _IO_CB cb, int fd, int events){ + + ev_io_init(io, cb, fd, events); + +} + +void +core_io_start(core_loop *loop, core_io *io){ + + ev_io_start(loop ? loop : CORE_LOOP, io); + +} + +void +core_io_stop(core_loop *loop, core_io *io){ + + if (io->events || io->fd){ + + ev_io_stop(loop ? loop : CORE_LOOP, io); + + io->fd = io->events = 0x0; + + } + +} +/* =========== IO =========== */ + + +/* =========== TASK =========== */ + +void +core_task_init(core_task *task, _TASK_CB cb){ + + ev_idle_init(task, cb); + +} + +void +core_task_start(core_loop *loop, core_task *task){ + + ev_idle_start(loop ? loop : CORE_LOOP, task); + +} + +void +core_task_stop(core_loop *loop, core_task *task){ + + ev_idle_stop(loop ? loop : CORE_LOOP, task); + +} + +/* =========== TASK =========== */ + + +core_loop * +core_default_loop(){ + // ev_supported_backends() & EVBACKEND_EPOLL || // Linux 使用 epoll + // ev_supported_backends() & EVBACKEND_KQUEUE || // mac|BSD 使用 kqueue + // ev_supported_backends() & EVBACKEND_SELECT || // other 使用 select + // EVFLAG_AUTO // select 都没有就自动选择 + return ev_default_loop(ev_embeddable_backends() & ev_supported_backends() || EVFLAG_AUTO); +} + +void +core_break(core_loop *loop, int mode){ + return ev_break(loop ? loop : CORE_LOOP, mode); +} + +void +core_ev_set_allocator(void *(*cb)(void *ptr, long size)){ + return ev_set_allocator(cb); +} + +void +core_ev_set_syserr_cb(void (*cb)(const char *msg)){ + return ev_set_syserr_cb(cb); +} + +int +core_start(core_loop *loop, int mode){ + + return ev_run(loop ? loop : CORE_LOOP, mode); + +} From 176084c3d7340a163bf35f969b171d00afcc00de Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 21 Jun 2019 08:17:48 +0800 Subject: [PATCH 168/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0MQ=E7=9A=84=E7=A4=BA?= =?UTF-8?q?=E4=BE=8B,=E5=A2=9E=E5=8A=A0lua=5Ftable.lua=E7=9A=84=E6=80=A7?= =?UTF-8?q?=E8=83=BD=E6=B5=8B=E8=AF=95=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_MQ.lua | 53 ++++++++++++++++----------------------- script/test_lua_table.lua | 30 +++++++++++----------- 2 files changed, 36 insertions(+), 47 deletions(-) diff --git a/script/test_MQ.lua b/script/test_MQ.lua index 3b54e29b..060cb29b 100644 --- a/script/test_MQ.lua +++ b/script/test_MQ.lua @@ -1,45 +1,34 @@ --- 基于redis的订阅与发布消息队列(同步处理) +-- local MQ = require "MQ.stomp" +local MQ = require "MQ.redis" +-- local MQ = require "MQ.mqtt" + local cf = require "cf" -local MQ = require "MQ" require "utils" -local rds = MQ:new { +local mq = MQ:new { host = 'localhost', + -- port = 61613, + -- port = 1883, port = 6379, - type = 'redis' + -- vhost = '/exchange', + -- auth = "admin", + -- username = "guest", + -- password = "guest", } -print(rds:on('/test/*', function (msg) +mq:on('/test', function (msg) + print("收到来自/test的消息.") var_dump(msg) -end)) - -cf.at(1, function ( ... ) - rds:emit("/test/admin", '{"code":100}') end) -cf.sleep(10) -print("停止消息队列监听与投递.") -rds:close() - --- 基于mqtt的订阅与发布消息队列(同步处理) -local cf = require "cf" -local MQ = require "MQ" -require "utils" - -local mqtt = MQ:new { - host = 'localhost', - port = 1883, - type = 'mqtt' -} - -cf.at(0.1, function ( ... ) - mqtt:emit("/test/admin", '{"code":100}') +mq:on('/admin', function (msg) + print("收到来自/admin的消息.") + var_dump(msg) end) -print(mqtt:on('/test/*', function (msg) - var_dump(msg) -end)) +cf.at(0.1, function (args) + print(mq:emit('/test', '{"code":'..math.random(1, 100)..',"from":"/test"}')) + print(mq:emit('/admin', '{"code":'..math.random(1, 100)..',"from":"/admin"}')) +end) -cf.sleep(10) -print("停止消息队列监听与投递.") -mqtt:close() +mq:start() diff --git a/script/test_lua_table.lua b/script/test_lua_table.lua index 97b13b20..2619521c 100644 --- a/script/test_lua_table.lua +++ b/script/test_lua_table.lua @@ -1,19 +1,19 @@ -- 0m0.190s --- local t = {} --- local insert = table.insert --- for i = 1, 10000000 do --- insert(t, i) --- end +local t = {} +local insert = table.insert +for i = 1, 10000000 do + insert(t, i) +end -- 0m1.359s --- local t = {} --- for i = 1, 10000000 do --- t[#t+1] = i --- end +local t = {} +for i = 1, 10000000 do + t[#t+1] = i +end --- -- 0m0.666s --- local t = {} --- local insert = table.insert --- for i = 1, 10000 do --- insert(t, 1, i) --- end \ No newline at end of file +-- 0m0.666s +local t = {} +local insert = table.insert +for i = 1, 10000 do + insert(t, 1, i) +end From 19288f49eff2828dab0ee6e8dda5fb4ca10d69d5 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 21 Jun 2019 08:33:06 +0800 Subject: [PATCH 169/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E5=BC=95=E7=94=A8=E9=94=99=E8=AF=AF=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 24 ++++++++++++------------ src/core_ev.c | 13 +++++++++++++ src/core_ev.h | 7 +++++++ 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/core.c b/src/core.c index d8fdd111..1adc614f 100644 --- a/src/core.c +++ b/src/core.c @@ -129,28 +129,28 @@ void signal_init(){ /* 忽略父进程退出的信号 */ - ev_signal_init(&sighup, SIG_IGNORE, SIGHUP); - ev_signal_start(CORE_LOOP_ &sighup); + core_signal_init(&sighup, SIG_IGNORE, SIGHUP); + core_signal_start(CORE_LOOP_ &sighup); /* 忽略管道信号 */ - ev_signal_init(&sigpipe, SIG_IGNORE, SIGPIPE); - ev_signal_start(CORE_LOOP_ &sigpipe); + core_signal_init(&sigpipe, SIG_IGNORE, SIGPIPE); + core_signal_start(CORE_LOOP_ &sigpipe); /* 忽略Ctrl-Z操作信号 */ - ev_signal_init(&sigtstp, SIG_IGNORE, SIGTSTP); - ev_signal_start(CORE_LOOP_ &sigtstp); + core_signal_init(&sigtstp, SIG_IGNORE, SIGTSTP); + core_signal_start(CORE_LOOP_ &sigtstp); /* TERM信号 显示退出 */ - ev_signal_init(&sigterm, SIG_EXIT, SIGTERM); - ev_signal_start(CORE_LOOP_ &sigterm); + core_signal_init(&sigterm, SIG_EXIT, SIGTERM); + core_signal_start(CORE_LOOP_ &sigterm); /* INT信号 显示退出 */ - ev_signal_init(&sigint, SIG_EXIT, SIGINT); - ev_signal_start(CORE_LOOP_ &sigint); + core_signal_init(&sigint, SIG_EXIT, SIGINT); + core_signal_start(CORE_LOOP_ &sigint); /* QUIT信号 显示退出 */ - ev_signal_init(&sigquit, SIG_EXIT, SIGQUIT); - ev_signal_start(CORE_LOOP_ &sigquit); + core_signal_init(&sigquit, SIG_EXIT, SIGQUIT); + core_signal_start(CORE_LOOP_ &sigquit); } diff --git a/src/core_ev.c b/src/core_ev.c index 2a0030ef..ef28889d 100644 --- a/src/core_ev.c +++ b/src/core_ev.c @@ -87,6 +87,19 @@ core_task_stop(core_loop *loop, core_task *task){ /* =========== TASK =========== */ +/* =========== Signal =========== */ +void +core_signal_init(core_signal *signal, _SIGNAL_CB cb, int signum){ + ev_signal_init(signal, cb, signum); +} + +void +core_signal_start(core_loop *loop, core_signal *signal){ + ev_signal_start(loop ? loop : CORE_LOOP, signal); +} +/* =========== Signal =========== */ + + core_loop * core_default_loop(){ diff --git a/src/core_ev.h b/src/core_ev.h index e2a32912..c4b4a269 100644 --- a/src/core_ev.h +++ b/src/core_ev.h @@ -69,6 +69,7 @@ typedef struct ev_loop core_loop; typedef void (*_IO_CB)(core_loop *loop, core_io *io, int revents); typedef void (*_TASK_CB)(core_loop *loop, core_task *task, int revents); typedef void (*_TIMER_CB)(core_loop *loop, core_timer *timer, int revents); +typedef void (*_SIGNAL_CB)(core_loop *loop, core_signal *signal, int revents); /* =========== Timer =========== */ void core_timer_init(core_timer *timer, _TIMER_CB cb); @@ -94,6 +95,12 @@ void core_task_start(core_loop *loop, core_task *task); void core_task_stop(core_loop *loop, core_task *task); /* =========== TASK =========== */ +/* =========== Signal =========== */ +void core_signal_init(core_signal *signal, _SIGNAL_CB cb, int signum); + +void core_signal_start(core_loop *loop, core_signal *signal); +/* =========== Signal =========== */ + void core_break(core_loop *loop, int mode); int core_start(core_loop *loop, int mode); From b767e4a4b9813069cd90669e3cc3961a963a4e02 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 21 Jun 2019 13:28:03 +0800 Subject: [PATCH 170/956] =?UTF-8?q?=E4=BC=98=E5=8C=96http=20Websocket?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E5=88=A4=E6=96=AD=E5=B9=B6=E5=8E=BB=E9=99=A4?= =?UTF-8?q?=E5=86=97=E4=BD=99=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http.lua | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index da929f73..fa510d9c 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -344,43 +344,28 @@ end -- WebSocket local function Switch_Protocol(http, cls, sock, header, method, version, path, ip, start_time) - if version ~= 1.1 then - sock:send(ERROR_RESPONSE(http, 505, path, ip, header['X-Forwarded-For'] or ip, method, now() - start_time)) - sock:close() - return - end if method ~= 'GET' then - sock:send(ERROR_RESPONSE(http, 405, path, ip, header['X-Forwarded-For'] or ip, method, now() - start_time)) - sock:close() - return + return sock:send(ERROR_RESPONSE(http, 400, path, ip, header['X-Forwarded-For'] or ip, method, now() - start_time)) end - if not header['Upgrade'] or lower(header['Upgrade']) ~= 'websocket' then - sock:send(ERROR_RESPONSE(http, 400, path, ip, header['X-Forwarded-For'] or ip, method, now() - start_time)) - sock:close() - return + if version ~= 1.1 then + return sock:send(ERROR_RESPONSE(http, 400, path, ip, header['X-Forwarded-For'] or ip, method, now() - start_time)) end if not header['Upgrade'] or lower(header['Upgrade']) ~= 'websocket' then - sock:send(ERROR_RESPONSE(http, 406, path, ip, header['X-Forwarded-For'] or ip, method, now() - start_time)) - sock:close() - return + return sock:send(ERROR_RESPONSE(http, 401, path, ip, header['X-Forwarded-For'] or ip, method, now() - start_time)) end if header['Sec-WebSocket-Version'] ~= '13' then - sock:send(ERROR_RESPONSE(http, 505, path, ip, header['X-Forwarded-For'] or ip, method, now() - start_time)) - sock:close() - return + return sock:send(ERROR_RESPONSE(http, 403, path, ip, header['X-Forwarded-For'] or ip, method, now() - start_time)) end local sec_key = header['Sec-WebSocket-Key'] if not sec_key or sec_key == '' then - sock:send(ERROR_RESPONSE(http, 505, path, ip, header['X-Forwarded-For'] or ip, method, now() - start_time)) - sock:close() - return + return sock:send(ERROR_RESPONSE(http, 505, path, ip, header['X-Forwarded-For'] or ip, method, now() - start_time)) end local response = { REQUEST_STATUCODE_RESPONSE(101), HTTP_DATE(), - 'Connection: Upgrade', 'Server: '..(http.__server or SERVER), 'Upgrade: WebSocket', + 'Connection: Upgrade', 'Sec-WebSocket-Accept: '..base64(sha1(sec_key..'258EAFA5-E914-47DA-95CA-C5AB0DC85B11')) } local protocol = header['Sec-Websocket-Protocol'] @@ -390,7 +375,7 @@ local function Switch_Protocol(http, cls, sock, header, method, version, path, i http:tolog(101, path, header['X-Real-IP'] or ip, X_Forwarded_FORMAT(header['X-Forwarded-For'] or ip), method, now() - start_time) local ok = sock:send(concat(response, CRLF)..CRLF2) if not ok then - return sock:close() + return end return wsserver:new({cls = cls, sock = sock}):start() end From 3407c195e90a7f61e1e799ea9c3128676d9f306e Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 22 Jun 2019 12:03:30 +0800 Subject: [PATCH 171/956] =?UTF-8?q?=E4=BC=98=E5=8C=96Websocket=20server?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/websocket/server.lua | 45 +++++++++++----------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/lualib/protocol/websocket/server.lua b/lualib/protocol/websocket/server.lua index 9349d698..544e0a5a 100644 --- a/lualib/protocol/websocket/server.lua +++ b/lualib/protocol/websocket/server.lua @@ -27,30 +27,21 @@ function websocket:ctor(opt) self.sock._timeout = nil end +-- 异步消息发送 function websocket:add_to_queue (f) if not self.queue then self.queue = {f} return cf_fork(function (...) - while 1 do - for index, func in ipairs(self.queue) do - if not self.closed then - local ok, writeable = pcall(func) - if not ok then - Log:ERROR(writeable) - end - if not writeable then - self.queue = nil - return -- Log:WARN("断开连接或写入失败, 不再警告:", index) - end - end + for index, func in ipairs(self.queue) do + local ok, writeable = pcall(func) + if not ok then + Log:ERROR(writeable) end - self.queue = {} - cf_sleep(0) -- 让出协程执行权 - if self.closed or #self.queue == 0 then - self.queue = nil - return + if not writeable then + break end end + self.queue = nil end) end return self.queue and insert(self.queue, f) @@ -61,11 +52,10 @@ function websocket:send (data, binary) if self.closed then return end - if type(data) == 'string' and data ~= '' then - self:add_to_queue(function () - return _send_frame(self.sock, true, binary and 0x2 or 0x1, data, self.max_payload_len, self.send_masked) - end) - end + assert(type(data) == 'string' and data ~= '', "websoket error: send发送的消息应该是string类型.") + self:add_to_queue(function () + return _send_frame(self.sock, true, binary and 0x2 or 0x1, data, self.max_payload_len, self.send_masked) + end) end -- 发送close帧 @@ -74,11 +64,12 @@ function websocket:close (data) return end self.closed = true - if type(data) == 'string' and data ~= '' then - self:add_to_queue(function () - return _send_frame(self.sock, true, 0x8, char(((1000 >> 8) & 0xff), (1000 & 0xff))..data, self.max_payload_len, self.send_masked) - end) - end + self:add_to_queue(function () + return _send_frame(self.sock, true, 0x8, char(((1000 >> 8) & 0xff), (1000 & 0xff))..data, self.max_payload_len, self.send_masked) + end) + self:add_to_queue(function () + return self.sock:close() + end) end -- Websocket Server 事件循环 From f7e27130da34c4d30604da41ea6c65c058a68eb5 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 22 Jun 2019 12:32:37 +0800 Subject: [PATCH 172/956] =?UTF-8?q?=E4=BC=98=E5=8C=96protocol=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/websocket/protocol.lua | 45 ++++++++++---------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/lualib/protocol/websocket/protocol.lua b/lualib/protocol/websocket/protocol.lua index 70d6bd65..cb1c281a 100644 --- a/lualib/protocol/websocket/protocol.lua +++ b/lualib/protocol/websocket/protocol.lua @@ -9,6 +9,8 @@ local str_char = string.char local rand = math.random local tostring = tostring local type = type +local error = error +local assert = assert local new_tab = require("sys").new_tab @@ -138,9 +140,6 @@ function _M.recv_frame(sock, max_payload_len, force_masking) end end - -- print("payload len: ", payload_len, ", max payload len: ", - -- max_payload_len) - if payload_len > max_payload_len then return nil, nil, "exceeding max payload len" end @@ -152,7 +151,6 @@ function _M.recv_frame(sock, max_payload_len, force_masking) else rest = payload_len end - -- print("rest: ", rest) local data if rest > 0 then @@ -226,7 +224,7 @@ end local function build_frame(fin, opcode, payload_len, payload, masking) - -- XXX optimize this when we have string.buffer in LuaJIT 2.1 + local fst if fin then fst = 0x80 | opcode @@ -249,7 +247,7 @@ local function build_frame(fin, opcode, payload_len, payload, masking) end snd = 127 - -- XXX we only support 31-bit length here + extra_len_bytes = char(0, 0, 0, 0, (payload_len >> 24) & 0xff, (payload_len >> 16) & 0xff, @@ -282,33 +280,24 @@ _M.build_frame = build_frame function _M.send_frame(sock, fin, opcode, payload, max_payload_len, masking) - if not payload then - payload = "" - - elseif type(payload) ~= "string" then - payload = tostring(payload) - end + assert(type(payload) == 'string' and #payload <= max_payload_len, "无效的数据类型或长度超出预期") - local payload_len = #payload + local payload_len = #payload - if payload_len > max_payload_len then - return nil, "payload too big" + if opcode & 0x8 ~= 0 then + if payload_len > 125 then + return error("控制帧的有效载荷长度太多") end - - if opcode & 0x8 ~= 0 then - if payload_len > 125 then - return nil, "too much payload for control frame" - end - if not fin then - return nil, "fragmented control frame" - end + if not fin then + return error("畸形的控制帧") end + end - local frame, err = build_frame(fin, opcode, payload_len, payload, masking) - if not frame then - return nil, "failed to build frame: " .. err - end - return sock_send(sock, frame) + local frame, err = build_frame(fin, opcode, payload_len, payload, masking) + if not frame then + return error("错误的数据帧:"..err) + end + return sock_send(sock, frame) end return _M From dba1095f5aa2c4b633464102b77b6c33212380d6 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 22 Jun 2019 14:20:11 +0800 Subject: [PATCH 173/956] =?UTF-8?q?=E4=BC=98=E5=8C=96ws=E5=85=B3=E9=97=AD?= =?UTF-8?q?=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/websocket/server.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/protocol/websocket/server.lua b/lualib/protocol/websocket/server.lua index 544e0a5a..0da15d50 100644 --- a/lualib/protocol/websocket/server.lua +++ b/lualib/protocol/websocket/server.lua @@ -65,7 +65,7 @@ function websocket:close (data) end self.closed = true self:add_to_queue(function () - return _send_frame(self.sock, true, 0x8, char(((1000 >> 8) & 0xff), (1000 & 0xff))..data, self.max_payload_len, self.send_masked) + return _send_frame(self.sock, true, 0x8, char(((1000 >> 8) & 0xff), (1000 & 0xff))..(type(data) == 'string' and data or ""), self.max_payload_len, self.send_masked) end) self:add_to_queue(function () return self.sock:close() From c8a25401cda7633a6b36f20c80ff1bf8d36c5158 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 22 Jun 2019 14:22:35 +0800 Subject: [PATCH 174/956] =?UTF-8?q?=E4=BC=98=E5=8C=96mail=E5=BA=93?= =?UTF-8?q?=E5=B9=B6=E4=B8=94=E5=88=86=E7=A6=BB=E5=8D=8F=E8=AE=AE=E4=B8=8E?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E7=9A=84=E4=BB=A3=E7=A0=81,=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E5=8F=91=E9=80=81=E6=B5=81=E7=A8=8B=E5=B9=B6=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E4=B8=80=E4=B8=AA=E5=8F=AF=E8=83=BD=E5=AD=98=E5=9C=A8?= =?UTF-8?q?=E7=9A=84=E6=95=B0=E6=8D=AE=E5=8C=85=E6=A0=BC=E5=BC=8F=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E7=9A=84=E9=97=AE=E9=A2=98.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/mail/init.lua | 185 +++---------------------- lualib/mail/socket.lua | 44 ------ lualib/protocol/smtp/init.lua | 248 ++++++++++++++++++++++++++++++++++ 3 files changed, 269 insertions(+), 208 deletions(-) delete mode 100644 lualib/mail/socket.lua create mode 100644 lualib/protocol/smtp/init.lua diff --git a/lualib/mail/init.lua b/lualib/mail/init.lua index 1f1d9807..a274858d 100644 --- a/lualib/mail/init.lua +++ b/lualib/mail/init.lua @@ -1,31 +1,9 @@ -local crypt = require "crypt" -local base64encode = crypt.base64encode - -local socket = require "mail.socket" -local connect = socket.connect -local close = socket.close -local send = socket.send -local recv = socket.recv +local smtp = require "protocol.smtp" local tonumber = tonumber -local tostring = tostring local match = string.match -local fmt = string.format local os_date = os.date -local MAX_PACKET_SIZE = 1024 - -local mail = {} - -local function read_packet(str) - local str_code, err = match(str, "(%d+) (.+)\r\n") - local code = tonumber(str_code) - if not code then - return - end - return code, err -end - local function check_mail(mail) if match(mail, '.+@.+') then return true @@ -37,124 +15,7 @@ local function time() return os_date("[%Y/%m/%d %H:%M:%S]") end --- HELO 命令 -local function HELO_PACKET(session, SSL) - local code, data, err - data, err = recv(session, MAX_PACKET_SIZE, SSL) - if not data then - return nil, err - end - code, err = read_packet(data) - if not code then - return nil, time().."[HELO ERROR]: 不支持的协议." - end - -- 发送HELO命令 - send(session, "HELO CoreFramework(lua)/0.1\r\n", SSL) - -- 发送HELO命令 - data, err = recv(session, MAX_PACKET_SIZE, SSL) - if not data then - return nil, err - end - code, err = read_packet(data) - if code ~= 250 and code ~= 220 then - return nil, time()..'[HELO ERROR]: ' .. tostring(err) or '服务器关闭了连接.' - end - return true -end - --- 验证登录 -local function AUTH_PACKET(session, username, password, SSL) - local code, data, err - send(session, "AUTH LOGIN\r\n", SSL) - data, err = recv(session, MAX_PACKET_SIZE, SSL) - if not data then - return nil, time()..'[AUTH LOGIN ERROR]: 1.' .. tostring(err) or '服务器关闭了连接. ' - end - code, err = read_packet(data) - if not code or code ~= 334 then - return nil, time()..'[AUTH LOGIN ERROR]: 1. 验证失败('.. tostring(code) .. (err or '未知错误') ..')' - end - -- 发送base64用户名 - send(session, base64encode(username)..'\r\n', SSL) - data, err = recv(session, MAX_PACKET_SIZE, SSL) - if not data then - return nil, time()..'[AUTH LOGIN ERROR]: 2.' .. tostring(err) or '服务器关闭了连接.' - end - code, err = read_packet(data) - if not code or code ~= 334 then - return nil, time()..'[AUTH LOGIN ERROR]: 2. 验证失败('.. tostring(code) .. (err or '未知错误') ..')' - end - -- 发送base64密码 - send(session, base64encode(password)..'\r\n', SSL) - data, err = recv(session, MAX_PACKET_SIZE, SSL) - if not data then - return nil, time()..'[AUTH LOGIN ERROR]: 3.' .. tostring(err) or '服务器关闭了连接.' - end - code, err = read_packet(data) - if not code or code ~= 235 then - return nil, time()..'[AUTH LOGIN ERROR]: 3. 验证失败('.. tostring(code) .. (err or '未知错误') ..')' - end - return code, err -end - --- 发送邮件头部 -local function MAIL_HEADER(session, from, to, SSL) - local code, data, err - -- 邮件发送者 - send(session, fmt("MAIL FROM:<%s>\r\n", from), SSL) - data, err = recv(session, MAX_PACKET_SIZE, SSL) - if not data then - return nil, time()..'[MAIL FROM ERROR]: ' .. tostring(err) or '服务器关闭了连接. ' - end - code, err = read_packet(data) - if not code or code ~= 250 then - return nil, time()..'[MAIL FROM ERROR]: ('.. tostring(code) .. (err or '未知错误') ..')' - end - -- 邮件接收者 - send(session, fmt("RCPT TO:<%s>\r\n", to), SSL) - data, err = recv(session, MAX_PACKET_SIZE, SSL) - if not data then - return nil, time()..'[RCPT TO ERROR]: ' .. tostring(err) or '服务器关闭了连接. ' - end - code, err = read_packet(data) - if not code or code ~= 250 then - return nil, time()..'[RCPT TO ERROR]: ('.. tostring(code) .. (err or '未知错误') ..')' - end - return true -end - --- 发送邮件内容 -local function MAIL_CONTENT(session, from, to, subject, mime, content, SSL) - local code, data, err - -- DATA命令, 开始发送邮件实体 - send(session, "DATA\r\n", SSL) - data, err = recv(session, MAX_PACKET_SIZE, SSL) - if not data then - return nil, time()..'[MAIL CONTENT ERROR]: ' .. tostring(err) or '服务器关闭了连接. ' - end - code, err = read_packet(data) - if not code or code ~= 354 then - return nil, time()..'[MAIL CONTENT ERROR]: ('.. tostring(code) .. (err or '未知错误') ..')' - end - local FROM = fmt("from:<%s>\r\n", from) - local TO = fmt("to:<%s>\r\n", to) - local SUBJECT = fmt("subject:%s\r\n", subject) - if mime and mime == 'html' then - mime = 'Content-Type: text/html\r\n' - else - mime = '' - end - send(session, FROM..TO..SUBJECT..mime..'\r\n'..content..'\r\n\r\n.\r\n', SSL) - data, err = recv(session, MAX_PACKET_SIZE, SSL) - if not data then - return nil, time()..'[MAIL CONTENT ERROR]: ' .. tostring(err) or '服务器关闭了连接. ' - end - code, err = read_packet(data) - if not code or code ~= 250 then - return nil, time()..'[MAIL CONTENT ERROR]: ('.. tostring(code) .. (err or '未知错误') ..')' - end - return true -end +local mail = {} function mail.send(opt) local ok, session, err @@ -176,37 +37,33 @@ function mail.send(opt) if not opt.content or opt.content == '' then return nil, "邮件内容为空, 请检查配置参数" end - -- 连接邮件服务器并且返回tcp session - session, err = connect(opt.host, opt.port, opt.SSL) - if not session then - return nil, err - end - -- HELO 命令 - ok, err = HELO_PACKET(session, opt.SSL) + + local s = smtp:new(opt):set_timeout(15) + + -- 尝试连接服务器 + local ok, err = s:connect() if not ok then - close(session) - return nil, err + return nil, err, s:close() end - -- AUTH LOGIN 命令 - ok, err = AUTH_PACKET(session, opt.username, opt.password, opt.SSL) + -- print("连接成功") + -- 尝试握手包 + local ok, err = s:hello_packet() if not ok then - close(session) - return nil, err + return nil, err, s:close() end - -- HEADER 头部 命令 1 - ok, err = MAIL_HEADER(session, opt.from, opt.to, opt.SSL) + -- print("握手成功") + -- 身份认证 + local ok, err = s:auth_packet() if not ok then - close(session) - return nil, err + return nil, err, s:close() end - -- HEADER 头部 命令 1 + Content-Type + Content Body内容 - ok, err = MAIL_CONTENT(session, opt.from, opt.to, opt.subject, opt.mime, opt.content, opt.SSL) + -- print("认证成功") + -- 发送数据 + local ok, err = s:send_mail() if not ok then - close(session) - return nil, err + return nil, err, s:close() end - close(session) - return ok, time()..": 邮件发送成功!" + return ok, time()..": 邮件发送成功!", s:close() end diff --git a/lualib/mail/socket.lua b/lualib/mail/socket.lua deleted file mode 100644 index 32b766ff..00000000 --- a/lualib/mail/socket.lua +++ /dev/null @@ -1,44 +0,0 @@ -local TCP = require "internal.TCP" - -local socket = {} --- hook connect 与 ssl connect -function socket.connect(host, port, SSL) - local session = TCP:new() - if not SSL then - local ok, err = session:connect(host, port) - if not ok then - session:close() - return ok, err - end - else - local ok, err = session:ssl_connect(host, port) - if not ok then - session:close() - return ok, err - end - end - return session -end - --- hook read 与 ssl read -function socket.recv(session, bytes, SSL) - if not SSL then - return session:recv(bytes) - end - return session:ssl_recv(bytes) -end - --- hook send 与 ssl send -function socket.send(session, buf, SSL) - if not SSL then - return session:send(buf) - end - return session:ssl_send(buf) -end - --- hook close session -function socket.close(session) - return session:close() -end - -return socket diff --git a/lualib/protocol/smtp/init.lua b/lualib/protocol/smtp/init.lua new file mode 100644 index 00000000..ed9206db --- /dev/null +++ b/lualib/protocol/smtp/init.lua @@ -0,0 +1,248 @@ +local class = require "class" +local tcp = require "internal.TCP" + +local crypt = require "crypt" +local base64encode = crypt.base64encode + +local type = type +local tonumber = tonumber +local tostring = tostring +local match = string.match +local fmt = string.format +local os_date = os.date +local concat = table.concat + +local MAX_PACKET_SIZE = 1024 + +local function read_packet(str) + local str_code, err = match(str, "(%d+) (.+)\r\n") + local code = tonumber(str_code) + if not code then + return + end + return code, err +end + +local function time() + return os_date("[%Y/%m/%d %H:%M:%S]") +end + +local smtp = class("smtp") + +function smtp:ctor (opt) + self.ssl = opt.SSL + self.host = opt.host + self.port = opt.port + self.to = opt.to + self.from = opt.from + self.mime = opt.mime + self.subject = opt.subject + self.content = opt.content + self.username = opt.username + self.password = opt.password + self.sock = tcp:new() +end + +-- 发送握手包 +function smtp:hello_packet () + local code, data, err + data, err = self:recv(MAX_PACKET_SIZE) + if not data then + return nil, err + end + code, err = read_packet(data) + if not code then + return nil, time().."[HELO ERROR]: 不支持的协议." + end + -- 发送HELO命令 + local ok = self:send("HELO cf_smtp/0.1\r\n") + if not ok then + return nil, time().."[HELO ERROR]: 发送HELO失败." + end + -- 接收HELO回应 + data, err = self:recv(MAX_PACKET_SIZE) + if not data then + return nil, err + end + code, err = read_packet(data) + if code ~= 250 and code ~= 220 then + return nil, time()..'[HELO ERROR]: ' .. tostring(err) or '服务器关闭了连接.' + end + return true +end + +-- 登录认证 +function smtp:auth_packet () + local code, data, err + local ok = self:send("AUTH LOGIN\r\n") + if not ok then + return nil, "AUTH LOGIN ERROR]: 发送AUTH LOGIN失败" + end + data, err = self:recv(MAX_PACKET_SIZE) + if not data then + return nil, time()..'[AUTH LOGIN ERROR]: 1.' .. tostring(err) or '服务器关闭了连接. ' + end + code, err = read_packet(data) + if not code or code ~= 334 then + return nil, time()..'[AUTH LOGIN ERROR]: 1. 验证失败('.. tostring(code) .. (err or '未知错误') ..')' + end + -- 发送base64用户名 + local ok = self:send(base64encode(self.username)..'\r\n') + if not ok then + return nil, "[AUTH LOGIN ERROR]: 发送username失败" + end + data, err = self:recv(MAX_PACKET_SIZE) + if not data then + return nil, time()..'[AUTH LOGIN ERROR]: 2.' .. tostring(err) or '服务器关闭了连接.' + end + code, err = read_packet(data) + if not code or code ~= 334 then + return nil, time()..'[AUTH LOGIN ERROR]: 2. 验证失败('.. tostring(code) .. (err or '未知错误') ..')' + end + -- 发送base64密码 + local ok = self:send(base64encode(self.password)..'\r\n') + if not ok then + return nil, "[AUTH LOGIN ERROR]: 发送password失败" + end + data, err = self:recv(MAX_PACKET_SIZE) + if not data then + return nil, time()..'[AUTH LOGIN ERROR]: 3.' .. tostring(err) or '服务器关闭了连接.' + end + code, err = read_packet(data) + if not code or code ~= 235 then + return nil, time()..'[AUTH LOGIN ERROR]: 3. 验证失败('.. tostring(code) .. (err or '未知错误') ..')' + end + return code, err +end + +-- 发送邮件头部 +function smtp:send_header () + local code, data, err + -- 邮件发送者 + local ok = self:send(fmt("MAIL FROM:<%s>\r\n", self.from)) + if not ok then + return nil, "[MAIL FROM ERROR]: 发送FROM失败" + end + data, err = self:recv(MAX_PACKET_SIZE) + if not data then + return nil, time()..'[MAIL FROM ERROR]: ' .. tostring(err) or '服务器关闭了连接. ' + end + code, err = read_packet(data) + if not code or code ~= 250 then + return nil, time()..'[MAIL FROM ERROR]: ('.. tostring(code) .. (err or '未知错误') ..')' + end + -- 邮件接收者 + local ok = self:send(fmt("RCPT TO:<%s>\r\n", self.to)) + if not ok then + return nil, "[MAIL FROM ERROR]: 发送TO失败" + end + data, err = self:recv(MAX_PACKET_SIZE) + if not data then + return nil, time()..'[RCPT TO ERROR]: ' .. tostring(err) or '服务器关闭了连接. ' + end + code, err = read_packet(data) + if not code or code ~= 250 then + return nil, time()..'[RCPT TO ERROR]: ('.. tostring(code) .. (err or '未知错误') ..')' + end + return true +end + +-- 发送邮件内容 +function smtp:send_content () + local code, data, err + -- DATA命令, 开始发送邮件实体 + local ok = self:send("DATA\r\n") + if not ok then + return nil, "[MAIL CONTENT ERROR]: 发送DATA失败" + end + data, err = self:recv(MAX_PACKET_SIZE) + if not data then + return nil, time()..'[MAIL CONTENT ERROR]: ' .. tostring(err) or '服务器关闭了连接. ' + end + code, err = read_packet(data) + if not code or code ~= 354 then + return nil, time()..'[MAIL CONTENT ERROR]: ('.. tostring(code) .. (err or '未知错误') ..')' + end + local FROM = fmt("from:<%s>\r\n", self.from) + local TO = fmt("to:<%s>\r\n", self.to) + local SUBJECT = fmt("subject:%s\r\n", self.subject) + if self.mime and self.mime == 'html' then + self.mime = concat({ + "Content-Type: text/html; charset=utf-8", + "Content-Transfer-Encoding:base64\r\n" + }, '\r\n') + else + self.mime = concat({ + "Content-Type: text/plain; charset=utf-8", + "Content-Transfer-Encoding:base64\r\n" + }, '\r\n') + end + local ok = self:send(FROM..TO..SUBJECT..self.mime..'\r\n'..base64encode(self.content)..'\r\n\r\n.\r\n') + if not ok then + return nil, "[MAIL CONTENT ERROR]: 发送Content失败" + end + data, err = self:recv(MAX_PACKET_SIZE) + if not data then + return nil, time()..'[MAIL CONTENT ERROR]: ' .. tostring(err) or '服务器关闭了连接. ' + end + code, err = read_packet(data) + if not code or code ~= 250 then + return nil, time()..'[MAIL CONTENT ERROR]: ('.. tostring(code) .. (err or '未知错误') ..')' + end + return true +end + +function smtp:send_mail () + local ok, err = self:send_header() + if not ok then + return ok, err + end + local ok, err = self:send_content() + if not ok then + return ok, err + end + return true +end + +-- 超时时间 +function smtp:set_timeout (timeout) + if type(timeout) == number and number > 0 then + self.timeout = timeout + end + return self +end + + +-- 连接到smtp服务器 +function smtp:connect () + self.sock:timeout(self.timeout or 15) + if self.ssl then + return self.sock:ssl_connect(self.host, self.port) + end + return self.sock:connect(self.host, self.port) +end + +-- 接收数据 +function smtp:recv (bytes) + if self.ssl then + return self.sock:ssl_recv(bytes) + end + return self.sock:recv(bytes) +end + +-- 发送数据 +function smtp:send (data) + if self.ssl then + return self.sock:ssl_send(data) + end + return self.sock:send(data) +end + +function smtp:close () + if self.sock then + self.sock:close() + self.sock = nil + end +end + +return smtp From 9766a3190654a365ed26b91e338d9c3248806235 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 22 Jun 2019 14:56:07 +0800 Subject: [PATCH 175/956] =?UTF-8?q?=E5=88=86=E7=A6=BBmail(smtp)=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E7=9A=84header=E4=B8=8Ebody=E5=8F=91=E9=80=81,=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=A4=A7=E9=83=A8=E5=88=86=E4=B8=AD=E6=96=87?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=E4=B8=8E=E4=BC=98=E5=8C=96=E6=B5=81=E7=A8=8B?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/smtp/init.lua | 45 +++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/lualib/protocol/smtp/init.lua b/lualib/protocol/smtp/init.lua index ed9206db..39d97f2e 100644 --- a/lualib/protocol/smtp/init.lua +++ b/lualib/protocol/smtp/init.lua @@ -30,7 +30,7 @@ end local smtp = class("smtp") function smtp:ctor (opt) - self.ssl = opt.SSL + self.ssl = opt.SSL or opt.ssl self.host = opt.host self.port = opt.port self.to = opt.to @@ -45,6 +45,7 @@ end -- 发送握手包 function smtp:hello_packet () + -- 接收服务端信息 local code, data, err data, err = self:recv(MAX_PACKET_SIZE) if not data then @@ -74,6 +75,7 @@ end -- 登录认证 function smtp:auth_packet () local code, data, err + -- 发送登录认证请求 local ok = self:send("AUTH LOGIN\r\n") if not ok then return nil, "AUTH LOGIN ERROR]: 发送AUTH LOGIN失败" @@ -118,8 +120,8 @@ end -- 发送邮件头部 function smtp:send_header () local code, data, err - -- 邮件发送者 - local ok = self:send(fmt("MAIL FROM:<%s>\r\n", self.from)) + -- 发送邮件来源 + local ok = self:send(fmt("MAIL FROM: <%s>\r\n", self.from)) if not ok then return nil, "[MAIL FROM ERROR]: 发送FROM失败" end @@ -131,8 +133,8 @@ function smtp:send_header () if not code or code ~= 250 then return nil, time()..'[MAIL FROM ERROR]: ('.. tostring(code) .. (err or '未知错误') ..')' end - -- 邮件接收者 - local ok = self:send(fmt("RCPT TO:<%s>\r\n", self.to)) + -- 发送邮件接收者 + local ok = self:send(fmt("RCPT TO: <%s>\r\n", self.to)) if not ok then return nil, "[MAIL FROM ERROR]: 发送TO失败" end @@ -150,7 +152,7 @@ end -- 发送邮件内容 function smtp:send_content () local code, data, err - -- DATA命令, 开始发送邮件实体 + -- 发送DATA命令, 开始发送邮件实体 local ok = self:send("DATA\r\n") if not ok then return nil, "[MAIL CONTENT ERROR]: 发送DATA失败" @@ -163,23 +165,26 @@ function smtp:send_content () if not code or code ~= 354 then return nil, time()..'[MAIL CONTENT ERROR]: ('.. tostring(code) .. (err or '未知错误') ..')' end - local FROM = fmt("from:<%s>\r\n", self.from) - local TO = fmt("to:<%s>\r\n", self.to) - local SUBJECT = fmt("subject:%s\r\n", self.subject) if self.mime and self.mime == 'html' then - self.mime = concat({ - "Content-Type: text/html; charset=utf-8", - "Content-Transfer-Encoding:base64\r\n" - }, '\r\n') + self.mime = "MIME-Version: 1.0\r\nContent-Type: text/html; charset=utf-8\r\nContent-Transfer-Encoding: base64\r\n" else - self.mime = concat({ - "Content-Type: text/plain; charset=utf-8", - "Content-Transfer-Encoding:base64\r\n" - }, '\r\n') - end - local ok = self:send(FROM..TO..SUBJECT..self.mime..'\r\n'..base64encode(self.content)..'\r\n\r\n.\r\n') + self.mime = "MIME-Version: 1.0\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Transfer-Encoding: base64\r\n" + end + -- 发送邮件实体头部 + local ok = self:send(concat({ + fmt("From: <%s>\r\n", self.from), + fmt("To: <%s>\r\n", self.to), + fmt("Subject: %s\r\n", self.subject), + self.mime, + '\r\n' + })) + if not ok then + return nil, "[MAIL CONTENT ERROR]: 发送Content Headers失败." + end + -- 发送邮件实体内容 + local ok = self:send(base64encode(self.content)..'\r\n\r\n.\r\n') if not ok then - return nil, "[MAIL CONTENT ERROR]: 发送Content失败" + return nil, "[MAIL CONTENT ERROR]: 发送Content Body失败." end data, err = self:recv(MAX_PACKET_SIZE) if not data then From a233b7c258b0ece0fa7b11120e80570e25ef8530 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 26 Jun 2019 02:39:19 +0800 Subject: [PATCH 176/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_ev.h | 1 + src/core_sys.c | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/core_ev.h b/src/core_ev.h index c4b4a269..e06d14ac 100644 --- a/src/core_ev.h +++ b/src/core_ev.h @@ -30,6 +30,7 @@ /* eventfd 与 signalfd */ #if defined(__linux) || defined(__linux__) + #define EV_USE_LINUXAIO 1 #define EV_USE_EPOLL 1 #define EV_USE_INOTIFY 1 #define EV_USE_SIGNALFD 1 diff --git a/src/core_sys.c b/src/core_sys.c index a31ae86f..f8308163 100644 --- a/src/core_sys.c +++ b/src/core_sys.c @@ -10,15 +10,15 @@ now(){ int /* 此方法可用于检查是否为有效ipv4地址*/ ipv4(const char *IP){ if (!IP) return 0; - struct in_addr addr; - if (inet_pton(AF_INET, IP, &addr) == 1) return 1; - return 0; + struct in_addr addr; + if (inet_pton(AF_INET, IP, &addr) == 1) return 1; + return 0; } int /* 此方法可用于检查是否为有效ipv6地址*/ ipv6(const char *IP){ if (!IP) return 0; - struct in6_addr addr; - if (inet_pton(AF_INET6, IP, &addr) == 1) return 1; - return 0; -} \ No newline at end of file + struct in6_addr addr; + if (inet_pton(AF_INET6, IP, &addr) == 1) return 1; + return 0; +} From 03c2bf376037120982ca9ee0cb86f7f6846fffa6 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 26 Jun 2019 05:55:41 +0800 Subject: [PATCH 177/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=E8=A2=AB=E4=BC=98=E5=8C=96=E5=90=8E?= =?UTF-8?q?=E7=9A=84=E9=9A=90=E8=97=8F=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/sha256.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/luaclib/src/lcrypt/sha256.c b/luaclib/src/lcrypt/sha256.c index b172ab05..30ed69b0 100644 --- a/luaclib/src/lcrypt/sha256.c +++ b/luaclib/src/lcrypt/sha256.c @@ -34,7 +34,7 @@ static const WORD K[64] = { 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 }; - +static inline void sha256_transform(SHA256_CTX *ctx, const BYTE data[]) { WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; @@ -77,6 +77,7 @@ void sha256_transform(SHA256_CTX *ctx, const BYTE data[]) ctx->state[7] += h; } +static inline void sha256_init(SHA256_CTX *ctx) { ctx->datalen = 0; @@ -91,6 +92,7 @@ void sha256_init(SHA256_CTX *ctx) ctx->state[7] = 0x5be0cd19; } +static inline void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len) { WORD i; @@ -108,6 +110,7 @@ void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len) } } +static inline void sha256_final(SHA256_CTX *ctx, BYTE hash[]) { WORD i; @@ -155,10 +158,10 @@ void sha256_final(SHA256_CTX *ctx, BYTE hash[]) } static inline -void sha256(SHA256_CTX *ctx, const char *buffer, size_t sz, char* hash){ - sha256_init(&ctx); - sha256_update(&ctx, buffer, sz); - sha256_final(&ctx, hash); +void sha256(SHA256_CTX *ctx, const uint8_t *buffer, size_t sz, uint8_t* hash){ + sha256_init(ctx); + sha256_update(ctx, buffer, sz); + sha256_final(ctx, hash); } LUAMOD_API int From dd1e6eaa6ab72cb4f7a161b43b810030901c4241 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 26 Jun 2019 10:00:23 +0800 Subject: [PATCH 178/956] =?UTF-8?q?=E4=BC=98=E5=8C=96makefile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcjson/Makefile | 2 +- luaclib/src/lcrypt/Makefile | 2 +- luaclib/src/lhttpparser/Makefile | 2 +- luaclib/src/lpeg/makefile | 5 +++-- src/Makefile | 10 ++++------ 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/luaclib/src/lcjson/Makefile b/luaclib/src/lcjson/Makefile index d6efdca5..57d53911 100644 --- a/luaclib/src/lcjson/Makefile +++ b/luaclib/src/lcjson/Makefile @@ -13,7 +13,7 @@ CFLAGS = -O3 -Wall -DNDEBUG -fPIC CC = cc INCLUDE = -I/usr/local/include -LIB = -L/usr/local/lib +LIB = -L/usr/local/lib -L../ -L../../../ fpconv.o: fpconv.c fpconv.h strbuf.o: strbuf.c strbuf.h diff --git a/luaclib/src/lcrypt/Makefile b/luaclib/src/lcrypt/Makefile index fd6b041d..cbeee6e7 100644 --- a/luaclib/src/lcrypt/Makefile +++ b/luaclib/src/lcrypt/Makefile @@ -14,7 +14,7 @@ CFLAGS = -O3 -w -shared -fPIC -DNDEBUG DLL = -lcore INCLUDE = -I/usr/local/include -LIB = -L/usr/local/lib +LIB = -L/usr/local/lib -L../ -L../../../ build: $(CC) -o lcrypt.so lcrypt.c crc.c sha1.c sha256.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) diff --git a/luaclib/src/lhttpparser/Makefile b/luaclib/src/lhttpparser/Makefile index 3f71bfe3..c2171874 100644 --- a/luaclib/src/lhttpparser/Makefile +++ b/luaclib/src/lhttpparser/Makefile @@ -18,7 +18,7 @@ CFLAGS = -O3 -w -shared -fPIC -DNDEBUG DLL = -lcore INCLUDE = -I/usr/local/include -LIB = -L/usr/local/lib +LIB = -L/usr/local/lib -L../ -L../../../ httpparser.o: httpparser.c httpparser.h lhttpparser.o: lhttpparser.c httpparser.h diff --git a/luaclib/src/lpeg/makefile b/luaclib/src/lpeg/makefile index da23cafc..64f56630 100644 --- a/luaclib/src/lpeg/makefile +++ b/luaclib/src/lpeg/makefile @@ -33,6 +33,7 @@ CWARNS = -Wall -Wextra -pedantic \ CFLAGS = $(CWARNS) $(COPT) -std=c99 -I$(LUADIR) -fPIC CC = gcc +LIBS = -L/usr/local/lib -L../ -L../../../ FILES = lpvm.o lpcap.o lptree.o lpcode.o lpprint.o @@ -45,13 +46,13 @@ macosx: $(MAKE) lpeg.so "DLLFLAGS = -bundle -undefined dynamic_lookup" build: $(FILES) - $(CC) -o lpeg.so $(FILES) -shared -fPIC -lcore + $(CC) -o lpeg.so $(FILES) -shared -fPIC $(LIBS) -lcore mv *.so ../../ rm -rf *.o *.so rebuild: $(FILES) - $(CC) -o lpeg.so $(FILES) -shared -fPIC -lcore + $(CC) -o lpeg.so $(FILES) -shared -fPIC $(LIBS) -lcore mv *.so ../../ rm -rf *.o *.so diff --git a/src/Makefile b/src/Makefile index 51142951..e6060800 100644 --- a/src/Makefile +++ b/src/Makefile @@ -9,7 +9,7 @@ default : CC = cc -LIBS += -L/usr/local/lib +LIBS += -L./ -L/usr/local/lib INCLUDES += -I/usr/local/include # 使用jemalloc内存分配器请启用这段 @@ -31,11 +31,9 @@ build : $(CC) -o libcore.so core.c core_ev.c core_memory.c core_sys.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) - mv *.so /usr/local/lib - $(CC) -o cfadmin core_start.c $(MACRO) $(INCLUDES) $(LIBS) -lcore -ldl - mv cfadmin ../ + mv cfadmin ../ && mv *.so ../ rebuild: @@ -43,9 +41,9 @@ rebuild: $(CC) -o libcore.so core.c core_ev.c core_memory.c core_sys.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) - mv *.so /usr/local/lib + $(CC) -o cfadmin core_start.c $(MACRO) $(INCLUDES) $(LIBS) -lcore -ldl - $(CC) -o cfadmin core_start.c core.c $(MACRO) $(INCLUDES) $(LIBS) -lcore -ldl + mv *.so ../ mv cfadmin ../ From c778c08172d86c58195a907631754b69bf640cb6 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 26 Jun 2019 15:49:40 +0800 Subject: [PATCH 179/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0QQ=E7=BE=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f2be6a9e..3b1e4c65 100644 --- a/README.md +++ b/README.md @@ -62,9 +62,11 @@ ## 联系作者 -> [issues](https://github.com/CandyMi/core_framework/issues) +> [issues](https://github.com/CandyMi/core_framework/issues) -> 作者邮箱 +> 作者邮箱 + +> QQ群:**727531854** ## 支持 From 32c70619d73f4d405f682582630ca08b82f18c2f Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 28 Jun 2019 20:21:20 +0800 Subject: [PATCH 180/956] =?UTF-8?q?=E4=BC=98=E5=8C=96HTTP=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http.lua | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index fa510d9c..af980c3a 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -35,14 +35,11 @@ local PARSER_HTTP_RESPONSE = httpparser.parser_http_response local RESPONSE_CHUNKED_PARSER = httpparser.parser_response_chunked local type = type -local assert = assert -local setmetatable = setmetatable local tostring = tostring local next = next local pcall = pcall local ipairs = ipairs local time = os.time -local char = string.char local lower = string.lower local upper = string.upper local match = string.match @@ -631,11 +628,11 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) if Connection == 'Connection: keep-alive' then header[#header+1] = "Keep-Alive: timeout="..timeout end + http:tolog(statucode, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, now() - start) local ok = send_response(sock, header, body) if not ok then return sock:close() end - http:tolog(statucode, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, now() - start) if statucode ~= 200 or Connection ~= 'Connection: keep-alive' then return sock:close() end From 2f59bfa938e9a107aae15798ff5ccdc4d9713abb Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 29 Jun 2019 02:54:42 +0800 Subject: [PATCH 181/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=AE=80=E5=8D=95?= =?UTF-8?q?=E7=9A=84CSV=E6=96=87=E4=BB=B6=E8=AF=BB=E5=8F=96=E6=96=B9?= =?UTF-8?q?=E6=B3=95,=20=E5=A2=9E=E5=8A=A0csv=E6=B5=8B=E8=AF=95=E7=A4=BA?= =?UTF-8?q?=E4=BE=8B=E4=B8=8E=E6=B5=8B=E8=AF=95=E8=AF=BB=E5=8F=96=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Excel.csv | 4 ++++ lualib/csv.lua | 36 ++++++++++++++++++++++++++++++++++++ script/test_csv.lua | 5 +++++ 3 files changed, 45 insertions(+) create mode 100644 Excel.csv create mode 100644 lualib/csv.lua create mode 100644 script/test_csv.lua diff --git a/Excel.csv b/Excel.csv new file mode 100644 index 00000000..e7b258c7 --- /dev/null +++ b/Excel.csv @@ -0,0 +1,4 @@ +ID,Name,Des,Model, +1,广告,www.baidu.com,csv.png,1.1 +2,公告,www.notice.com,notice.png,2.1 +3,测试,www.test.com,test.png,3.1 diff --git a/lualib/csv.lua b/lualib/csv.lua new file mode 100644 index 00000000..2f78ab8f --- /dev/null +++ b/lualib/csv.lua @@ -0,0 +1,36 @@ +local csv = {} + +-- 读取并且解析CSV文件, 格式如下: +--[[ + [1] = [Name1, Name2, Nam3, ..., NameN] + [2] = [Value1, Value2, Value3, ..., ValueN] + . + .. + ... + [N] = [Value1, Value2, Value3, ..., ValueN] +]] +-- 规则1: 第一行为所有字段的名称, 第二行开始到第N行是内容; +-- 规则2: 每行不允许出现空格与逗号引号等特殊字符, 空格字符将会被删除掉; +-- 规则3: 打开csv文件失败将返回nil与一个err错误信息. +function csv.loadfile (path) + local file, err = io.open(path, "r") + if not file then + return nil, err + end + local tab = {} + for line in file:lines() do + local items = {} + for item in line:gmatch("([^,\r\n]+)") do + if item and item ~= '' then + items[#items + 1] = item:gsub("[ \"']", "") + end + end + if #items > 0 then + tab[#tab + 1] = items + end + end + file:close() + return tab +end + +return csv diff --git a/script/test_csv.lua b/script/test_csv.lua new file mode 100644 index 00000000..42aa5a8b --- /dev/null +++ b/script/test_csv.lua @@ -0,0 +1,5 @@ +local csv = require "csv" + +local Log = require("logging"):new() + +Log:DEBUG(csv.loadfile("../Excel.csv")) From 234bd216183679641980c657f604d698a750c7f3 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 29 Jun 2019 08:30:05 +0800 Subject: [PATCH 182/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0crypt=E5=BA=93hex?= =?UTF-8?q?=E5=8F=82=E6=95=B0,=20=E7=94=A8=E4=BA=8E=E8=B0=83=E8=AF=95?= =?UTF-8?q?=E4=B8=8E=E6=89=93=E5=8D=B016=E4=BD=8D=E5=8F=AF=E8=AF=BB?= =?UTF-8?q?=E5=AD=97=E7=AC=A6=E4=B8=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/crypt/init.lua | 72 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 12 deletions(-) diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index 360b2276..4ca95cfc 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -64,16 +64,40 @@ function crypt.sha256 (str, hex) return hash end -function crypt.xor_str (...) - return xor_str(...) +function crypt.xor_str (str, sec, hex) + local hash = xor_str(str, sec) + if hash and hex then + local tab = new_tab(#hash, 0) + for i = 1, #hash do + tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) + end + return concat(tab) + end + return hash end -function crypt.randomkey(...) - return randomkey(...) +function crypt.randomkey(hex) + local hash = randomkey() + if hash and hex then + local tab = new_tab(#hash, 0) + for i = 1, #hash do + tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) + end + return concat(tab) + end + return hash end -function crypt.hashkey (...) - return hashkey(...) +function crypt.hashkey (key, hex) + local hash = hashkey(key) + if hash and hex then + local tab = new_tab(#hash, 0) + for i = 1, #hash do + tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) + end + return concat(tab) + end + return hash end function crypt.hmac_sha1 (key, text, hex) @@ -100,16 +124,40 @@ function crypt.hmac_sha256 (key, text, hex) return hash end -function crypt.hmac_hash (...) - return hmac_hash(...) +function crypt.hmac_hash (key, text, hex) + local hash = hmac_hash(key, text) + if hash and hex then + local tab = new_tab(#hash, 0) + for i = 1, #hash do + tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) + end + return concat(tab) + end + return hash end -function crypt.hmac64 (...) - return hmac64(...) +function crypt.hmac64 (key, text, hex) + local hash = hmac64(key, text) + if hash and hex then + local tab = new_tab(#hash, 0) + for i = 1, #hash do + tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) + end + return concat(tab) + end + return hash end -function crypt.hmac64_md5 (...) - return hmac64_md5(...) +function crypt.hmac64_md5 (key, text, hex) + local hash = hmac64_md5(key, text) + if hash and hex then + local tab = new_tab(#hash, 0) + for i = 1, #hash do + tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) + end + return concat(tab) + end + return hash end function crypt.base64encode (...) From 1858813bbca8fa1045700b4f0cd99e93e0ef5c8c Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 29 Jun 2019 08:32:18 +0800 Subject: [PATCH 183/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8A=A0=E5=AF=86?= =?UTF-8?q?=E5=BA=93=E6=B5=8B=E8=AF=95=E4=BB=A3=E7=A0=81=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_crypt.lua | 51 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 script/test_crypt.lua diff --git a/script/test_crypt.lua b/script/test_crypt.lua new file mode 100644 index 00000000..8b981cec --- /dev/null +++ b/script/test_crypt.lua @@ -0,0 +1,51 @@ +local crypt = require "crypt" +local Log = require("logging"):new() + +-- 测试crc32编码 +Log:DEBUG("测试crc32",crypt.crc32("admin")) +-- 测试crc64编码 +Log:DEBUG("测试crc64", crypt.crc64("admin")) + +-- 测试hmac64编码 +Log:DEBUG("测试hmac64", crypt.hmac64("12345678", "abcdefgh", true)) + +-- 测试hmac64_md5编码 +Log:DEBUG("测试hmac64_md5", crypt.hmac64_md5("12345678", "abcdefgh", true)) + +-- 测试hmac_hash编码 +Log:DEBUG("测试hmac_hash", crypt.hmac_hash("12345678", "abcdefgh", true)) + +-- 测试randomkey编码 +Log:DEBUG("测试randomkey", crypt.randomkey(true)) + +-- 测试hashkey编码 +Log:DEBUG("测试hashkey", crypt.hashkey("admin", true)) + +-- 测试sha1编码, 第二个参数表示进行hex +Log:DEBUG("测试sha1", crypt.sha1("admin", true)) +-- 测试hmac_sha1编码, 第二个参数表示进行hex +Log:DEBUG("测试hmac_sha1", crypt.hmac_sha1("admin", "123", true)) + +-- 测试sha256编码, 第二个参数表示进行hex +Log:DEBUG("测试sha256", crypt.sha256("admin", true)) +-- 测试hmac_sha256编码, 第二个参数表示进行hex +Log:DEBUG("测试hmac_sha256", crypt.hmac_sha256("admin", "123", true)) + +-- 测试desencode编码 +Log:DEBUG("测试desencode", crypt.desencode("12345678", "87654321")) + +-- 测试desdecode编码 +Log:DEBUG("测试desdecode", crypt.desdecode("12345678", crypt.desencode("12345678", "87654321")) == "87654321") + +-- 测试hexencode编码 +Log:DEBUG("测试hexencode", crypt.hexencode("1234567890"), crypt.hexencode("1234567890") == "31323334353637383930") +-- 测试hexdecode编码 +Log:DEBUG("测试hexdecode", crypt.hexdecode("31323334353637383930"), crypt.hexdecode("31323334353637383930") == "1234567890") + +-- 测试base64encode编码 +Log:DEBUG("测试base64encode", crypt.base64encode("1234567890"), crypt.base64encode("1234567890") == "MTIzNDU2Nzg5MA==") +-- 测试base64decode编码 +Log:DEBUG("测试base64decode", crypt.base64decode("MTIzNDU2Nzg5MA=="), crypt.base64decode("MTIzNDU2Nzg5MA==") == "1234567890") + +-- 测试str_xor编码, 2次xor将还原. +Log:DEBUG("测试str_xor", crypt.xor_str("1234567890", "123", true), crypt.xor_str(crypt.xor_str("1234567890", "123"), "123") == "1234567890") From 4c914a98f3bc614c5fc2c210c26b849623a02e35 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 29 Jun 2019 22:33:51 +0800 Subject: [PATCH 184/956] =?UTF-8?q?=E5=88=A0=E9=99=A4conf.d=E5=BF=BD?= =?UTF-8?q?=E7=95=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e3ca5c97..80ced870 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ # Prerequisites -*.d +# *.d # Object files *.o From ef0a81ae7ea86aee044c9e1980e1251c5e6cc23f Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 29 Jun 2019 22:34:13 +0800 Subject: [PATCH 185/956] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=BC=82=E6=AD=A5?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E7=9A=84=E5=88=B7=E6=96=B0=E9=A2=91=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/logging/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua index b6c74142..f208eedc 100644 --- a/lualib/logging/init.lua +++ b/lualib/logging/init.lua @@ -26,7 +26,7 @@ local cf = require "cf" if io_type(io.output()) == 'file' then io.output():setvbuf("full", 1 << 20) - cf.at(0.2, function () + cf.at(0.3, function () return io_flush() -- 定期刷新缓冲, 减少日志缓冲频繁导致的性能问题 end) end From f24d88b5a4176d20471964e6f9579ad996a0417c Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 29 Jun 2019 22:34:45 +0800 Subject: [PATCH 186/956] =?UTF-8?q?=E8=A1=A5=E5=85=85=E7=BC=BA=E5=A4=B1?= =?UTF-8?q?=E7=9A=84=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/conf.d/nginx.conf | 66 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 docker/conf.d/nginx.conf diff --git a/docker/conf.d/nginx.conf b/docker/conf.d/nginx.conf new file mode 100644 index 00000000..469b57cb --- /dev/null +++ b/docker/conf.d/nginx.conf @@ -0,0 +1,66 @@ +worker_processes 1; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + + +events { + worker_connections 65535; +} + + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "req_time:[$request_time/Sec]" "upstream_time:[$upstream_response_time/Sec]" '; + + access_log /var/log/nginx/access.log main; + + #优化文件发送 + sendfile on; + #tcp_nopush on; + #关闭Nagle算法 + tcp_nodelay on; + + keepalive_timeout 65; + + gzip on; + gzip_vary on; + gzip_min_length 1k; + gzip_comp_level 9; + gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png; + gzip_disable "MSIE [1-6]\."; + + + upstream myweb { + server webapp1:8080; + server webapp2:8080; + server webapp3:8080; + } + + server { + listen 80; + #access_log /var/log/nginx/8080.log main; + + location /ws { + proxy_pass http://myweb/ws; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + location / { + proxy_pass http://myweb; + proxy_http_version 1.1; + proxy_ignore_client_abort on; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + } + +} From a1ee268a8febd44895836b2d8d74757a466627ec Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 29 Jun 2019 22:52:47 +0800 Subject: [PATCH 187/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=9D=9E=E6=B3=95PAT?= =?UTF-8?q?H=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Router.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index d42ce920..98e87ae8 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -110,7 +110,7 @@ local function find_route (method, path) return end -- 凡是找到'../'并且检查路径回退已经超出静态文件根目录返回404 - if find(path, '%.%./') and check_path_deep(tab) then + if find(path, '/../', 1, true) and check_path_deep(tab) then return end load_file = load_file or function (path) From 7088e5fe709fb78184f19f4550eda6cf61f9c8ae Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 1 Jul 2019 03:40:18 +0800 Subject: [PATCH 188/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0makefile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 17 +++++++++++++++++ src/Makefile | 4 ++-- 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100755 build.sh diff --git a/build.sh b/build.sh new file mode 100755 index 00000000..0c1883b4 --- /dev/null +++ b/build.sh @@ -0,0 +1,17 @@ +# 运行这个文件可以安装libev与lua +current=`pwd` + +mkdir build && cd build + +git clone https://github.com/CandyMi/lua -b v5.3.5 +git clone https://github.com/CandyMi/libev -b v4.25 + +cd ${current}/build/lua && + make all MYCFLAGS=-fPIC MYCFLAGS+=-DLUA_USE_DLOPEN SYSLIBS=-ldl && + cp lua.h luaconf.h lualib.h lauxlib.h /usr/local/include && cp liblua.* /usr/local/lib + +cd ${current}/build/libev && + sh autogen.sh && ./configure --prefix=/usr/local && + make && make install + +cd ${current} && sudo rm -rf build diff --git a/src/Makefile b/src/Makefile index e6060800..fad0d2d3 100644 --- a/src/Makefile +++ b/src/Makefile @@ -23,9 +23,9 @@ INCLUDES += -I/usr/local/include # MACRO += -w -Os -L./ -L../ -DTCMALLOC # 默认情况下使用系统内存分配器 -CFLAGS += -Wall -Os -fPIC --shared -fno-strict-aliasing +CFLAGS += -Wall -Os -fPIC --shared -fno-strict-aliasing -Wl,-rpath,/usr/local/lib DLL += -lev -llua -MACRO += -w -Os +MACRO += -w -Os -Wl,-rpath,./ build : From 681e7a6352092faea1ec14487fbec944a61a99e3 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 1 Jul 2019 03:46:19 +0800 Subject: [PATCH 189/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0makefile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Makefile | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/Makefile b/src/Makefile index fad0d2d3..cc935076 100644 --- a/src/Makefile +++ b/src/Makefile @@ -9,7 +9,7 @@ default : CC = cc -LIBS += -L./ -L/usr/local/lib +LIBS += -L. -L/usr/local/lib INCLUDES += -I/usr/local/include # 使用jemalloc内存分配器请启用这段 @@ -24,28 +24,24 @@ INCLUDES += -I/usr/local/include # 默认情况下使用系统内存分配器 CFLAGS += -Wall -Os -fPIC --shared -fno-strict-aliasing -Wl,-rpath,/usr/local/lib -DLL += -lev -llua +DLL += -lev -llua -ldl MACRO += -w -Os -Wl,-rpath,./ build : $(CC) -o libcore.so core.c core_ev.c core_memory.c core_sys.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) - $(CC) -o cfadmin core_start.c $(MACRO) $(INCLUDES) $(LIBS) -lcore -ldl + $(CC) -o cfadmin core_start.c $(MACRO) $(INCLUDES) $(LIBS) -lcore - mv cfadmin ../ && mv *.so ../ + mv *.so cfadmin ../ rebuild: - rm -rf *.o *.so - $(CC) -o libcore.so core.c core_ev.c core_memory.c core_sys.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) - $(CC) -o cfadmin core_start.c $(MACRO) $(INCLUDES) $(LIBS) -lcore -ldl - - mv *.so ../ + $(CC) -o cfadmin core_start.c $(MACRO) $(INCLUDES) $(LIBS) -lcore - mv cfadmin ../ + mv *.so cfadmin ../ clean : From ea5aa486211189f2517f8790058f285f8c590585 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 1 Jul 2019 04:30:09 +0800 Subject: [PATCH 190/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sh b/build.sh index 0c1883b4..f8e5b2fb 100755 --- a/build.sh +++ b/build.sh @@ -1,7 +1,7 @@ # 运行这个文件可以安装libev与lua current=`pwd` -mkdir build && cd build +rm -rf build && mkdir build && cd build git clone https://github.com/CandyMi/lua -b v5.3.5 git clone https://github.com/CandyMi/libev -b v4.25 @@ -14,4 +14,4 @@ cd ${current}/build/libev && sh autogen.sh && ./configure --prefix=/usr/local && make && make install -cd ${current} && sudo rm -rf build +cd ${current} && rm -rf build From 93526c706d0f9bcc2e0e91238ba0e0d71a313b6c Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 1 Jul 2019 04:31:09 +0800 Subject: [PATCH 191/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0luaclib=E7=BC=96?= =?UTF-8?q?=E8=AF=91=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/Makefile | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/luaclib/Makefile b/luaclib/Makefile index 40d3498b..54327178 100644 --- a/luaclib/Makefile +++ b/luaclib/Makefile @@ -8,36 +8,34 @@ default : @echo "=======================================" INCLUDES += -I/usr/local/include -LIBS += -L/usr/local/lib -L./ -L../ -# CFLAGS = -Wall -Os -fPIC --shared -I/usr/local/include -L./ -L../ -DJEMALLOC -ljemalloc -# CFLAGS = -Wall -Os -fPIC --shared -I/usr/local/include -L./ -L../ -DTCMALLOC -ltcmalloc +LIBS += -L./ -L../ -L/usr/local/lib +# CFLAGS = -Wall -Os -fPIC --shared -DJEMALLOC -ljemalloc +# CFLAGS = -Wall -Os -fPIC --shared -DTCMALLOC -ltcmalloc CFLAGS = -Wall -Os -fPIC --shared build : - rm -rf *.o *.so $(CC) -o sys.so src/lsys.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore - $(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore $(CC) -o udp.so src/ludp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore - $(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore $(CC) -o task.so src/ltask.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore + $(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore + $(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore ### 以下为内置第三方库编译位置 ### - cd src/lcrypt && rm -rf *.o *.so && make build + cd src/lpeg && rm -rf *.o *.so && make build # 增加lpeg库 + cd src/lcrypt && rm -rf *.o *.so && make build # 增加crypt库 + cd src/lcjson && rm -rf *.o *.so && make build # 增加cjson库 cd src/lhttpparser && rm -rf *.o *.so && make build # 增加PicoHTTPParser库 - cd src/lcjson && rm -rf *.o *.so && make build # 增加cjson库 - cd src/lpeg && rm -rf *.o *.so && make build # 增加lpeg库 rebuild : - rm -rf *.o *.so $(CC) -o sys.so src/lsys.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore - $(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore $(CC) -o udp.so src/ludp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore - $(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore $(CC) -o task.so src/ltask.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore + $(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore + $(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore ### 以下为内置第三方库编译位置 ### - cd src/lcrypt && rm -rf *.o *.so && make rebuild - cd src/lhttpparser && rm -rf *.o *.so && make rebuild # 增加PicoHTTPParser库 - cd src/lcjson && rm -rf *.o *.so && make rebuild # 增加cjson库 - cd src/lpeg && rm -rf *.o *.so && make rebuild # 增加lpeg库 + cd src/lpeg && rm -rf *.o *.so && make build # 增加lpeg库 + cd src/lcrypt && rm -rf *.o *.so && make build # 增加crypt库 + cd src/lcjson && rm -rf *.o *.so && make build # 增加cjson库 + cd src/lhttpparser && rm -rf *.o *.so && make build # 增加PicoHTTPParser库 clean : rm -rf *.so From 2528fe3b3b6dc83d2b1672b08e6d3b67853195d2 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 1 Jul 2019 04:43:14 +0800 Subject: [PATCH 192/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0Dockerfile=E5=AE=89?= =?UTF-8?q?=E8=A3=85=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/Dockerfile | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 38269742..e87e7dc3 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,22 +1,13 @@ FROM centos:7 MAINTAINER CandyMi "869646063@qq.com" -# 加入include与lib搜索路径 -ENV LD_LIBRARY_PATH /usr/local/lib:$LD_LIBRARY_PATH -ENV C_INCLUDE_PATH /usr/local/include:$C_INCLUDE_PATH - WORKDIR /root/download -COPY ./lua-5.3.5.tar.gz /root/download/lua.tar.gz -COPY ./libev-4.25.tar.gz /root/download/libev.tar.gz - RUN rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 \ - && yum install nc gcc file vim autoconf make git readline-devel openssl-devel -y \ - && tar zxvf lua.tar.gz && cd lua* && make linux MYCFLAGS=-fPIC && make install && cd .. \ - && tar zxvf libev.tar.gz && cd libev* && ./configure --prefix=/usr/local && make && make install \ + && yum install nc gcc file vim autoconf automake make libtool git readline-devel openssl-devel -y \ && rm -rf /roo/download /var/cache/yum \ && git clone https://github.com/CandyMi/core_framework /app \ - && cd /app && make build + && cd /app && sh build.sh && make build # 使用者可在启动容器时使用-v命令将您的代码目录直接挂载到/app/script目录进行调试操作 WORKDIR /app From 5726bb4b2d22b770a96134d07773af3c7bc22041 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 1 Jul 2019 06:45:17 +0800 Subject: [PATCH 193/956] =?UTF-8?q?=E8=B0=83=E6=95=B4makefile,=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E7=BC=96=E8=AF=91=E9=80=9F=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcjson/Makefile | 18 +++------ luaclib/src/lhttpparser/Makefile | 12 ++---- luaclib/src/lpeg/makefile | 64 +++----------------------------- src/Makefile | 4 +- 4 files changed, 17 insertions(+), 81 deletions(-) diff --git a/luaclib/src/lcjson/Makefile b/luaclib/src/lcjson/Makefile index 57d53911..6d07e11a 100644 --- a/luaclib/src/lcjson/Makefile +++ b/luaclib/src/lcjson/Makefile @@ -7,26 +7,20 @@ default : @echo "Please use 'make clean' command to clean all." @echo "=======================================" -# CFLAGS = -O3 -Wall -DNDEBUG -fPIC -DJEMALLOC -ljemalloc -# CFLAGS = -O3 -Wall -DNDEBUG -fPIC -DTCMALLOC -ltcmalloc -CFLAGS = -O3 -Wall -DNDEBUG -fPIC +# CFLAGS = -O3 -Wall -DNDEBUG -DJEMALLOC -ljemalloc +# CFLAGS = -O3 -Wall -DNDEBUG -DTCMALLOC -ltcmalloc +CFLAGS = -O3 -Wall -DNDEBUG CC = cc INCLUDE = -I/usr/local/include LIB = -L/usr/local/lib -L../ -L../../../ -fpconv.o: fpconv.c fpconv.h -strbuf.o: strbuf.c strbuf.h - -OBJS = fpconv.o strbuf.o - -build: $(OBJS) - $(CC) -o cjson.so lua_cjson.c $(OBJS) $(INCLUDE) $(LIB) $(CFLAGS) -shared -fPIC -lcore +build: + $(CC) -o cjson.so lua_cjson.c fpconv.c strbuf.c $(INCLUDE) $(LIB) $(CFLAGS) -shared -fPIC -lcore mv cjson.so ../../ rebuild: $(OBJS) - $(CC) -o cjson.so lua_cjson.c $(OBJS) $(INCLUDE) $(LIB) $(CFLAGS) -shared -fPIC -lcore - rm -rf *.o + $(CC) -o cjson.so lua_cjson.c fpconv.c strbuf.c $(INCLUDE) $(LIB) $(CFLAGS) -shared -fPIC -lcore mv cjson.so ../../ clean: diff --git a/luaclib/src/lhttpparser/Makefile b/luaclib/src/lhttpparser/Makefile index c2171874..891270e6 100644 --- a/luaclib/src/lhttpparser/Makefile +++ b/luaclib/src/lhttpparser/Makefile @@ -20,17 +20,13 @@ DLL = -lcore INCLUDE = -I/usr/local/include LIB = -L/usr/local/lib -L../ -L../../../ -httpparser.o: httpparser.c httpparser.h -lhttpparser.o: lhttpparser.c httpparser.h -OBJS = httpparser.o lhttpparser.o - -build: $(OBJS) - $(CC) -o httpparser.so $(OBJS) $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) +build: + $(CC) -o httpparser.so httpparser.c lhttpparser.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) mv httpparser.so ../../ -rebuild: $(OBJS) - $(CC) -o httpparser.so $(OBJS) $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) +rebuild: + $(CC) -o httpparser.so httpparser.c lhttpparser.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) mv httpparser.so ../../ clean: diff --git a/luaclib/src/lpeg/makefile b/luaclib/src/lpeg/makefile index 64f56630..424dd06a 100644 --- a/luaclib/src/lpeg/makefile +++ b/luaclib/src/lpeg/makefile @@ -7,70 +7,16 @@ default : @echo "Please use 'make clean' command to clean all." @echo "=======================================" -LIBNAME = lpeg -LUADIR = ../lua/ - -COPT = -O2 -DNDEBUG -# COPT = -g - -CWARNS = -Wall -Wextra -pedantic \ - -Waggregate-return \ - -Wcast-align \ - -Wcast-qual \ - -Wdisabled-optimization \ - -Wpointer-arith \ - -Wshadow \ - -Wsign-compare \ - -Wundef \ - -Wwrite-strings \ - -Wbad-function-cast \ - -Wdeclaration-after-statement \ - -Wmissing-prototypes \ - -Wnested-externs \ - -Wstrict-prototypes \ -# -Wunreachable-code \ - - -CFLAGS = $(CWARNS) $(COPT) -std=c99 -I$(LUADIR) -fPIC CC = gcc LIBS = -L/usr/local/lib -L../ -L../../../ -FILES = lpvm.o lpcap.o lptree.o lpcode.o lpprint.o - -# For Linux -linux: - $(MAKE) lpeg.so "DLLFLAGS = -shared -fPIC" - -# For Mac OS -macosx: - $(MAKE) lpeg.so "DLLFLAGS = -bundle -undefined dynamic_lookup" - -build: $(FILES) - $(CC) -o lpeg.so $(FILES) -shared -fPIC $(LIBS) -lcore +build: + $(CC) -o lpeg.so lpcap.c lpcode.c lpprint.c lptree.c lpvm.c -O2 -shared -fPIC $(LIBS) -lcore mv *.so ../../ - rm -rf *.o *.so - -rebuild: $(FILES) - $(CC) -o lpeg.so $(FILES) -shared -fPIC $(LIBS) -lcore +rebuild: + $(CC) -o lpeg.so lpcap.c lpcode.c lpprint.c lptree.c lpvm.c -O2 -shared -fPIC $(LIBS) -lcore mv *.so ../../ - rm -rf *.o *.so - - -lpeg.so: $(FILES) - env $(CC) $(DLLFLAGS) $(FILES) -o lpeg.so - -$(FILES): makefile - -test: test.lua re.lua lpeg.so - ./test.lua clean: - rm -f $(FILES) lpeg.so - - -lpcap.o: lpcap.c lpcap.h lptypes.h -lpcode.o: lpcode.c lptypes.h lpcode.h lptree.h lpvm.h lpcap.h -lpprint.o: lpprint.c lptypes.h lpprint.h lptree.h lpvm.h lpcap.h -lptree.o: lptree.c lptypes.h lpcap.h lpcode.h lptree.h lpvm.h lpprint.h -lpvm.o: lpvm.c lpcap.h lptypes.h lpvm.h lpprint.h lptree.h + rm -f *.so *.o diff --git a/src/Makefile b/src/Makefile index cc935076..43a418b7 100644 --- a/src/Makefile +++ b/src/Makefile @@ -23,9 +23,9 @@ INCLUDES += -I/usr/local/include # MACRO += -w -Os -L./ -L../ -DTCMALLOC # 默认情况下使用系统内存分配器 -CFLAGS += -Wall -Os -fPIC --shared -fno-strict-aliasing -Wl,-rpath,/usr/local/lib +CFLAGS += -Wall -O3 -fPIC --shared -fno-strict-aliasing -Wl,-rpath,/usr/local/lib DLL += -lev -llua -ldl -MACRO += -w -Os -Wl,-rpath,./ +MACRO += -w -O3 -Wl,-rpath,./ build : From 6905959fc758b9693568141894d49eb59ed37875 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 1 Jul 2019 07:22:50 +0800 Subject: [PATCH 194/956] =?UTF-8?q?=E8=B0=83=E6=95=B4makeifile=E9=80=82?= =?UTF-8?q?=E9=85=8Dtc=E4=B8=8Eje=E5=86=85=E5=AD=98=E5=88=86=E9=85=8D?= =?UTF-8?q?=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/Makefile | 6 +++--- src/Makefile | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/luaclib/Makefile b/luaclib/Makefile index 54327178..f3bb4db3 100644 --- a/luaclib/Makefile +++ b/luaclib/Makefile @@ -9,9 +9,9 @@ default : INCLUDES += -I/usr/local/include LIBS += -L./ -L../ -L/usr/local/lib -# CFLAGS = -Wall -Os -fPIC --shared -DJEMALLOC -ljemalloc -# CFLAGS = -Wall -Os -fPIC --shared -DTCMALLOC -ltcmalloc -CFLAGS = -Wall -Os -fPIC --shared +# CFLAGS = -Wall -O3 -fPIC --shared -DJEMALLOC -ljemalloc +# CFLAGS = -Wall -O3 -fPIC --shared -DTCMALLOC -ltcmalloc +CFLAGS = -Wall -O3 -fPIC --shared build : $(CC) -o sys.so src/lsys.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore diff --git a/src/Makefile b/src/Makefile index 43a418b7..b76205fb 100644 --- a/src/Makefile +++ b/src/Makefile @@ -13,14 +13,14 @@ LIBS += -L. -L/usr/local/lib INCLUDES += -I/usr/local/include # 使用jemalloc内存分配器请启用这段 -# CFLAGS += -Wall -Os -fPIC --shared -DJEMALLOC -fno-strict-aliasing -# DLL += -ljemalloc -lev -llua -# MACRO += -w -Os -L./ -L../ -DJEMALLOC +# CFLAGS += -Wall -Os -fPIC --shared -DJEMALLOC -fno-strict-aliasing -Wl,-rpath,/usr/local/lib +# DLL += -ljemalloc -lev -llua -ldl +# MACRO += -w -O3 -Wl,-rpath,./ -DJEMALLOC # 使用tcmalloc内存分配器请启用这段 -# CFLAGS += -Wall -Os -fPIC --shared -DTCMALLOC -fno-strict-aliasing -# DLL += -ltcmalloc -lev -llua -# MACRO += -w -Os -L./ -L../ -DTCMALLOC +# CFLAGS += -Wall -Os -fPIC --shared -DTCMALLOC -fno-strict-aliasing -Wl,-rpath,/usr/local/lib +# DLL += -ltcmalloc -lev -llua -ldl +# MACRO += -w -O3 -Wl,-rpath,./ -DTCMALLOC # 默认情况下使用系统内存分配器 CFLAGS += -Wall -O3 -fPIC --shared -fno-strict-aliasing -Wl,-rpath,/usr/local/lib From aa7f7b3ccebb61ae1496b1efd6815b168ddde129 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 1 Jul 2019 07:34:11 +0800 Subject: [PATCH 195/956] =?UTF-8?q?=E4=BC=98=E5=8C=96makefile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcjson/Makefile | 10 +++++----- luaclib/src/lcrypt/Makefile | 2 +- luaclib/src/lhttpparser/Makefile | 9 ++++----- luaclib/src/lpeg/makefile | 6 ++++-- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/luaclib/src/lcjson/Makefile b/luaclib/src/lcjson/Makefile index 6d07e11a..45bbae03 100644 --- a/luaclib/src/lcjson/Makefile +++ b/luaclib/src/lcjson/Makefile @@ -7,20 +7,20 @@ default : @echo "Please use 'make clean' command to clean all." @echo "=======================================" -# CFLAGS = -O3 -Wall -DNDEBUG -DJEMALLOC -ljemalloc -# CFLAGS = -O3 -Wall -DNDEBUG -DTCMALLOC -ltcmalloc -CFLAGS = -O3 -Wall -DNDEBUG +# CFLAGS = -O3 -Wall -DJEMALLOC -shared -fPIC -ljemalloc +# CFLAGS = -O3 -Wall -DTCMALLOC -shared -fPIC -ltcmalloc +CFLAGS = -O3 -Wall -shared -fPIC CC = cc INCLUDE = -I/usr/local/include LIB = -L/usr/local/lib -L../ -L../../../ build: - $(CC) -o cjson.so lua_cjson.c fpconv.c strbuf.c $(INCLUDE) $(LIB) $(CFLAGS) -shared -fPIC -lcore + $(CC) -o cjson.so lua_cjson.c fpconv.c strbuf.c $(INCLUDE) $(LIB) $(CFLAGS) -lcore mv cjson.so ../../ rebuild: $(OBJS) - $(CC) -o cjson.so lua_cjson.c fpconv.c strbuf.c $(INCLUDE) $(LIB) $(CFLAGS) -shared -fPIC -lcore + $(CC) -o cjson.so lua_cjson.c fpconv.c strbuf.c $(INCLUDE) $(LIB) $(CFLAGS) -lcore mv cjson.so ../../ clean: diff --git a/luaclib/src/lcrypt/Makefile b/luaclib/src/lcrypt/Makefile index cbeee6e7..61cc769b 100644 --- a/luaclib/src/lcrypt/Makefile +++ b/luaclib/src/lcrypt/Makefile @@ -9,7 +9,7 @@ default : CC = cc -CFLAGS = -O3 -w -shared -fPIC -DNDEBUG +CFLAGS = -O3 -w -shared -fPIC DLL = -lcore diff --git a/luaclib/src/lhttpparser/Makefile b/luaclib/src/lhttpparser/Makefile index 891270e6..89c4ca6c 100644 --- a/luaclib/src/lhttpparser/Makefile +++ b/luaclib/src/lhttpparser/Makefile @@ -11,15 +11,14 @@ CC = cc # 如果确认是intel的CPU并且有SSE4可以自行开启这个编译参数, 经过测试http解析速度至少快2倍 # 这里为了兼容AMD CPU不作为默认开启选项 -# CFLAGS = -O3 -w -shared -fPIC -DNDEBUG -msse4 - -CFLAGS = -O3 -w -shared -fPIC -DNDEBUG - -DLL = -lcore +# CFLAGS = -O3 -w -shared -fPIC -msse4 INCLUDE = -I/usr/local/include LIB = -L/usr/local/lib -L../ -L../../../ +CFLAGS = -O3 -w -shared -fPIC +DLL = -lcore + build: $(CC) -o httpparser.so httpparser.c lhttpparser.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) diff --git a/luaclib/src/lpeg/makefile b/luaclib/src/lpeg/makefile index 424dd06a..d6d5e930 100644 --- a/luaclib/src/lpeg/makefile +++ b/luaclib/src/lpeg/makefile @@ -9,13 +9,15 @@ default : CC = gcc LIBS = -L/usr/local/lib -L../ -L../../../ +CFLAGS = -O2 -shared -fPIC +DLL = -lcore build: - $(CC) -o lpeg.so lpcap.c lpcode.c lpprint.c lptree.c lpvm.c -O2 -shared -fPIC $(LIBS) -lcore + $(CC) -o lpeg.so lpcap.c lpcode.c lpprint.c lptree.c lpvm.c $(CFLAGS) $(LIBS) $(DLL) mv *.so ../../ rebuild: - $(CC) -o lpeg.so lpcap.c lpcode.c lpprint.c lptree.c lpvm.c -O2 -shared -fPIC $(LIBS) -lcore + $(CC) -o lpeg.so lpcap.c lpcode.c lpprint.c lptree.c lpvm.c $(CFLAGS) $(LIBS) $(DLL) mv *.so ../../ clean: From f78a09fec2a6ba473e55a2d96b9a931b4e96690b Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 1 Jul 2019 07:47:43 +0800 Subject: [PATCH 196/956] =?UTF-8?q?=E4=B8=BAadmin=E5=BA=93=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/httpctx.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lualib/admin/httpctx.lua b/lualib/admin/httpctx.lua index 95a864b6..cdbb2293 100644 --- a/lualib/admin/httpctx.lua +++ b/lualib/admin/httpctx.lua @@ -43,6 +43,11 @@ function ctx:get_cookie (name) return Cookie.getCookie(name) end +-- 获取上传的文件 +function ctx:get_files () + return self._content.files +end + -- 获取请求参数表 function ctx:get_args () local args = self._content.args From 58cf7039e604d5f1ca0f1efaf4bc42755ebb300c Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 2 Jul 2019 05:36:01 +0800 Subject: [PATCH 197/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=9C=A8Mac=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E4=B8=8B=E9=BB=98=E8=AE=A4=E4=BD=BF=E7=94=A8SELECT?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98=E5=BC=95=E8=B5=B7=E7=9A=8499%CPU?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E5=8D=A0=E7=94=A8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_ev.c | 18 +++++++++++++----- src/core_ev.h | 13 +++++++------ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/core_ev.c b/src/core_ev.c index ef28889d..4973bd5d 100644 --- a/src/core_ev.c +++ b/src/core_ev.c @@ -103,11 +103,19 @@ core_signal_start(core_loop *loop, core_signal *signal){ core_loop * core_default_loop(){ - // ev_supported_backends() & EVBACKEND_EPOLL || // Linux 使用 epoll - // ev_supported_backends() & EVBACKEND_KQUEUE || // mac|BSD 使用 kqueue - // ev_supported_backends() & EVBACKEND_SELECT || // other 使用 select - // EVFLAG_AUTO // select 都没有就自动选择 - return ev_default_loop(ev_embeddable_backends() & ev_supported_backends() || EVFLAG_AUTO); + + /* 默认使用 SELECT */ + int BEST_BACKEND = EVBACKEND_SELECT; + +#ifdef EV_USE_EPOLL /* Linux */ + BEST_BACKEND = EVBACKEND_EPOLL; +#endif + +#ifdef EV_USE_KQUEUE /* Unix | Mac */ + BEST_BACKEND = EVBACKEND_KQUEUE; +#endif + + return ev_default_loop( BEST_BACKEND ); } void diff --git a/src/core_ev.h b/src/core_ev.h index e06d14ac..6898a67b 100644 --- a/src/core_ev.h +++ b/src/core_ev.h @@ -14,11 +14,12 @@ #define EV_USE_FLOOR 1 // 如果没有math.h库, 可以定义NO_USE_FLOOR #endif -#define EV_USE_REALTIME 1 - -#define EV_USE_MONOTONIC 1 - -#define EV_USE_CLOCK_SYSCALL 1 +#define EV_FORK_ENABLE 0 +#define EV_CHILD_ENABLE 0 +#define EV_CHECK_ENABLE 0 +#define EV_EMBED_ENABLE 0 +#define EV_PREPARE_ENABLE 0 +#define EV_PREPARE_ENABLE 0 #define EV_USE_4HEAP 1 @@ -30,7 +31,7 @@ /* eventfd 与 signalfd */ #if defined(__linux) || defined(__linux__) - #define EV_USE_LINUXAIO 1 + // #define EV_USE_LINUXAIO 1 /* 待完整支持AIO后再根据情况开启 */ #define EV_USE_EPOLL 1 #define EV_USE_INOTIFY 1 #define EV_USE_SIGNALFD 1 From ae2c68fc02ee4df3ff4455dd7654c8da92b9449f Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 3 Jul 2019 02:27:13 +0800 Subject: [PATCH 198/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0lcrypt,=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0sha512=E3=80=81hmac=5Fmd5=E3=80=81hmac=5Fsha512?= =?UTF-8?q?=E5=93=88=E5=B8=8C=E6=91=98=E8=A6=81=E7=AE=97=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/Makefile | 6 +- luaclib/src/lcrypt/attributes.h | 160 +++++ luaclib/src/lcrypt/crc.c | 6 +- luaclib/src/lcrypt/lcrypt.c | 17 +- luaclib/src/lcrypt/md5.c | 308 ++++++++++ luaclib/src/lcrypt/md5.h | 37 ++ luaclib/src/lcrypt/sha2.c | 1007 +++++++++++++++++++++++++++++++ luaclib/src/lcrypt/sha2.h | 81 +++ luaclib/src/lcrypt/sha256.c | 226 ------- lualib/crypt/init.lua | 88 ++- 10 files changed, 1682 insertions(+), 254 deletions(-) create mode 100644 luaclib/src/lcrypt/attributes.h create mode 100644 luaclib/src/lcrypt/md5.c create mode 100644 luaclib/src/lcrypt/md5.h create mode 100644 luaclib/src/lcrypt/sha2.c create mode 100644 luaclib/src/lcrypt/sha2.h delete mode 100644 luaclib/src/lcrypt/sha256.c diff --git a/luaclib/src/lcrypt/Makefile b/luaclib/src/lcrypt/Makefile index 61cc769b..2ed7b789 100644 --- a/luaclib/src/lcrypt/Makefile +++ b/luaclib/src/lcrypt/Makefile @@ -9,7 +9,7 @@ default : CC = cc -CFLAGS = -O3 -w -shared -fPIC +CFLAGS = -O3 -Wall -shared -fPIC DLL = -lcore @@ -17,11 +17,11 @@ INCLUDE = -I/usr/local/include LIB = -L/usr/local/lib -L../ -L../../../ build: - $(CC) -o lcrypt.so lcrypt.c crc.c sha1.c sha256.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) + $(CC) -o lcrypt.so lcrypt.c crc.c md5.c sha1.c sha2.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) mv lcrypt.so ../../ rebuild: - $(CC) -o lcrypt.so lcrypt.c crc.c sha1.c sha256.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) + $(CC) -o lcrypt.so lcrypt.c crc.c md5.c sha1.c sha2.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) mv lcrypt.so ../../ clean: diff --git a/luaclib/src/lcrypt/attributes.h b/luaclib/src/lcrypt/attributes.h new file mode 100644 index 00000000..866e143a --- /dev/null +++ b/luaclib/src/lcrypt/attributes.h @@ -0,0 +1,160 @@ +/* attributes.h; some GCC extensions wrapped up so they do nothing elsewhere + * Copyright 2009-2010 Rob Kendrick , distributed under MIT + * + * Of course, some compilers define __GNUC__ and a GCC version number, and + * aren't GCC. If they choke when you use this file, report a bug to your + * compiler vendor. + */ + +#ifndef _GCC_ATTRIBUTES_ +#define _GCC_ATTRIBUTES_ + +#ifdef __GNUC__ +# define GCC_VERSION (__GNUC__ * 10000 \ + + __GNUC_MINOR__ * 100 \ + + __GNUC_PATCHLEVEL__) +#else +# define GCC_VERSION 0 +#endif + +/* Symbol behaviors *********************************************************/ + +#if GCC_VERSION >= 20500 +# define _NORETURN __attribute__ ((noreturn)) +#else +# define _NORETURN +#endif + +#if GCC_VERSION >= 20700 +# define _CONSTRUCTOR __attribute__ ((constructor)) +# define _DESTRUCTOR __attribute__ ((destructor)) +# define _WEAK __attribute__ ((weak)) +#else +# define _CONSTRUCTOR +# define _DESTRUCTOR +# define _WEAK +#endif + +#if GCC_VERSION >= 30000 +# define _MALLOC __attribute__ ((malloc)) +#else +# define _MALLOC +#endif + +#if GCC_VERSION >= 30300 +# define _CLEANUP(x) __attribute__ ((cleanup(x))) +#else +# define _CLEANUP(x) +#endif + +/* Parameter and call checking **********************************************/ + +#if GCC_VERSION >= 20300 +# define _FORMAT(...) __attribute__ ((format(__VA_ARGS__))) +#else +# define _FORMAT(...) +#endif + +#if GCC_VERSION >= 20700 +# define _UNUSED __attribute__ ((unused)) +#else +# define _UNUSED +#endif + +#if GCC_VERSION >= 20800 +# define _FORMAT_ARG(x) __attribute__ ((format_arg(x))) +#else +# define _FORMAT_ARG(x) +#endif + +#if GCC_VERSION >= 30100 +# define _DEPRECATED __attribute__ ((deprecated)) +# define _USED __attribute__ ((used)) +#else +# define _DEPRECATED +# define _USED +#endif + +#if GCC_VERSION >= 30300 +# define _NONNULL(...) __attribute__ ((nonnull(__VA_ARGS__))) +# define _WARN_UNUSED_RESULT __attribute__ ((warn_unused_result)) +#else +# define _NONNULL(...) +# define _WARN_UNUSED_RESULT +#endif + +#if GCC_VERSION >= 40400 +# define _BOUNDED(...) __attribute ((__bounded__(__VAR_ARGS__))) +#else +# define _BOUNDED(...) +#endif + +/* Performance and optimisation-related *************************************/ + +#if GCC_VERSION >= 20500 +# define _CONST __attribute__ ((const)) +#else +# define _CONST +#endif + +#if GCC_VERSION >= 20700 +# define _ALIGNED(x) __attribute__ ((aligned(x))) +# define _PACKED __attribute__ ((packed)) +#else +# define _ALIGNED(x) +# define _PACKED +#endif + +#if GCC_VERSION >= 30000 +# define _PURE __attribute__ ((pure)) +#else +# define _PURE +#endif + +#if GCC_VERSION >= 30100 +# define _NO_INLINE __attribute__ ((noinline)) +#else +# define _NO_INLINE +#endif + +#if GCC_VERSION >= 30200 +# define _NOTHROW __attribute__ ((nothrow)) +#else +# define _NOTHROW +#endif + +#if GCC_VERSION >= 40300 +# define _HOT __attribute__ ((hot)) +# define _COLD __attribute__ ((cold)) +#else +# define _HOT +# define _COLD +#endif + +#if GCC_VERSION >= 40400 +# define _OPTIMISE(x) __attribute__ ((optimize(x))) +# define _TARGET(x) __attribute__ ((target(x))) +# define _SSEREGPARM __attribute__ ((sseregparm)) +#else +# define _OPTIMISE(x) +# define _TARGET(x) +# define _SSEREGPARM +#endif + +/* We don't have any version support information for these */ + +#if __GNUC__ +# define _ALWAYS_INLINE __attribute__ ((always_inline)) +# define _FLATTEN __attribute__ ((flatten)) +#else +# define _ALWAYS_INLINE +# define _FLATTEN +#endif + +/****************************************************************************/ + +#ifdef __GNUC__ +# undef GCC_VERSION +#endif + +#endif /* _GCC_ATTRIBUTES_ */ diff --git a/luaclib/src/lcrypt/crc.c b/luaclib/src/lcrypt/crc.c index 14a065c9..107c4981 100644 --- a/luaclib/src/lcrypt/crc.c +++ b/luaclib/src/lcrypt/crc.c @@ -202,11 +202,10 @@ static uint64_t CRC64[] = { 0x536fa08fdfd90e51, 0x29b7d047efec8728, }; -LUAMOD_API int +int lcrc32(lua_State *L){ size_t len; const char *str = luaL_checklstring(L, 1, &len); - if (len <= 0) return luaL_error(L, "crc32 err: #1 need a string"); uint32_t i = 0; uint32_t crc = 0xFFFFFFFF; @@ -216,11 +215,10 @@ lcrc32(lua_State *L){ return 1; }; -LUAMOD_API int +int lcrc64(lua_State *L){ size_t len; const char *str = luaL_checklstring(L, 1, &len); - if (len <= 0) return luaL_error(L, "crc64 err: #1 need a string"); uint32_t i = 0; uint64_t crc = 0x0; diff --git a/luaclib/src/lcrypt/lcrypt.c b/luaclib/src/lcrypt/lcrypt.c index 5a2b21ed..e2c5b74c 100644 --- a/luaclib/src/lcrypt/lcrypt.c +++ b/luaclib/src/lcrypt/lcrypt.c @@ -943,6 +943,10 @@ lxor_str(lua_State *L) { return 1; } +// defined in md5.c +int lmd5(lua_State *L); +int lhmac_md5(lua_State *L); + // defined in crc.c int lcrc32(lua_State *L); int lcrc64(lua_State *L); @@ -955,6 +959,9 @@ int lhmac_sha1(lua_State *L); int lsha256(lua_State *L); int lhmac_sha256(lua_State *L); +int lsha512(lua_State *L); +int lhmac_sha512(lua_State *L); + LUAMOD_API int luaopen_lcrypt(lua_State *L) { luaL_checkversion(L); @@ -971,12 +978,16 @@ luaopen_lcrypt(lua_State *L) { { "dhsecret", ldhsecret }, { "base64encode", lb64encode }, { "base64decode", lb64decode }, + { "md5", lmd5 }, + { "hmac_md5", lhmac_md5 }, + { "crc32", lcrc32 }, + { "crc64", lcrc64 }, { "sha1", lsha1 }, - { "hmac_sha1", lhmac_sha1 }, { "sha256", lsha256 }, + { "sha512", lsha512 }, + { "hmac_sha1", lhmac_sha1 }, { "hmac_sha256", lhmac_sha256 }, - { "crc32", lcrc32 }, - { "crc64", lcrc64 }, + { "hmac_sha512", lhmac_sha512 }, { "hmac_hash", lhmac_hash }, { "xor_str", lxor_str }, { NULL, NULL }, diff --git a/luaclib/src/lcrypt/md5.c b/luaclib/src/lcrypt/md5.c new file mode 100644 index 00000000..a48fb4f4 --- /dev/null +++ b/luaclib/src/lcrypt/md5.c @@ -0,0 +1,308 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +/* Trivial Lua Binding, Copyright 2010 Rob Kendrick */ + +#define LUA_LIB + +#include "md5.h" + +#define PUT_64BIT_LE(cp, value) do { \ + (cp)[7] = (value) >> 56; \ + (cp)[6] = (value) >> 48; \ + (cp)[5] = (value) >> 40; \ + (cp)[4] = (value) >> 32; \ + (cp)[3] = (value) >> 24; \ + (cp)[2] = (value) >> 16; \ + (cp)[1] = (value) >> 8; \ + (cp)[0] = (value); } while (0) + +#define PUT_32BIT_LE(cp, value) do { \ + (cp)[3] = (value) >> 24; \ + (cp)[2] = (value) >> 16; \ + (cp)[1] = (value) >> 8; \ + (cp)[0] = (value); } while (0) + +static u_int8_t PADDING[MD5_BLOCK_LENGTH] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +static void +MD5Init(MD5_CTX *ctx) +{ + ctx->count = 0; + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xefcdab89; + ctx->state[2] = 0x98badcfe; + ctx->state[3] = 0x10325476; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +static void +MD5Update(MD5_CTX *ctx, const unsigned char *input, size_t len) +{ + size_t have, need; + + /* Check how many bytes we already have and how many more we need. */ + have = (size_t)((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1)); + need = MD5_BLOCK_LENGTH - have; + + /* Update bitcount */ + ctx->count += (u_int64_t)len << 3; + + if (len >= need) { + if (have != 0) { + bcopy(input, ctx->buffer + have, need); + MD5Transform(ctx->state, ctx->buffer); + input += need; + len -= need; + have = 0; + } + + /* Process data in MD5_BLOCK_LENGTH-byte chunks. */ + while (len >= MD5_BLOCK_LENGTH) { + MD5Transform(ctx->state, input); + input += MD5_BLOCK_LENGTH; + len -= MD5_BLOCK_LENGTH; + } + } + + /* Handle any remaining bytes of data. */ + if (len != 0) + bcopy(input, ctx->buffer + have, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +static void +MD5Final(unsigned char digest[MD5_DIGEST_LENGTH], MD5_CTX *ctx) +{ + u_int8_t count[8]; + size_t padlen; + int i; + + /* Convert count to 8 bytes in little endian order. */ + PUT_64BIT_LE(count, ctx->count); + + /* Pad out to 56 mod 64. */ + padlen = MD5_BLOCK_LENGTH - + ((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1)); + if (padlen < 1 + 8) + padlen += MD5_BLOCK_LENGTH; + MD5Update(ctx, PADDING, padlen - 8); /* padlen - 8 <= 64 */ + MD5Update(ctx, count, 8); + + if (digest != NULL) { + for (i = 0; i < 4; i++) + PUT_32BIT_LE(digest + i * 4, ctx->state[i]); + } + bzero(ctx, sizeof(*ctx)); /* in case it's sensitive */ +} + + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void +MD5Transform(u_int32_t state[4], const u_int8_t block[MD5_BLOCK_LENGTH]) +{ + u_int32_t a, b, c, d, in[MD5_BLOCK_LENGTH / 4]; + +#if BYTE_ORDER == LITTLE_ENDIAN + bcopy(block, in, sizeof(in)); +#else + for (a = 0; a < MD5_BLOCK_LENGTH / 4; a++) { + in[a] = (u_int32_t)( + (u_int32_t)(block[a * 4 + 0]) | + (u_int32_t)(block[a * 4 + 1]) << 8 | + (u_int32_t)(block[a * 4 + 2]) << 16 | + (u_int32_t)(block[a * 4 + 3]) << 24); + } +#endif + + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + + MD5STEP(F1, a, b, c, d, in[ 0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[ 1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[ 2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[ 3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[ 4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[ 5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[ 6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[ 7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[ 8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[ 9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[ 1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[ 6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[ 0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[ 5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[ 4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[ 9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[ 3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[ 8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[ 2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[ 7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[ 5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[ 8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[ 1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[ 4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[ 7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[ 0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[ 3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[ 6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[ 9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2 ] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[ 0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7 ] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5 ] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3 ] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1 ] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8 ] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6 ] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4 ] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2 ] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9 ] + 0xeb86d391, 21); + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; +} + +int +lmd5(lua_State *L) +{ + MD5_CTX ctx; + u_int8_t sum[MD5_DIGEST_LENGTH]; + size_t l; + const char *d = luaL_checklstring(L, 1, &l); + + MD5Init(&ctx); + MD5Update(&ctx, (const u_int8_t *)d, l); + MD5Final(sum, &ctx); + + lua_pushlstring(L, (char *)sum, MD5_DIGEST_LENGTH); + + return 1; +} + + +#define BLOCKSIZE 64 + +static inline +void xor_key(uint8_t key[BLOCKSIZE], uint32_t xor) { + int i; + for (i=0;i BLOCKSIZE) { + MD5_CTX ctx; + MD5Init(&ctx); + MD5Update(&ctx, key, key_sz); + MD5Final(rkey, &ctx1); + key_sz = MD5_DIGEST_LENGTH; + } else { + memcpy(rkey, key, key_sz); + } + + xor_key(rkey, 0x5c5c5c5c); + MD5Init(&ctx1); + MD5Update(&ctx1, rkey, BLOCKSIZE); + + xor_key(rkey, 0x5c5c5c5c ^ 0x36363636); + MD5Init(&ctx2); + MD5Update(&ctx2, rkey, BLOCKSIZE); + MD5Update(&ctx2, text, text_sz); + MD5Final(digest2, &ctx2); + + MD5Update(&ctx1, digest2, MD5_DIGEST_LENGTH); + MD5Final(digest1, &ctx1); + + lua_pushlstring(L, (const char *)digest1, MD5_DIGEST_LENGTH); + + return 1; +} diff --git a/luaclib/src/lcrypt/md5.h b/luaclib/src/lcrypt/md5.h new file mode 100644 index 00000000..d042d21f --- /dev/null +++ b/luaclib/src/lcrypt/md5.h @@ -0,0 +1,37 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + */ + +#ifndef _MD5_H_ +#define _MD5_H_ + +#include "attributes.h" +#include "../../../src/core.h" + +#define MD5_BLOCK_LENGTH 64 +#define MD5_DIGEST_LENGTH 16 + +typedef struct MD5Context { + u_int32_t state[4]; /* state */ + u_int64_t count; /* number of bits, mod 2^64 */ + u_int8_t buffer[MD5_BLOCK_LENGTH]; /* input buffer */ +} MD5_CTX; + +static void MD5Init(MD5_CTX *); +static void MD5Update(MD5_CTX *, const u_int8_t *, size_t) + _BOUNDED(__string__,2,3); +static void MD5Final(u_int8_t [MD5_DIGEST_LENGTH], MD5_CTX *) + _BOUNDED(__minbytes__,1,MD5_DIGEST_LENGTH); +static void MD5Transform(u_int32_t [4], const u_int8_t [MD5_BLOCK_LENGTH]) + _BOUNDED(__minbytes__,1,4) + _BOUNDED(__minbytes__,2,MD5_BLOCK_LENGTH); + +#endif /* _MD5_H_ */ diff --git a/luaclib/src/lcrypt/sha2.c b/luaclib/src/lcrypt/sha2.c new file mode 100644 index 00000000..3d3cc382 --- /dev/null +++ b/luaclib/src/lcrypt/sha2.c @@ -0,0 +1,1007 @@ +/* + * FILE: sha2.c + * AUTHOR: Aaron D. Gifford + * + * Copyright (c) 2000-2001, Aaron D. Gifford + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $From: sha2.c,v 1.1 2001/11/08 00:01:51 adg Exp adg $ + */ + +/* Trivial Lua Binding, Copyright 2010 Rob Kendrick */ + +#define LUA_LIB + +#include "sha2.h" + + +/*** SHA-256/384/512 Machine Architecture Definitions *****************/ +/* + * BYTE_ORDER NOTE: + * + * Please make sure that your system defines BYTE_ORDER. If your + * architecture is little-endian, make sure it also defines + * LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are + * equivilent. + * + * If your system does not define the above, then you can do so by + * hand like this: + * + * #define LITTLE_ENDIAN 1234 + * #define BIG_ENDIAN 4321 + * + * And for little-endian machines, add: + * + * #define BYTE_ORDER LITTLE_ENDIAN + * + * Or for big-endian machines: + * + * #define BYTE_ORDER BIG_ENDIAN + * + * The FreeBSD machine this was written on defines BYTE_ORDER + * appropriately by including (which in turn includes + * where the appropriate definitions are actually + * made). + */ +#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN) +#error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN +#endif + + +/*** SHA-256/384/512 Various Length Definitions ***********************/ +/* NOTE: Most of these are in sha2.h */ +#define SHA256_SHORT_BLOCK_LENGTH (SHA256_BLOCK_LENGTH - 8) +#define SHA384_SHORT_BLOCK_LENGTH (SHA384_BLOCK_LENGTH - 16) +#define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16) + + +/*** ENDIAN REVERSAL MACROS *******************************************/ +#if BYTE_ORDER == LITTLE_ENDIAN +#define REVERSE32(w,x) { \ + u_int32_t tmp = (w); \ + tmp = (tmp >> 16) | (tmp << 16); \ + (x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \ +} +#define REVERSE64(w,x) { \ + u_int64_t tmp = (w); \ + tmp = (tmp >> 32) | (tmp << 32); \ + tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \ + ((tmp & 0x00ff00ff00ff00ffULL) << 8); \ + (x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \ + ((tmp & 0x0000ffff0000ffffULL) << 16); \ +} +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + +/* + * Macro for incrementally adding the unsigned 64-bit integer n to the + * unsigned 128-bit integer (represented using a two-element array of + * 64-bit words): + */ +#define ADDINC128(w,n) { \ + (w)[0] += (u_int64_t)(n); \ + if ((w)[0] < (n)) { \ + (w)[1]++; \ + } \ +} + +/*** THE SIX LOGICAL FUNCTIONS ****************************************/ +/* + * Bit shifting and rotation (used by the six SHA-XYZ logical functions: + * + * NOTE: The naming of R and S appears backwards here (R is a SHIFT and + * S is a ROTATION) because the SHA-256/384/512 description document + * (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this + * same "backwards" definition. + */ +/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */ +#define R(b,x) ((x) >> (b)) +/* 32-bit Rotate-right (used in SHA-256): */ +#define S32(b,x) (((x) >> (b)) | ((x) << (32 - (b)))) +/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */ +#define S64(b,x) (((x) >> (b)) | ((x) << (64 - (b)))) + +/* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */ +#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) +#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +/* Four of six logical functions used in SHA-256: */ +#define Sigma0_256(x) (S32(2, (x)) ^ S32(13, (x)) ^ S32(22, (x))) +#define Sigma1_256(x) (S32(6, (x)) ^ S32(11, (x)) ^ S32(25, (x))) +#define sigma0_256(x) (S32(7, (x)) ^ S32(18, (x)) ^ R(3 , (x))) +#define sigma1_256(x) (S32(17, (x)) ^ S32(19, (x)) ^ R(10, (x))) + +/* Four of six logical functions used in SHA-384 and SHA-512: */ +#define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x))) +#define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x))) +#define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7, (x))) +#define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ R( 6, (x))) + +/*** INTERNAL FUNCTION PROTOTYPES *************************************/ +/* NOTE: These should not be accessed directly from outside this + * library -- they are intended for private internal visibility/use + * only. + */ +static void SHA512Last(SHA2_CTX *); +static void SHA256Transform(SHA2_CTX *, const u_int8_t *); +static void SHA512Transform(SHA2_CTX *, const u_int8_t *); + + +/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/ +/* Hash constant words K for SHA-256: */ +const static u_int32_t K256[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, + 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, + 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, + 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, + 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, + 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, + 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL, + 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, + 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, + 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, + 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, + 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL, + 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; + +/* Initial hash value H for SHA-256: */ +const static u_int32_t sha256_initial_hash_value[8] = { + 0x6a09e667UL, + 0xbb67ae85UL, + 0x3c6ef372UL, + 0xa54ff53aUL, + 0x510e527fUL, + 0x9b05688cUL, + 0x1f83d9abUL, + 0x5be0cd19UL +}; + +/* Hash constant words K for SHA-384 and SHA-512: */ +const static u_int64_t K512[80] = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, + 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, + 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, + 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, + 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, + 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, + 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, + 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, + 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, + 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, + 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, + 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, + 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, + 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, + 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, + 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, + 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, + 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, + 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, + 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, + 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, + 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, + 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, + 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, + 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL +}; + +/* Initial hash value H for SHA-384 */ +// const static u_int64_t sha384_initial_hash_value[8] = { +// 0xcbbb9d5dc1059ed8ULL, +// 0x629a292a367cd507ULL, +// 0x9159015a3070dd17ULL, +// 0x152fecd8f70e5939ULL, +// 0x67332667ffc00b31ULL, +// 0x8eb44a8768581511ULL, +// 0xdb0c2e0d64f98fa7ULL, +// 0x47b5481dbefa4fa4ULL +// }; + +/* Initial hash value H for SHA-512 */ +const static u_int64_t sha512_initial_hash_value[8] = { + 0x6a09e667f3bcc908ULL, + 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, + 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, + 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, + 0x5be0cd19137e2179ULL +}; + + +/*** SHA-256: *********************************************************/ +static void +SHA256Init(SHA2_CTX *context) +{ + if (context == NULL) + return; + bcopy(sha256_initial_hash_value, context->state.st32, + SHA256_DIGEST_LENGTH); + bzero(context->buffer, SHA256_BLOCK_LENGTH); + context->bitcount[0] = 0; +} + +#ifdef SHA2_UNROLL_TRANSFORM + +/* Unrolled SHA-256 round macros: */ + +#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) do { \ + W256[j] = (u_int32_t)data[3] | ((u_int32_t)data[2] << 8) | \ + ((u_int32_t)data[1] << 16) | ((u_int32_t)data[0] << 24); \ + data += 4; \ + T1 = (h) + Sigma1_256((e)) + Ch((e), (f), (g)) + K256[j] + W256[j]; \ + (d) += T1; \ + (h) = T1 + Sigma0_256((a)) + Maj((a), (b), (c)); \ + j++; \ +} while(0) + +#define ROUND256(a,b,c,d,e,f,g,h) do { \ + s0 = W256[(j+1)&0x0f]; \ + s0 = sigma0_256(s0); \ + s1 = W256[(j+14)&0x0f]; \ + s1 = sigma1_256(s1); \ + T1 = (h) + Sigma1_256((e)) + Ch((e), (f), (g)) + K256[j] + \ + (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \ + (d) += T1; \ + (h) = T1 + Sigma0_256((a)) + Maj((a), (b), (c)); \ + j++; \ +} while(0) + +static void +SHA256Transform(SHA2_CTX *context, const u_int8_t *data) +{ + u_int32_t a, b, c, d, e, f, g, h, s0, s1; + u_int32_t T1, *W256; + int j; + + W256 = (u_int32_t *)context->buffer; + + /* Initialize registers with the prev. intermediate value */ + a = context->state.st32[0]; + b = context->state.st32[1]; + c = context->state.st32[2]; + d = context->state.st32[3]; + e = context->state.st32[4]; + f = context->state.st32[5]; + g = context->state.st32[6]; + h = context->state.st32[7]; + + j = 0; + do { + /* Rounds 0 to 15 (unrolled): */ + ROUND256_0_TO_15(a,b,c,d,e,f,g,h); + ROUND256_0_TO_15(h,a,b,c,d,e,f,g); + ROUND256_0_TO_15(g,h,a,b,c,d,e,f); + ROUND256_0_TO_15(f,g,h,a,b,c,d,e); + ROUND256_0_TO_15(e,f,g,h,a,b,c,d); + ROUND256_0_TO_15(d,e,f,g,h,a,b,c); + ROUND256_0_TO_15(c,d,e,f,g,h,a,b); + ROUND256_0_TO_15(b,c,d,e,f,g,h,a); + } while (j < 16); + + /* Now for the remaining rounds to 64: */ + do { + ROUND256(a,b,c,d,e,f,g,h); + ROUND256(h,a,b,c,d,e,f,g); + ROUND256(g,h,a,b,c,d,e,f); + ROUND256(f,g,h,a,b,c,d,e); + ROUND256(e,f,g,h,a,b,c,d); + ROUND256(d,e,f,g,h,a,b,c); + ROUND256(c,d,e,f,g,h,a,b); + ROUND256(b,c,d,e,f,g,h,a); + } while (j < 64); + + /* Compute the current intermediate hash value */ + context->state.st32[0] += a; + context->state.st32[1] += b; + context->state.st32[2] += c; + context->state.st32[3] += d; + context->state.st32[4] += e; + context->state.st32[5] += f; + context->state.st32[6] += g; + context->state.st32[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = 0; +} + +#else /* SHA2_UNROLL_TRANSFORM */ + +static void +SHA256Transform(SHA2_CTX *context, const u_int8_t *data) +{ + u_int32_t a, b, c, d, e, f, g, h, s0, s1; + u_int32_t T1, T2, *W256; + int j; + + W256 = (u_int32_t *)context->buffer; + + /* Initialize registers with the prev. intermediate value */ + a = context->state.st32[0]; + b = context->state.st32[1]; + c = context->state.st32[2]; + d = context->state.st32[3]; + e = context->state.st32[4]; + f = context->state.st32[5]; + g = context->state.st32[6]; + h = context->state.st32[7]; + + j = 0; + do { + W256[j] = (u_int32_t)data[3] | ((u_int32_t)data[2] << 8) | + ((u_int32_t)data[1] << 16) | ((u_int32_t)data[0] << 24); + data += 4; + /* Apply the SHA-256 compression function to update a..h */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j]; + T2 = Sigma0_256(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 16); + + do { + /* Part of the message block expansion: */ + s0 = W256[(j+1)&0x0f]; + s0 = sigma0_256(s0); + s1 = W256[(j+14)&0x0f]; + s1 = sigma1_256(s1); + + /* Apply the SHA-256 compression function to update a..h */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + + (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); + T2 = Sigma0_256(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 64); + + /* Compute the current intermediate hash value */ + context->state.st32[0] += a; + context->state.st32[1] += b; + context->state.st32[2] += c; + context->state.st32[3] += d; + context->state.st32[4] += e; + context->state.st32[5] += f; + context->state.st32[6] += g; + context->state.st32[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = T2 = 0; +} + +#endif /* SHA2_UNROLL_TRANSFORM */ + +static void +SHA256Update(SHA2_CTX *context, const u_int8_t *data, size_t len) +{ + size_t freespace, usedspace; + + /* Calling with no data is valid (we do nothing) */ + if (len == 0) + return; + + usedspace = (context->bitcount[0] >> 3) % SHA256_BLOCK_LENGTH; + if (usedspace > 0) { + /* Calculate how much free space is available in the buffer */ + freespace = SHA256_BLOCK_LENGTH - usedspace; + + if (len >= freespace) { + /* Fill the buffer completely and process it */ + bcopy(data, &context->buffer[usedspace], freespace); + context->bitcount[0] += freespace << 3; + len -= freespace; + data += freespace; + SHA256Transform(context, context->buffer); + } else { + /* The buffer is not yet full */ + bcopy(data, &context->buffer[usedspace], len); + context->bitcount[0] += len << 3; + /* Clean up: */ + usedspace = freespace = 0; + return; + } + } + while (len >= SHA256_BLOCK_LENGTH) { + /* Process as many complete blocks as we can */ + SHA256Transform(context, data); + context->bitcount[0] += SHA256_BLOCK_LENGTH << 3; + len -= SHA256_BLOCK_LENGTH; + data += SHA256_BLOCK_LENGTH; + } + if (len > 0) { + /* There's left-overs, so save 'em */ + bcopy(data, context->buffer, len); + context->bitcount[0] += len << 3; + } + /* Clean up: */ + usedspace = freespace = 0; +} + +static void +SHA256Final(u_int8_t digest[], SHA2_CTX *context) +{ + u_int32_t *d = (u_int32_t *)digest; + unsigned int usedspace; + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != NULL) { + usedspace = (context->bitcount[0] >> 3) % SHA256_BLOCK_LENGTH; +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert FROM host byte order */ + REVERSE64(context->bitcount[0], context->bitcount[0]); +#endif + if (usedspace > 0) { + /* Begin padding with a 1 bit: */ + context->buffer[usedspace++] = 0x80; + + if (usedspace <= SHA256_SHORT_BLOCK_LENGTH) { + /* Set-up for the last transform: */ + bzero(&context->buffer[usedspace], SHA256_SHORT_BLOCK_LENGTH - usedspace); + } else { + if (usedspace < SHA256_BLOCK_LENGTH) { + bzero(&context->buffer[usedspace], SHA256_BLOCK_LENGTH - usedspace); + } + /* Do second-to-last transform: */ + SHA256Transform(context, context->buffer); + + /* And set-up for the last transform: */ + bzero(context->buffer, SHA256_SHORT_BLOCK_LENGTH); + } + } else { + /* Set-up for the last transform: */ + bzero(context->buffer, SHA256_SHORT_BLOCK_LENGTH); + + /* Begin padding with a 1 bit: */ + *context->buffer = 0x80; + } + /* Set the bit count: */ + *(u_int64_t *)&context->buffer[SHA256_SHORT_BLOCK_LENGTH] = context->bitcount[0]; + + /* Final transform: */ + SHA256Transform(context, context->buffer); + +#if BYTE_ORDER == LITTLE_ENDIAN + { + /* Convert TO host byte order */ + int j; + for (j = 0; j < 8; j++) { + REVERSE32(context->state.st32[j], + context->state.st32[j]); + *d++ = context->state.st32[j]; + } + } +#else + bcopy(context->state.st32, d, SHA256_DIGEST_LENGTH); +#endif + } + + /* Clean up state data: */ + bzero(context, sizeof(*context)); + usedspace = 0; +} + + +/*** SHA-512: *********************************************************/ +static void +SHA512Init(SHA2_CTX *context) +{ + if (context == NULL) + return; + bcopy(sha512_initial_hash_value, context->state.st64, + SHA512_DIGEST_LENGTH); + bzero(context->buffer, SHA512_BLOCK_LENGTH); + context->bitcount[0] = context->bitcount[1] = 0; +} + +#ifdef SHA2_UNROLL_TRANSFORM + +/* Unrolled SHA-512 round macros: */ + +#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) do { \ + W512[j] = (u_int64_t)data[7] | ((u_int64_t)data[6] << 8) | \ + ((u_int64_t)data[5] << 16) | ((u_int64_t)data[4] << 24) | \ + ((u_int64_t)data[3] << 32) | ((u_int64_t)data[2] << 40) | \ + ((u_int64_t)data[1] << 48) | ((u_int64_t)data[0] << 56); \ + data += 8; \ + T1 = (h) + Sigma1_512((e)) + Ch((e), (f), (g)) + K512[j] + W512[j]; \ + (d) += T1; \ + (h) = T1 + Sigma0_512((a)) + Maj((a), (b), (c)); \ + j++; \ +} while(0) + + +#define ROUND512(a,b,c,d,e,f,g,h) do { \ + s0 = W512[(j+1)&0x0f]; \ + s0 = sigma0_512(s0); \ + s1 = W512[(j+14)&0x0f]; \ + s1 = sigma1_512(s1); \ + T1 = (h) + Sigma1_512((e)) + Ch((e), (f), (g)) + K512[j] + \ + (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); \ + (d) += T1; \ + (h) = T1 + Sigma0_512((a)) + Maj((a), (b), (c)); \ + j++; \ +} while(0) + +static void +SHA512Transform(SHA2_CTX *context, const u_int8_t *data) +{ + u_int64_t a, b, c, d, e, f, g, h, s0, s1; + u_int64_t T1, *W512 = (u_int64_t *)context->buffer; + int j; + + /* Initialize registers with the prev. intermediate value */ + a = context->state.st64[0]; + b = context->state.st64[1]; + c = context->state.st64[2]; + d = context->state.st64[3]; + e = context->state.st64[4]; + f = context->state.st64[5]; + g = context->state.st64[6]; + h = context->state.st64[7]; + + j = 0; + do { + ROUND512_0_TO_15(a,b,c,d,e,f,g,h); + ROUND512_0_TO_15(h,a,b,c,d,e,f,g); + ROUND512_0_TO_15(g,h,a,b,c,d,e,f); + ROUND512_0_TO_15(f,g,h,a,b,c,d,e); + ROUND512_0_TO_15(e,f,g,h,a,b,c,d); + ROUND512_0_TO_15(d,e,f,g,h,a,b,c); + ROUND512_0_TO_15(c,d,e,f,g,h,a,b); + ROUND512_0_TO_15(b,c,d,e,f,g,h,a); + } while (j < 16); + + /* Now for the remaining rounds up to 79: */ + do { + ROUND512(a,b,c,d,e,f,g,h); + ROUND512(h,a,b,c,d,e,f,g); + ROUND512(g,h,a,b,c,d,e,f); + ROUND512(f,g,h,a,b,c,d,e); + ROUND512(e,f,g,h,a,b,c,d); + ROUND512(d,e,f,g,h,a,b,c); + ROUND512(c,d,e,f,g,h,a,b); + ROUND512(b,c,d,e,f,g,h,a); + } while (j < 80); + + /* Compute the current intermediate hash value */ + context->state.st64[0] += a; + context->state.st64[1] += b; + context->state.st64[2] += c; + context->state.st64[3] += d; + context->state.st64[4] += e; + context->state.st64[5] += f; + context->state.st64[6] += g; + context->state.st64[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = 0; +} + +#else /* SHA2_UNROLL_TRANSFORM */ + +static void +SHA512Transform(SHA2_CTX *context, const u_int8_t *data) +{ + u_int64_t a, b, c, d, e, f, g, h, s0, s1; + u_int64_t T1, T2, *W512 = (u_int64_t *)context->buffer; + int j; + + /* Initialize registers with the prev. intermediate value */ + a = context->state.st64[0]; + b = context->state.st64[1]; + c = context->state.st64[2]; + d = context->state.st64[3]; + e = context->state.st64[4]; + f = context->state.st64[5]; + g = context->state.st64[6]; + h = context->state.st64[7]; + + j = 0; + do { + W512[j] = (u_int64_t)data[7] | ((u_int64_t)data[6] << 8) | + ((u_int64_t)data[5] << 16) | ((u_int64_t)data[4] << 24) | + ((u_int64_t)data[3] << 32) | ((u_int64_t)data[2] << 40) | + ((u_int64_t)data[1] << 48) | ((u_int64_t)data[0] << 56); + data += 8; + /* Apply the SHA-512 compression function to update a..h */ + T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j]; + T2 = Sigma0_512(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 16); + + do { + /* Part of the message block expansion: */ + s0 = W512[(j+1)&0x0f]; + s0 = sigma0_512(s0); + s1 = W512[(j+14)&0x0f]; + s1 = sigma1_512(s1); + + /* Apply the SHA-512 compression function to update a..h */ + T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + + (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); + T2 = Sigma0_512(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 80); + + /* Compute the current intermediate hash value */ + context->state.st64[0] += a; + context->state.st64[1] += b; + context->state.st64[2] += c; + context->state.st64[3] += d; + context->state.st64[4] += e; + context->state.st64[5] += f; + context->state.st64[6] += g; + context->state.st64[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = T2 = 0; +} + +#endif /* SHA2_UNROLL_TRANSFORM */ + +static void +SHA512Update(SHA2_CTX *context, const u_int8_t *data, size_t len) +{ + size_t freespace, usedspace; + + /* Calling with no data is valid (we do nothing) */ + if (len == 0) + return; + + usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; + if (usedspace > 0) { + /* Calculate how much free space is available in the buffer */ + freespace = SHA512_BLOCK_LENGTH - usedspace; + + if (len >= freespace) { + /* Fill the buffer completely and process it */ + bcopy(data, &context->buffer[usedspace], freespace); + ADDINC128(context->bitcount, freespace << 3); + len -= freespace; + data += freespace; + SHA512Transform(context, context->buffer); + } else { + /* The buffer is not yet full */ + bcopy(data, &context->buffer[usedspace], len); + ADDINC128(context->bitcount, len << 3); + /* Clean up: */ + usedspace = freespace = 0; + return; + } + } + while (len >= SHA512_BLOCK_LENGTH) { + /* Process as many complete blocks as we can */ + SHA512Transform(context, data); + ADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3); + len -= SHA512_BLOCK_LENGTH; + data += SHA512_BLOCK_LENGTH; + } + if (len > 0) { + /* There's left-overs, so save 'em */ + bcopy(data, context->buffer, len); + ADDINC128(context->bitcount, len << 3); + } + /* Clean up: */ + usedspace = freespace = 0; +} + +static void +SHA512Last(SHA2_CTX *context) +{ + unsigned int usedspace; + + usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert FROM host byte order */ + REVERSE64(context->bitcount[0],context->bitcount[0]); + REVERSE64(context->bitcount[1],context->bitcount[1]); +#endif + if (usedspace > 0) { + /* Begin padding with a 1 bit: */ + context->buffer[usedspace++] = 0x80; + + if (usedspace <= SHA512_SHORT_BLOCK_LENGTH) { + /* Set-up for the last transform: */ + bzero(&context->buffer[usedspace], SHA512_SHORT_BLOCK_LENGTH - usedspace); + } else { + if (usedspace < SHA512_BLOCK_LENGTH) { + bzero(&context->buffer[usedspace], SHA512_BLOCK_LENGTH - usedspace); + } + /* Do second-to-last transform: */ + SHA512Transform(context, context->buffer); + + /* And set-up for the last transform: */ + bzero(context->buffer, SHA512_BLOCK_LENGTH - 2); + } + } else { + /* Prepare for final transform: */ + bzero(context->buffer, SHA512_SHORT_BLOCK_LENGTH); + + /* Begin padding with a 1 bit: */ + *context->buffer = 0x80; + } + /* Store the length of input data (in bits): */ + *(u_int64_t *)&context->buffer[SHA512_SHORT_BLOCK_LENGTH] = context->bitcount[1]; + *(u_int64_t *)&context->buffer[SHA512_SHORT_BLOCK_LENGTH+8] = context->bitcount[0]; + + /* Final transform: */ + SHA512Transform(context, context->buffer); +} + +static void +SHA512Final(u_int8_t digest[], SHA2_CTX *context) +{ + u_int64_t *d = (u_int64_t *)digest; + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != NULL) { + SHA512Last(context); + + /* Save the hash data for output: */ +#if BYTE_ORDER == LITTLE_ENDIAN + { + /* Convert TO host byte order */ + int j; + for (j = 0; j < 8; j++) { + REVERSE64(context->state.st64[j], + context->state.st64[j]); + *d++ = context->state.st64[j]; + } + } +#else + bcopy(context->state.st64, d, SHA512_DIGEST_LENGTH); +#endif + } + + /* Zero out state data */ + bzero(context, sizeof(*context)); +} + + +/*** SHA-384: *********************************************************/ +// static void +// SHA384Init(SHA2_CTX *context) +// { +// if (context == NULL) +// return; +// bcopy(sha384_initial_hash_value, context->state.st64, +// SHA512_DIGEST_LENGTH); +// bzero(context->buffer, SHA384_BLOCK_LENGTH); +// context->bitcount[0] = context->bitcount[1] = 0; +// } +// +// static void +// SHA384Update(SHA2_CTX *context, const u_int8_t *data, size_t len) +// { +// SHA512Update((SHA2_CTX *)context, data, len); +// } +// +// static void +// SHA384Final(u_int8_t digest[], SHA2_CTX *context) +// { +// u_int64_t *d = (u_int64_t *)digest; +// +// /* If no digest buffer is passed, we don't bother doing this: */ +// if (digest != NULL) { +// SHA512Last((SHA2_CTX *)context); +// +// /* Save the hash data for output: */ +// #if BYTE_ORDER == LITTLE_ENDIAN +// { +// /* Convert TO host byte order */ +// int j; +// for (j = 0; j < 6; j++) { +// REVERSE64(context->state.st64[j], +// context->state.st64[j]); +// *d++ = context->state.st64[j]; +// } +// } +// #else +// bcopy(context->state.st64, d, SHA384_DIGEST_LENGTH); +// #endif +// } +// +// /* Zero out state data */ +// bzero(context, sizeof(*context)); +// } + +int // lua-sha256 +lsha256(lua_State *L){ + SHA2_CTX ctx; + size_t l; + const u_int8_t* str = (const u_int8_t*)luaL_checklstring(L, 1, &l); + u_int8_t text[SHA256_DIGEST_LENGTH]; + SHA256Init(&ctx); + SHA256Update(&ctx, str, l); + SHA256Final(text, &ctx); + lua_pushlstring(L, (const char *)text, SHA256_DIGEST_LENGTH); + return 1; +} + +int // lua-sha512 +lsha512(lua_State *L){ + SHA2_CTX ctx; + size_t l; + const u_int8_t* str = (const u_int8_t*)luaL_checklstring(L, 1, &l); + u_int8_t text[SHA512_DIGEST_LENGTH]; + SHA512Init(&ctx); + SHA512Update(&ctx, str, l); + SHA512Final(text, &ctx); + lua_pushlstring(L, (const char *)text, SHA512_DIGEST_LENGTH); + return 1; +} + +#define BLOCKSIZE_128_256 64 + +static inline void +xor_key_128_256(uint8_t key[BLOCKSIZE_128_256], uint32_t xor) { + int i; + for (i=0;i SHA256_BLOCK_LENGTH) { + SHA2_CTX ctx; + SHA256Init(&ctx); + SHA256Update(&ctx, key, key_sz); + SHA256Final(rkey, &ctx); + key_sz = SHA256_DIGEST_LENGTH; + } else { + memcpy(rkey, key, key_sz); + } + + xor_key_128_256(rkey, 0x5c5c5c5c); + SHA256Init(&ctx1); + SHA256Update(&ctx1, rkey, BLOCKSIZE_128_256); + + xor_key_128_256(rkey, 0x5c5c5c5c ^ 0x36363636); + SHA256Init(&ctx2); + SHA256Update(&ctx2, rkey, BLOCKSIZE_128_256); + SHA256Update(&ctx2, text, text_sz); + SHA256Final(digest2, &ctx2); + + SHA256Update(&ctx1, digest2, SHA256_DIGEST_LENGTH); + SHA256Final(digest1, &ctx1); + + lua_pushlstring(L, (const char *)digest1, SHA256_DIGEST_LENGTH); + return 1; +} + +int // lua-hmac_sha512 +lhmac_sha512(lua_State *L){ + size_t key_sz = 0; + const uint8_t * key = (const uint8_t *)luaL_checklstring(L, 1, &key_sz); + size_t text_sz = 0; + const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 2, &text_sz); + SHA2_CTX ctx1, ctx2; + uint8_t digest1[SHA512_DIGEST_LENGTH]; + uint8_t digest2[SHA512_DIGEST_LENGTH]; + uint8_t rkey[BLOCKSIZE_384_512]; + memset(rkey, 0, BLOCKSIZE_384_512); + + if (key_sz > BLOCKSIZE_384_512) { + SHA2_CTX ctx; + SHA512Init(&ctx); + SHA512Update(&ctx, key, key_sz); + SHA512Final(rkey, &ctx); + key_sz = SHA512_DIGEST_LENGTH; + } else { + memcpy(rkey, key, key_sz); + } + + xor_key_384_512(rkey, 0x5c5c5c5c); + SHA512Init(&ctx1); + SHA512Update(&ctx1, rkey, BLOCKSIZE_384_512); + + xor_key_384_512(rkey, 0x5c5c5c5c ^ 0x36363636); + SHA512Init(&ctx2); + SHA512Update(&ctx2, rkey, BLOCKSIZE_384_512); + SHA512Update(&ctx2, text, text_sz); + SHA512Final(digest2, &ctx2); + + SHA512Update(&ctx1, digest2, SHA512_DIGEST_LENGTH); + SHA512Final(digest1, &ctx1); + + lua_pushlstring(L, (const char *)digest1, SHA512_DIGEST_LENGTH); + return 1; +} diff --git a/luaclib/src/lcrypt/sha2.h b/luaclib/src/lcrypt/sha2.h new file mode 100644 index 00000000..4cbf7bbf --- /dev/null +++ b/luaclib/src/lcrypt/sha2.h @@ -0,0 +1,81 @@ +/* + * FILE: sha2.h + * AUTHOR: Aaron D. Gifford + * + * Copyright (c) 2000-2001, Aaron D. Gifford + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $From: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $ + */ + +#ifndef _SHA2_H +#define _SHA2_H + +#include "attributes.h" +#include "../../../src/core.h" + +/*** SHA-256/384/512 Various Length Definitions ***********************/ +#define SHA256_BLOCK_LENGTH 64 +#define SHA256_DIGEST_LENGTH 32 +#define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2 + 1) +#define SHA384_BLOCK_LENGTH 128 +#define SHA384_DIGEST_LENGTH 48 +#define SHA384_DIGEST_STRING_LENGTH (SHA384_DIGEST_LENGTH * 2 + 1) +#define SHA512_BLOCK_LENGTH 128 +#define SHA512_DIGEST_LENGTH 64 +#define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1) + + +/*** SHA-256/384/512 Context Structure *******************************/ +typedef struct _SHA2_CTX { + union { + u_int32_t st32[8]; + u_int64_t st64[8]; + } state; + u_int64_t bitcount[2]; + u_int8_t buffer[SHA512_BLOCK_LENGTH]; +} SHA2_CTX; + +static void SHA256Init(SHA2_CTX *); +static void SHA256Update(SHA2_CTX *, const u_int8_t *, size_t) + _BOUNDED(__string__,2,3); +static void SHA256Final(u_int8_t[SHA256_DIGEST_LENGTH], SHA2_CTX *) + _BOUNDED(__minbytes__,1,SHA256_DIGEST_LENGTH); + +// static void SHA384Init(SHA2_CTX *); +// static void SHA384Update(SHA2_CTX *, const u_int8_t *, size_t) +// _BOUNDED(__string__,2,3); +// static void SHA384Final(u_int8_t[SHA384_DIGEST_LENGTH], SHA2_CTX *) +// _BOUNDED(__minbytes__,1,SHA384_DIGEST_LENGTH); + +static void SHA512Init(SHA2_CTX *); +static void SHA512Update(SHA2_CTX *, const u_int8_t *, size_t) + _BOUNDED(__string__,2,3); +static void SHA512Final(u_int8_t[SHA512_DIGEST_LENGTH], SHA2_CTX *) + _BOUNDED(__minbytes__,1,SHA512_DIGEST_LENGTH); + +#endif /* _SHA2_H */ diff --git a/luaclib/src/lcrypt/sha256.c b/luaclib/src/lcrypt/sha256.c deleted file mode 100644 index 30ed69b0..00000000 --- a/luaclib/src/lcrypt/sha256.c +++ /dev/null @@ -1,226 +0,0 @@ -#include "../../../src/core.h" - -#define SHA256_HASH_SIZE 32 - -typedef unsigned char BYTE; -typedef unsigned int WORD; - -typedef struct { - BYTE data[64]; // current 512-bit chunk of message data, just like a buffer - WORD datalen; // sign the data length of current chunk - unsigned long long bitlen; // the bit length of the total message - WORD state[8]; // store the middle state of hash abstract -} SHA256_CTX; - - -#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) -#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) - -#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) -#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) -#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) -#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) -#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) -#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) - -static const WORD K[64] = { - 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, - 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, - 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, - 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, - 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, - 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, - 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, - 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 -}; - -static inline -void sha256_transform(SHA256_CTX *ctx, const BYTE data[]) -{ - WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; - - // initialization - for (i = 0, j = 0; i < 16; ++i, j += 4) - m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); - for ( ; i < 64; ++i) - m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; - - a = ctx->state[0]; - b = ctx->state[1]; - c = ctx->state[2]; - d = ctx->state[3]; - e = ctx->state[4]; - f = ctx->state[5]; - g = ctx->state[6]; - h = ctx->state[7]; - - for (i = 0; i < 64; ++i) { - t1 = h + EP1(e) + CH(e,f,g) + K[i] + m[i]; - t2 = EP0(a) + MAJ(a,b,c); - h = g; - g = f; - f = e; - e = d + t1; - d = c; - c = b; - b = a; - a = t1 + t2; - } - - ctx->state[0] += a; - ctx->state[1] += b; - ctx->state[2] += c; - ctx->state[3] += d; - ctx->state[4] += e; - ctx->state[5] += f; - ctx->state[6] += g; - ctx->state[7] += h; -} - -static inline -void sha256_init(SHA256_CTX *ctx) -{ - ctx->datalen = 0; - ctx->bitlen = 0; - ctx->state[0] = 0x6a09e667; - ctx->state[1] = 0xbb67ae85; - ctx->state[2] = 0x3c6ef372; - ctx->state[3] = 0xa54ff53a; - ctx->state[4] = 0x510e527f; - ctx->state[5] = 0x9b05688c; - ctx->state[6] = 0x1f83d9ab; - ctx->state[7] = 0x5be0cd19; -} - -static inline -void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len) -{ - WORD i; - - for (i = 0; i < len; ++i) { - ctx->data[ctx->datalen] = data[i]; - ctx->datalen++; - if (ctx->datalen == 64) { - // 64 byte = 512 bit means the buffer ctx->data has fully stored one chunk of message - // so do the sha256 hash map for the current chunk - sha256_transform(ctx, ctx->data); - ctx->bitlen += 512; - ctx->datalen = 0; - } - } -} - -static inline -void sha256_final(SHA256_CTX *ctx, BYTE hash[]) -{ - WORD i; - - i = ctx->datalen; - - // Pad whatever data is left in the buffer. - if (ctx->datalen < 56) { - ctx->data[i++] = 0x80; // pad 10000000 = 0x80 - while (i < 56) - ctx->data[i++] = 0x00; - } - else { - ctx->data[i++] = 0x80; - while (i < 64) - ctx->data[i++] = 0x00; - sha256_transform(ctx, ctx->data); - memset(ctx->data, 0, 56); - } - - // Append to the padding the total message's length in bits and transform. - ctx->bitlen += ctx->datalen * 8; - ctx->data[63] = ctx->bitlen; - ctx->data[62] = ctx->bitlen >> 8; - ctx->data[61] = ctx->bitlen >> 16; - ctx->data[60] = ctx->bitlen >> 24; - ctx->data[59] = ctx->bitlen >> 32; - ctx->data[58] = ctx->bitlen >> 40; - ctx->data[57] = ctx->bitlen >> 48; - ctx->data[56] = ctx->bitlen >> 56; - sha256_transform(ctx, ctx->data); - - // copying the final state to the output hash(use big endian). - for (i = 0; i < 4; ++i) { - hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; - hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; - hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; - hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; - hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; - hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; - hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; - hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; - } - -} - -static inline -void sha256(SHA256_CTX *ctx, const uint8_t *buffer, size_t sz, uint8_t* hash){ - sha256_init(ctx); - sha256_update(ctx, buffer, sz); - sha256_final(ctx, hash); -} - -LUAMOD_API int -lsha256(lua_State *L) { - size_t sz = 0; - const uint8_t * buffer = (const uint8_t *)luaL_checklstring(L, 1, &sz); - uint8_t hash[SHA256_HASH_SIZE]; - SHA256_CTX ctx; - sha256(&ctx, buffer, sz, hash); - lua_pushlstring(L, (const char *)hash, SHA256_HASH_SIZE); - return 1; -} - -static inline -void xor_buf(uint8_t buf1[SHA256_HASH_SIZE], uint8_t buf2[SHA256_HASH_SIZE], size_t key_size) { - int i; - for(i = 0; i< key_size; i++){ - *(buf1 + i) = *(buf1 + i) ^ 0x5c; - *(buf2 + i) = *(buf2 + i) ^ 0x36; - } -} - -LUAMOD_API int -lhmac_sha256(lua_State *L) { - size_t len_key = 0; - const uint8_t * key = (const uint8_t *)luaL_checklstring(L, 1, &len_key); - size_t len = 0; - const uint8_t * data = (const uint8_t *)luaL_checklstring(L, 2, &len); - int block_size = 64; - int hash_size = 32; - int key_size = block_size; - unsigned char buf[block_size]; - unsigned char buf2[block_size]; - bzero(buf, block_size); - bzero(buf2, block_size); - if (len_key > block_size) { - key_size = hash_size; - SHA256_CTX ctx; - sha256(&ctx, data, len, buf); - memcpy(buf2, buf, key_size); - } else { - memcpy(buf, key, len_key); - memcpy(buf2, key, len_key); - } - xor_buf(buf, buf2, key_size); - size_t hash_buf_size = key_size + (len < hash_size ? hash_size : len); - unsigned char hash_buf[hash_buf_size]; - memcpy(hash_buf, buf2, key_size); - memcpy(hash_buf + key_size, data, len); - - SHA256_CTX ctx1, ctx2; - unsigned char hash_out[SHA256_HASH_SIZE]; - sha256(&ctx1, hash_buf, key_size + len, hash_out); - memcpy(hash_buf, buf, key_size); - memcpy(hash_buf + key_size, hash_out, hash_size); - - unsigned char hash_buffer[SHA256_HASH_SIZE]; - sha256(&ctx2, hash_buf, key_size + hash_size, hash_buffer); - - lua_pushlstring(L, (const char *)hash_buffer, SHA256_HASH_SIZE); - return 1; -} diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index 4ca95cfc..cb99adc7 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -6,25 +6,28 @@ local byte = string.byte local match = string.match local concat = table.concat -local sha1 = CRYPT.sha1 -local hmac_sha1 = CRYPT.hmac_sha1 +local md5 = CRYPT.md5 +local hmac64 = CRYPT.hmac64 +local hmac_md5 = CRYPT.hmac_md5 +local hmac64_md5 = CRYPT.hmac64_md5 +local sha1 = CRYPT.sha1 local sha256 = CRYPT.sha256 -local hmac_sha256 = CRYPT.hmac_sha256 +local sha512 = CRYPT.sha512 -local xor_str = CRYPT.xor_str +local hmac_sha1 = CRYPT.hmac_sha1 +local hmac_sha256 = CRYPT.hmac_sha256 +local hmac_sha512 = CRYPT.hmac_sha512 local crc32 = CRYPT.crc32 local crc64 = CRYPT.crc64 -local randomkey = CRYPT.randomkey +local xor_str = CRYPT.xor_str local hashkey = CRYPT.hashkey +local randomkey = CRYPT.randomkey local hmac_hash = CRYPT.hmac_hash -local hmac64 = CRYPT.hmac64 -local hmac64_md5 = CRYPT.hmac64_md5 - local base64encode = CRYPT.base64encode local base64decode = CRYPT.base64decode @@ -40,8 +43,8 @@ local dhexchange = CRYPT.dhexchange local crypt = {} -function crypt.sha1(str, hex) - local hash = sha1(str) +function crypt.md5(str, hex) + local hash = md5(str) if hash and hex then local tab = new_tab(#hash, 0) for i = 1, #hash do @@ -52,8 +55,8 @@ function crypt.sha1(str, hex) return hash end -function crypt.sha256 (str, hex) - local hash = sha256(str) +function crypt.hmac_md5 (key, text, hex) + local hash = hmac_md5(key, text) if hash and hex then local tab = new_tab(#hash, 0) for i = 1, #hash do @@ -64,8 +67,8 @@ function crypt.sha256 (str, hex) return hash end -function crypt.xor_str (str, sec, hex) - local hash = xor_str(str, sec) +function crypt.sha1(str, hex) + local hash = sha1(str) if hash and hex then local tab = new_tab(#hash, 0) for i = 1, #hash do @@ -76,8 +79,8 @@ function crypt.xor_str (str, sec, hex) return hash end -function crypt.randomkey(hex) - local hash = randomkey() +function crypt.sha256 (str, hex) + local hash = sha256(str) if hash and hex then local tab = new_tab(#hash, 0) for i = 1, #hash do @@ -88,8 +91,8 @@ function crypt.randomkey(hex) return hash end -function crypt.hashkey (key, hex) - local hash = hashkey(key) +function crypt.sha512 (str, hex) + local hash = sha512(str) if hash and hex then local tab = new_tab(#hash, 0) for i = 1, #hash do @@ -100,6 +103,7 @@ function crypt.hashkey (key, hex) return hash end + function crypt.hmac_sha1 (key, text, hex) local hash = hmac_sha1(key, text) if hash and hex then @@ -124,6 +128,54 @@ function crypt.hmac_sha256 (key, text, hex) return hash end +function crypt.hmac_sha512 (key, text, hex) + local hash = hmac_sha512(key, text) + if hash and hex then + local tab = new_tab(#hash, 0) + for i = 1, #hash do + tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) + end + return concat(tab) + end + return hash +end + +function crypt.xor_str (str, sec, hex) + local hash = xor_str(str, sec) + if hash and hex then + local tab = new_tab(#hash, 0) + for i = 1, #hash do + tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) + end + return concat(tab) + end + return hash +end + +function crypt.randomkey(hex) + local hash = randomkey() + if hash and hex then + local tab = new_tab(#hash, 0) + for i = 1, #hash do + tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) + end + return concat(tab) + end + return hash +end + +function crypt.hashkey (key, hex) + local hash = hashkey(key) + if hash and hex then + local tab = new_tab(#hash, 0) + for i = 1, #hash do + tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) + end + return concat(tab) + end + return hash +end + function crypt.hmac_hash (key, text, hex) local hash = hmac_hash(key, text) if hash and hex then From 075b74ae569f2288fb9f84ab99b940c1cc08bc09 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 3 Jul 2019 02:27:39 +0800 Subject: [PATCH 199/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0test=5Fcrypt.lua?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_crypt.lua | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/script/test_crypt.lua b/script/test_crypt.lua index 8b981cec..d2ec905a 100644 --- a/script/test_crypt.lua +++ b/script/test_crypt.lua @@ -6,6 +6,30 @@ Log:DEBUG("测试crc32",crypt.crc32("admin")) -- 测试crc64编码 Log:DEBUG("测试crc64", crypt.crc64("admin")) +-- 测试md5编码 +Log:DEBUG("测试md5", crypt.md5("admin", true)) + +-- 测试sha1编码, 第二个参数表示进行hex +Log:DEBUG("测试sha1", crypt.sha1("admin", true)) + +-- 测试sha256编码, 第二个参数表示进行hex +Log:DEBUG("测试sha256", crypt.sha256("admin", true)) + +-- 测试sha512编码 +Log:DEBUG("测试sha512", crypt.sha512("admin", true)) + +-- 测试hmac_sha1编码, 第二个参数表示进行hex +Log:DEBUG("测试hmac_sha1", crypt.hmac_sha1("admin", "123", true)) + +-- 测试hmac_sha256编码, 第二个参数表示进行hex +Log:DEBUG("测试hmac_sha256", crypt.hmac_sha256("admin", "123", true)) + +-- 测试hmac_sha512编码, 第二个参数表示进行hex +Log:DEBUG("测试hmac_sha512", crypt.hmac_sha512("admin", "123", true)) + +-- 测试hmac_md5编码 +Log:DEBUG("测试hmac_md5", crypt.hmac_md5("admin", "123", true)) + -- 测试hmac64编码 Log:DEBUG("测试hmac64", crypt.hmac64("12345678", "abcdefgh", true)) @@ -21,19 +45,8 @@ Log:DEBUG("测试randomkey", crypt.randomkey(true)) -- 测试hashkey编码 Log:DEBUG("测试hashkey", crypt.hashkey("admin", true)) --- 测试sha1编码, 第二个参数表示进行hex -Log:DEBUG("测试sha1", crypt.sha1("admin", true)) --- 测试hmac_sha1编码, 第二个参数表示进行hex -Log:DEBUG("测试hmac_sha1", crypt.hmac_sha1("admin", "123", true)) - --- 测试sha256编码, 第二个参数表示进行hex -Log:DEBUG("测试sha256", crypt.sha256("admin", true)) --- 测试hmac_sha256编码, 第二个参数表示进行hex -Log:DEBUG("测试hmac_sha256", crypt.hmac_sha256("admin", "123", true)) - -- 测试desencode编码 Log:DEBUG("测试desencode", crypt.desencode("12345678", "87654321")) - -- 测试desdecode编码 Log:DEBUG("测试desdecode", crypt.desdecode("12345678", crypt.desencode("12345678", "87654321")) == "87654321") From 33e02b34ab744bb143931829d6e9597a43e35bbf Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 3 Jul 2019 02:32:45 +0800 Subject: [PATCH 200/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0lcrypt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/Makefile | 2 +- luaclib/src/lcrypt/md5.h | 14 +++++++------- luaclib/src/lcrypt/sha2.h | 16 ++++++++-------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/luaclib/src/lcrypt/Makefile b/luaclib/src/lcrypt/Makefile index 2ed7b789..cdfe5304 100644 --- a/luaclib/src/lcrypt/Makefile +++ b/luaclib/src/lcrypt/Makefile @@ -9,7 +9,7 @@ default : CC = cc -CFLAGS = -O3 -Wall -shared -fPIC +CFLAGS = -O3 -Wall -shared -fPIC -fno-strict-aliasing DLL = -lcore diff --git a/luaclib/src/lcrypt/md5.h b/luaclib/src/lcrypt/md5.h index d042d21f..ede61288 100644 --- a/luaclib/src/lcrypt/md5.h +++ b/luaclib/src/lcrypt/md5.h @@ -26,12 +26,12 @@ typedef struct MD5Context { } MD5_CTX; static void MD5Init(MD5_CTX *); -static void MD5Update(MD5_CTX *, const u_int8_t *, size_t) - _BOUNDED(__string__,2,3); -static void MD5Final(u_int8_t [MD5_DIGEST_LENGTH], MD5_CTX *) - _BOUNDED(__minbytes__,1,MD5_DIGEST_LENGTH); -static void MD5Transform(u_int32_t [4], const u_int8_t [MD5_BLOCK_LENGTH]) - _BOUNDED(__minbytes__,1,4) - _BOUNDED(__minbytes__,2,MD5_BLOCK_LENGTH); +static void MD5Update(MD5_CTX *, const u_int8_t *, size_t); + // _BOUNDED(__string__,2,3); +static void MD5Final(u_int8_t [MD5_DIGEST_LENGTH], MD5_CTX *); + // _BOUNDED(__minbytes__,1,MD5_DIGEST_LENGTH); +static void MD5Transform(u_int32_t [4], const u_int8_t [MD5_BLOCK_LENGTH]); + // _BOUNDED(__minbytes__,1,4) + // _BOUNDED(__minbytes__,2,MD5_BLOCK_LENGTH); #endif /* _MD5_H_ */ diff --git a/luaclib/src/lcrypt/sha2.h b/luaclib/src/lcrypt/sha2.h index 4cbf7bbf..51368d9c 100644 --- a/luaclib/src/lcrypt/sha2.h +++ b/luaclib/src/lcrypt/sha2.h @@ -61,10 +61,10 @@ typedef struct _SHA2_CTX { } SHA2_CTX; static void SHA256Init(SHA2_CTX *); -static void SHA256Update(SHA2_CTX *, const u_int8_t *, size_t) - _BOUNDED(__string__,2,3); -static void SHA256Final(u_int8_t[SHA256_DIGEST_LENGTH], SHA2_CTX *) - _BOUNDED(__minbytes__,1,SHA256_DIGEST_LENGTH); +static void SHA256Update(SHA2_CTX *, const u_int8_t *, size_t); + // _BOUNDED(__string__,2,3); +static void SHA256Final(u_int8_t[SHA256_DIGEST_LENGTH], SHA2_CTX *); + // _BOUNDED(__minbytes__,1,SHA256_DIGEST_LENGTH); // static void SHA384Init(SHA2_CTX *); // static void SHA384Update(SHA2_CTX *, const u_int8_t *, size_t) @@ -73,9 +73,9 @@ static void SHA256Final(u_int8_t[SHA256_DIGEST_LENGTH], SHA2_CTX *) // _BOUNDED(__minbytes__,1,SHA384_DIGEST_LENGTH); static void SHA512Init(SHA2_CTX *); -static void SHA512Update(SHA2_CTX *, const u_int8_t *, size_t) - _BOUNDED(__string__,2,3); -static void SHA512Final(u_int8_t[SHA512_DIGEST_LENGTH], SHA2_CTX *) - _BOUNDED(__minbytes__,1,SHA512_DIGEST_LENGTH); +static void SHA512Update(SHA2_CTX *, const u_int8_t *, size_t); + // _BOUNDED(__string__,2,3); +static void SHA512Final(u_int8_t[SHA512_DIGEST_LENGTH], SHA2_CTX *); + // _BOUNDED(__minbytes__,1,SHA512_DIGEST_LENGTH); #endif /* _SHA2_H */ From 4a970cedc1d920491d531f34724c3d270e8c8501 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 4 Jul 2019 03:51:02 +0800 Subject: [PATCH 201/956] =?UTF-8?q?=E9=80=82=E9=85=8D=E5=B9=B3=E5=8F=B0?= =?UTF-8?q?=E5=AE=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 25062fdd..37d6dd8a 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -43,7 +43,7 @@ void SETSOCKETOPT(int sockfd, int mode){ return exit(-1); } -#if defined(linux) +#ifdef TCP_DEFER_ACCEPT if (!mode) { /* 开启延迟Accept, 没数据来之前不回调accept */ ret = setsockopt(sockfd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &Enable, sizeof(Enable)); @@ -52,7 +52,9 @@ void SETSOCKETOPT(int sockfd, int mode){ return exit(-1); } } +#endif +#ifdef TCP_KEEPIDLE /* 设置 TCP keepalive 空闲时间 */ int keepidle = 30; ret = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle , sizeof(keepidle)); @@ -60,9 +62,9 @@ void SETSOCKETOPT(int sockfd, int mode){ LOG("ERROR", "TCP_KEEPIDLE 设置失败."); return exit(-1); } - #endif +#ifdef TCP_KEEPCNT /* 设置 TCP keepalive 探测总次数 */ int keepcount = 3; ret = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, &keepcount , sizeof(keepcount)); @@ -70,7 +72,9 @@ void SETSOCKETOPT(int sockfd, int mode){ LOG("ERROR", "TCP_KEEPCNT 设置失败."); return exit(-1); } +#endif +#ifdef TCP_KEEPINTVL /* 设置 TCP keepalive 每次探测间隔时间 */ int keepinterval = 5; ret = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, &keepinterval , sizeof(keepinterval)); @@ -78,6 +82,7 @@ void SETSOCKETOPT(int sockfd, int mode){ LOG("ERROR", "TCP_KEEPINTVL 设置失败."); return exit(-1); } +#endif } From c33f3cd566b3844b1cf0d73d18fc8b55924cbcc7 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 4 Jul 2019 04:31:57 +0800 Subject: [PATCH 202/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=9E=84=E5=BB=BA?= =?UTF-8?q?=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build.sh b/build.sh index f8e5b2fb..22082c62 100755 --- a/build.sh +++ b/build.sh @@ -6,12 +6,12 @@ rm -rf build && mkdir build && cd build git clone https://github.com/CandyMi/lua -b v5.3.5 git clone https://github.com/CandyMi/libev -b v4.25 -cd ${current}/build/lua && - make all MYCFLAGS=-fPIC MYCFLAGS+=-DLUA_USE_DLOPEN SYSLIBS=-ldl && - cp lua.h luaconf.h lualib.h lauxlib.h /usr/local/include && cp liblua.* /usr/local/lib - cd ${current}/build/libev && sh autogen.sh && ./configure --prefix=/usr/local && make && make install +cd ${current}/build/lua && + make all MYCFLAGS=-fPIC MYCFLAGS+=-DLUA_USE_DLOPEN SYSLIBS=-ldl && + cp lua.h luaconf.h lualib.h lauxlib.h /usr/local/include && cp liblua.* /usr/local/lib + cd ${current} && rm -rf build From 1f56b5942f3fa4f4c7486598be6a4b5639eee24f Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 4 Jul 2019 04:52:19 +0800 Subject: [PATCH 203/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=B8=AAtcp?= =?UTF-8?q?=20sockopt=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 37d6dd8a..b8a4ecd3 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -15,6 +15,8 @@ void SETSOCKETOPT(int sockfd, int mode){ int ret = 0; + int no = 0; + /* 设置非阻塞 */ ret = non_blocking(sockfd); if (ret) { @@ -22,6 +24,12 @@ void SETSOCKETOPT(int sockfd, int mode){ return exit(-1); } + ret = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&no, sizeof(no)); + if (ret){ + LOG("ERROR", "IPV6_V6ONLY 关闭失败."); + return exit(-1); + } + /* 地址/端口重用 */ ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &Enable, sizeof(Enable)); if (ret){ @@ -102,8 +110,8 @@ create_server_fd(int port, int backlog){ SA.sin6_port = htons(port); SA.sin6_addr = in6addr_any; - int bind_siccess = bind(sockfd, (struct sockaddr *)&SA, sizeof(struct sockaddr_in6)); - if (0 > bind_siccess) { + int bind_success = bind(sockfd, (struct sockaddr *)&SA, sizeof(struct sockaddr_in6)); + if (0 > bind_success) { return -1; /* 绑定套接字失败 */ } From 16cf53eee0ba033e099e23fc57a7b13ece1c5d3c Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 4 Jul 2019 05:47:05 +0800 Subject: [PATCH 204/956] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E4=B8=80=E6=AE=B5?= =?UTF-8?q?=E5=86=97=E4=BD=99=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/TCP.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index 00c47a5a..e3a5090c 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -269,12 +269,11 @@ end function TCP:connect(domain, port) local ok, IP = dns_resolve(domain) if not ok then - return nil, "Can't resolve this domain or ip:"..domain + return nil, "Can't resolve this domain or ip:"..(domain or IP or "") end self.fd = tcp_new_client_fd(IP, port) if not self.fd then - Log:ERROR("Connect This IP or Port Faild!"..domain, IP) - return nil, "Connect This host fault! :" + return nil, "Connect This host fault! "..(domain or "no domain")..":"..(port or "no port") end local co = co_self() self.CONNECT_IO = tcp_pop() From f78bc1acdf32af456b787903dfc1d516533d8297 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 4 Jul 2019 05:53:18 +0800 Subject: [PATCH 205/956] =?UTF-8?q?=E4=BC=98=E5=8C=96tcp=20listen=E4=BB=A3?= =?UTF-8?q?=E7=A0=81,=20=E5=A2=9E=E5=8A=A0=E6=A3=80=E6=9F=A5=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E7=9A=84=E6=83=85=E5=86=B5.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/init.lua | 4 ++-- lualib/internal/TCP.lua | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua index 055df85a..f35e9f08 100644 --- a/lualib/httpd/init.lua +++ b/lualib/httpd/init.lua @@ -163,10 +163,10 @@ function httpd:listen(ip, port, backlog) if type(backlog) == 'number' then self.sock:set_backlog(toint(backlog)) end - self.sock:listen(ip or "0.0.0.0", toint(port), function (fd, ipaddr) + local ok, err = self.sock:listen(ip or "0.0.0.0", toint(port), function (fd, ipaddr) return EVENT_DISPATCH(fd, match(ipaddr, '^::[f]+:(.+)') or ipaddr, self) end) - return self + return assert(ok and self, err) end -- 正确的运行方式 diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index e3a5090c..f2ab99bc 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -253,7 +253,7 @@ function TCP:listen(ip, port, cb) self.LISTEN_IO = tcp_pop() self.fd = tcp_new_server_fd(ip, port, self._backlog) if not self.fd then - return Log:ERROR("this IP and port Create A bind or listen method Faild! :) ") + return nil, "Listen port failed. Please check if the port is already occupied." end self.co = co_new(function (fd, ipaddr) while 1 do @@ -263,7 +263,7 @@ function TCP:listen(ip, port, cb) end end end) - return tcp_listen(self.LISTEN_IO, self.fd, self.co) + return true, tcp_listen(self.LISTEN_IO, self.fd, self.co) end function TCP:connect(domain, port) From 2d1d34311604eb37106b133867fa09ce3923c58f Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 4 Jul 2019 07:07:50 +0800 Subject: [PATCH 206/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=89=B9=E6=AE=8A?= =?UTF-8?q?=E6=83=85=E5=86=B5=E4=B8=8B,=20sockaddr=5Fin6=E6=9C=89=E9=9D=9E?= =?UTF-8?q?=E6=B3=95=E6=95=B0=E6=8D=AE=E7=9A=84=E9=97=AE=E9=A2=98.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index b8a4ecd3..04755bf2 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -15,7 +15,7 @@ void SETSOCKETOPT(int sockfd, int mode){ int ret = 0; - int no = 0; + int On = 0; /* 设置非阻塞 */ ret = non_blocking(sockfd); @@ -24,7 +24,7 @@ void SETSOCKETOPT(int sockfd, int mode){ return exit(-1); } - ret = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&no, sizeof(no)); + ret = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&On, sizeof(On)); if (ret){ LOG("ERROR", "IPV6_V6ONLY 关闭失败."); return exit(-1); @@ -106,11 +106,13 @@ create_server_fd(int port, int backlog){ SETSOCKETOPT(sockfd, SERVER); struct sockaddr_in6 SA; + memset(&SA, 0x0, sizeof(SA)); + SA.sin6_family = AF_INET6; SA.sin6_port = htons(port); SA.sin6_addr = in6addr_any; - int bind_success = bind(sockfd, (struct sockaddr *)&SA, sizeof(struct sockaddr_in6)); + int bind_success = bind(sockfd, (struct sockaddr *)&SA, sizeof(SA)); if (0 > bind_success) { return -1; /* 绑定套接字失败 */ } @@ -134,7 +136,9 @@ create_client_fd(const char *ipaddr, int port){ /* socket option set */ SETSOCKETOPT(sockfd, CLIENT); - struct sockaddr_in6 SA; + struct sockaddr_in6 SA; + memset(&SA, 0x0, sizeof(SA)); + SA.sin6_family = AF_INET6; SA.sin6_port = htons(port); inet_pton(AF_INET6, ipaddr, &SA.sin6_addr); @@ -147,7 +151,6 @@ create_client_fd(const char *ipaddr, int port){ } - static void TCP_IO_CB(CORE_P_ core_io *io, int revents) { @@ -200,7 +203,7 @@ IO_ACCEPT(CORE_P_ core_io *io, int revents){ for(;;) { errno = 0; struct sockaddr_in6 SA; - socklen_t slen = sizeof(struct sockaddr_in6); + socklen_t slen = sizeof(SA); int client = accept(io->fd, (struct sockaddr*)&SA, &slen); if (0 >= client) { if (errno != EAGAIN) From ae6ae12939eb8663c4ceb5f0306add34ecfdcc0f Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 4 Jul 2019 10:36:42 +0800 Subject: [PATCH 207/956] =?UTF-8?q?=E4=BC=98=E5=8C=96lua=E7=9A=84C?= =?UTF-8?q?=E5=BA=93=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 8 -------- luaclib/src/ludp.c | 16 ++++++++++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 04755bf2..a511bf4b 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -15,8 +15,6 @@ void SETSOCKETOPT(int sockfd, int mode){ int ret = 0; - int On = 0; - /* 设置非阻塞 */ ret = non_blocking(sockfd); if (ret) { @@ -24,12 +22,6 @@ void SETSOCKETOPT(int sockfd, int mode){ return exit(-1); } - ret = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&On, sizeof(On)); - if (ret){ - LOG("ERROR", "IPV6_V6ONLY 关闭失败."); - return exit(-1); - } - /* 地址/端口重用 */ ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &Enable, sizeof(Enable)); if (ret){ diff --git a/luaclib/src/ludp.c b/luaclib/src/ludp.c index 29145e6f..08706cd9 100644 --- a/luaclib/src/ludp.c +++ b/luaclib/src/ludp.c @@ -18,12 +18,18 @@ udp_socket_new(const char *ipaddr, int port){ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &ENABLE, sizeof(ENABLE)); struct sockaddr_in6 SA; + memset(&SA, 0x0, sizeof(SA)); + SA.sin6_family = AF_INET6; SA.sin6_port = htons(port); inet_pton(AF_INET6, ipaddr, &SA.sin6_addr); - connect(sockfd, (struct sockaddr*)&SA, sizeof(SA)); - + int ret = connect(sockfd, (struct sockaddr*)&SA, sizeof(SA)); + if (ret == -1) { + close(sockfd); + LOG("ERROR", strerror(errno)); + return -1; + } return sockfd; } @@ -91,13 +97,15 @@ int udp_connect(lua_State *L){ const char *ip = lua_tostring(L, 1); - if(!ip) {lua_settop(L, 0); return 0;} + if(!ip) return 0; int port = lua_tointeger(L, 2); - if(!port) {lua_settop(L, 0); return 0;} + if(!port) return 0; int fd = udp_socket_new(ip, port); + if (0 >= fd) return 0; + lua_pushinteger(L, fd > 0 ? fd : -1); return 1; From 1677e8d95a05370152f6ec09971b1dec894d809a Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 4 Jul 2019 20:57:37 +0800 Subject: [PATCH 208/956] =?UTF-8?q?=E4=BC=98=E5=8C=96socket=E5=88=9B?= =?UTF-8?q?=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 38 ++++++++++++++++++++++---------------- luaclib/src/ludp.c | 34 +++++++++++++++++++++++++--------- 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index a511bf4b..b2e517fa 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -22,12 +22,19 @@ void SETSOCKETOPT(int sockfd, int mode){ return exit(-1); } - /* 地址/端口重用 */ + /* 地址重用 */ ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &Enable, sizeof(Enable)); - if (ret){ - LOG("ERROR", "SO_REUSEADDR 设置失败."); - return exit(-1); - } + if (ret) { + LOG("ERROR", "设置 SO_REUSEADDR 失败."); + return exit(-1); + } + + /* 端口重用 */ + ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &Enable, sizeof(Enable)); + if (ret) { + LOG("ERROR", "设置 SO_REUSEPORT 失败."); + return exit(-1); + } /* 关闭小包延迟合并算法 */ ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &Enable, sizeof(Enable)); @@ -89,8 +96,8 @@ void SETSOCKETOPT(int sockfd, int mode){ /* server fd */ static int create_server_fd(int port, int backlog){ - errno = 0; - + errno = 0; + /* 建立 TCP Server Socket */ int sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); if (0 >= sockfd) return -1; @@ -104,24 +111,22 @@ create_server_fd(int port, int backlog){ SA.sin6_port = htons(port); SA.sin6_addr = in6addr_any; + /* 绑定套接字失败 */ int bind_success = bind(sockfd, (struct sockaddr *)&SA, sizeof(SA)); - if (0 > bind_success) { - return -1; /* 绑定套接字失败 */ - } + if (0 > bind_success) return -1; + /* 监听套接字失败 */ int listen_success = listen(sockfd, backlog); - if (0 > listen_success) { - return -1; /* 监听套接字失败 */ - } + if (0 > listen_success) return -1; + return sockfd; } /* client fd */ static int create_client_fd(const char *ipaddr, int port){ - errno = 0; - - /* 建立socket */ + errno = 0; + /* 建立 TCP Client Socket */ int sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); if (0 >= sockfd) return -1; @@ -134,6 +139,7 @@ create_client_fd(const char *ipaddr, int port){ SA.sin6_family = AF_INET6; SA.sin6_port = htons(port); inet_pton(AF_INET6, ipaddr, &SA.sin6_addr); + connect(sockfd, (struct sockaddr*)&SA, sizeof(SA)); if (errno != EINPROGRESS){ close(sockfd); diff --git a/luaclib/src/ludp.c b/luaclib/src/ludp.c index 08706cd9..1c301e4a 100644 --- a/luaclib/src/ludp.c +++ b/luaclib/src/ludp.c @@ -2,20 +2,36 @@ #include "../../src/core.h" +static inline +void SETSOCKETOPT(int sockfd) { + /* 设置非阻塞 */ + non_blocking(sockfd); + + int Enable = 1; + + int ret = 0; + /* 端口重用 */ + ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &Enable, sizeof(Enable)); + if (ret) { + LOG("ERROR", "设置 SO_REUSEADDR 失败."); + return exit(-1); + } + + ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &Enable, sizeof(Enable)); + if (ret) { + LOG("ERROR", "设置 SO_REUSEPORT 失败."); + return exit(-1); + } +} + int udp_socket_new(const char *ipaddr, int port){ - errno = 0; - /* 建立socket*/ - int sockfd = socket(AF_INET6, SOCK_DGRAM, 0); + /* 建立 UDP Socket */ + int sockfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); if (0 >= sockfd) return -1; - /* 设置非阻塞 */ - non_blocking(sockfd); - - int ENABLE = 1; - /* 端口重用 */ - setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &ENABLE, sizeof(ENABLE)); + SETSOCKETOPT(sockfd); struct sockaddr_in6 SA; memset(&SA, 0x0, sizeof(SA)); From b26d99d7bd4ba0d8fdfd8c304ff799b5e1bc5738 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 4 Jul 2019 21:02:29 +0800 Subject: [PATCH 209/956] =?UTF-8?q?=E5=88=A0=E9=99=A4=E4=B8=80=E6=AE=B5?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E7=9A=84=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/dns.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/lualib/protocol/dns.lua b/lualib/protocol/dns.lua index 1e03af93..ed14b46d 100644 --- a/lualib/protocol/dns.lua +++ b/lualib/protocol/dns.lua @@ -227,7 +227,6 @@ local function dns_query(domain) -- print("开始解析域名:["..domain.."], 开始时间: ", start) local dns_client, msg = get_dns_client() if not dns_client then - dns_client:close() check_wait(domain, wlist, nil, msg) return nil, msg end From 26a4dfebd0d71fe5ea9c720a2619f22770e82742 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 4 Jul 2019 21:05:00 +0800 Subject: [PATCH 210/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0UDP=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ludp.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/luaclib/src/ludp.c b/luaclib/src/ludp.c index 1c301e4a..29cdb1c5 100644 --- a/luaclib/src/ludp.c +++ b/luaclib/src/ludp.c @@ -4,24 +4,30 @@ static inline void SETSOCKETOPT(int sockfd) { + int Enable = 1; + + int ret = 0; + /* 设置非阻塞 */ non_blocking(sockfd); - int Enable = 1; - - int ret = 0; +#ifdef SO_REUSEADDR /* 端口重用 */ ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &Enable, sizeof(Enable)); if (ret) { LOG("ERROR", "设置 SO_REUSEADDR 失败."); return exit(-1); } +#endif +#ifdef SO_REUSEPORT ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &Enable, sizeof(Enable)); if (ret) { LOG("ERROR", "设置 SO_REUSEPORT 失败."); return exit(-1); } +#endif + } int From 35419ec76fee5196b0d1a7972460b9d85a402e23 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 4 Jul 2019 21:08:55 +0800 Subject: [PATCH 211/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E5=85=BC=E5=AE=B9=E6=80=A7=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index b2e517fa..f6d918d1 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -22,19 +22,25 @@ void SETSOCKETOPT(int sockfd, int mode){ return exit(-1); } +#ifdef SO_REUSEADDR /* 地址重用 */ ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &Enable, sizeof(Enable)); if (ret) { LOG("ERROR", "设置 SO_REUSEADDR 失败."); return exit(-1); } +#endif - /* 端口重用 */ - ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &Enable, sizeof(Enable)); - if (ret) { - LOG("ERROR", "设置 SO_REUSEPORT 失败."); - return exit(-1); - } +#ifdef SO_REUSEPORT + if (mode == SERVER) { + /* 端口重用 */ + ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &Enable, sizeof(Enable)); + if (ret) { + LOG("ERROR", "设置 SO_REUSEPORT 失败."); + return exit(-1); + } + } +#endif /* 关闭小包延迟合并算法 */ ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &Enable, sizeof(Enable)); @@ -51,7 +57,7 @@ void SETSOCKETOPT(int sockfd, int mode){ } #ifdef TCP_DEFER_ACCEPT - if (!mode) { + if (mode == SERVER) { /* 开启延迟Accept, 没数据来之前不回调accept */ ret = setsockopt(sockfd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &Enable, sizeof(Enable)); if (ret){ From c043e8bcfd5d30c1913e17e76f79a648c0fe9e78 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 4 Jul 2019 23:18:01 +0800 Subject: [PATCH 212/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0UDP=E5=AF=B9IP?= =?UTF-8?q?=E8=BD=AC=E6=8D=A2=E4=B8=8Econnect=E7=9A=84=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ludp.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/luaclib/src/ludp.c b/luaclib/src/ludp.c index 29cdb1c5..aebd7376 100644 --- a/luaclib/src/ludp.c +++ b/luaclib/src/ludp.c @@ -44,24 +44,28 @@ udp_socket_new(const char *ipaddr, int port){ SA.sin6_family = AF_INET6; SA.sin6_port = htons(port); - inet_pton(AF_INET6, ipaddr, &SA.sin6_addr); - + int error = inet_pton(AF_INET6, ipaddr, &SA.sin6_addr); + if (0 >= error) { + LOG("ERROR", strerror(errno)); + close(sockfd); + return -1; + } int ret = connect(sockfd, (struct sockaddr*)&SA, sizeof(SA)); if (ret == -1) { - close(sockfd); LOG("ERROR", strerror(errno)); + close(sockfd); return -1; } return sockfd; } static void -UDP_IO_CB(CORE_P_ ev_io *io, int revents){ +UDP_IO_CB(CORE_P_ core_io *io, int revents){ int status = 0; if (revents & EV_ERROR) { - LOG("ERROR", "Recevied a ev_io object internal error from libev."); + LOG("ERROR", "Recevied a core_io object internal error from libev."); return ; } @@ -137,7 +141,7 @@ udp_connect(lua_State *L){ int udp_start(lua_State *L){ - ev_io *io = (ev_io *) luaL_testudata(L, 1, "__UDP__"); + core_io *io = (core_io *) luaL_testudata(L, 1, "__UDP__"); if(!io) return 0; int fd = lua_tointeger(L, 2); @@ -160,7 +164,7 @@ udp_start(lua_State *L){ int udp_stop(lua_State *L){ - ev_io *io = (ev_io *) luaL_testudata(L, 1, "__UDP__"); + core_io *io = (core_io *) luaL_testudata(L, 1, "__UDP__"); if(!io) return 0; core_io_stop(CORE_LOOP_ io); @@ -185,7 +189,7 @@ udp_close(lua_State *L){ int udp_new(lua_State *L){ - ev_io *io = (ev_io *) lua_newuserdata(L, sizeof(ev_io)); + core_io *io = (core_io *) lua_newuserdata(L, sizeof(core_io)); if(!io) return 0; From b997f64f09261d9935452033976f6f35dea5add1 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 4 Jul 2019 23:52:37 +0800 Subject: [PATCH 213/956] =?UTF-8?q?=E4=BC=98=E5=8C=96ludp=E4=B8=8Eltcp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 43 ++++++++++++++++++++++++------------------- luaclib/src/ludp.c | 17 +++++++++-------- 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index f6d918d1..9a7ef86a 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -144,13 +144,18 @@ create_client_fd(const char *ipaddr, int port){ SA.sin6_family = AF_INET6; SA.sin6_port = htons(port); - inet_pton(AF_INET6, ipaddr, &SA.sin6_addr); - - connect(sockfd, (struct sockaddr*)&SA, sizeof(SA)); - if (errno != EINPROGRESS){ + int error = inet_pton(AF_INET6, ipaddr, &SA.sin6_addr); + if (0 >= error) { + LOG("ERROR", strerror(errno)); close(sockfd); return -1; } + + int ret = connect(sockfd, (struct sockaddr*)&SA, sizeof(SA)); + if (ret < 0 && errno != EINPROGRESS){ + close(sockfd); + return -1; + } return sockfd; } @@ -231,7 +236,7 @@ IO_ACCEPT(CORE_P_ core_io *io, int revents){ } } -int +static int tcp_read(lua_State *L){ errno = 0; @@ -264,7 +269,7 @@ tcp_read(lua_State *L){ return 0; } -int +static int tcp_sslread(lua_State *L){ errno = 0; @@ -296,7 +301,7 @@ tcp_sslread(lua_State *L){ return 0; } -int +static int tcp_write(lua_State *L){ errno = 0; @@ -325,7 +330,7 @@ tcp_write(lua_State *L){ return 0; } -int +static int tcp_sslwrite(lua_State *L){ SSL *ssl = lua_touserdata(L, 1); @@ -350,7 +355,7 @@ tcp_sslwrite(lua_State *L){ return 0; } -int +static int new_server_fd(lua_State *L){ const char *ip = lua_tostring(L, 1); if(!ip) return 0; @@ -368,7 +373,7 @@ new_server_fd(lua_State *L){ return 1; } -int +static int new_client_fd(lua_State *L){ const char *ip = lua_tostring(L, 1); if(!ip) return 0; @@ -384,7 +389,7 @@ new_client_fd(lua_State *L){ return 1; } -int +static int tcp_listen(lua_State *L){ core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); if(!io) return 0; @@ -406,7 +411,7 @@ tcp_listen(lua_State *L){ return 0; } -int +static int tcp_connect(lua_State *L){ core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); @@ -430,7 +435,7 @@ tcp_connect(lua_State *L){ } -int +static int tcp_sslconnect(lua_State *L){ SSL *ssl = (SSL*) lua_touserdata(L, 1); @@ -452,7 +457,7 @@ tcp_sslconnect(lua_State *L){ } -int +static int tcp_start(lua_State *L){ core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); @@ -480,7 +485,7 @@ tcp_start(lua_State *L){ } -int +static int ssl_new(lua_State *L){ int fd = lua_tointeger(L, 1); @@ -502,7 +507,7 @@ ssl_new(lua_State *L){ return 2; } -int +static int ssl_free(lua_State *L){ SSL_CTX *ssl_ctx = (SSL_CTX*) lua_touserdata(L, 1); @@ -514,7 +519,7 @@ ssl_free(lua_State *L){ return 0; } -int +static int tcp_new(lua_State *L){ core_io *io = (core_io *) lua_newuserdata(L, sizeof(core_io)); @@ -528,7 +533,7 @@ tcp_new(lua_State *L){ } -int +static int tcp_stop(lua_State *L){ core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); @@ -540,7 +545,7 @@ tcp_stop(lua_State *L){ } -int +static int tcp_close(lua_State *L){ int fd = lua_tointeger(L, 1); diff --git a/luaclib/src/ludp.c b/luaclib/src/ludp.c index aebd7376..106c33ee 100644 --- a/luaclib/src/ludp.c +++ b/luaclib/src/ludp.c @@ -30,7 +30,7 @@ void SETSOCKETOPT(int sockfd) { } -int +static int udp_socket_new(const char *ipaddr, int port){ errno = 0; /* 建立 UDP Socket */ @@ -50,6 +50,7 @@ udp_socket_new(const char *ipaddr, int port){ close(sockfd); return -1; } + int ret = connect(sockfd, (struct sockaddr*)&SA, sizeof(SA)); if (ret == -1) { LOG("ERROR", strerror(errno)); @@ -80,7 +81,7 @@ UDP_IO_CB(CORE_P_ core_io *io, int revents){ } } -int +static int udp_send(lua_State *L){ int fd = lua_tointeger(L, 1); @@ -99,7 +100,7 @@ udp_send(lua_State *L){ } -int +static int udp_recv(lua_State *L){ int fd = lua_tointeger(L, 1); @@ -119,7 +120,7 @@ udp_recv(lua_State *L){ } -int +static int udp_connect(lua_State *L){ const char *ip = lua_tostring(L, 1); @@ -138,7 +139,7 @@ udp_connect(lua_State *L){ } -int +static int udp_start(lua_State *L){ core_io *io = (core_io *) luaL_testudata(L, 1, "__UDP__"); @@ -161,7 +162,7 @@ udp_start(lua_State *L){ } -int +static int udp_stop(lua_State *L){ core_io *io = (core_io *) luaL_testudata(L, 1, "__UDP__"); @@ -173,7 +174,7 @@ udp_stop(lua_State *L){ } -int +static int udp_close(lua_State *L){ int fd = lua_tointeger(L, 1); @@ -186,7 +187,7 @@ udp_close(lua_State *L){ -int +static int udp_new(lua_State *L){ core_io *io = (core_io *) lua_newuserdata(L, sizeof(core_io)); From 3c2ef287fc01e5569309cbba2a6d3b4d097de6e1 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 6 Jul 2019 07:02:18 +0800 Subject: [PATCH 214/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9socket=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E8=AE=BE=E7=BD=AE,=20=E5=85=B3=E9=97=ADIPv6=20Only?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 47 ++++++++++++++++++++++++++-------------------- luaclib/src/ludp.c | 13 ++++++++++++- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 9a7ef86a..5a8046de 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -16,14 +16,10 @@ void SETSOCKETOPT(int sockfd, int mode){ int ret = 0; /* 设置非阻塞 */ - ret = non_blocking(sockfd); - if (ret) { - LOG("ERROR", "non_blocking 设置失败."); - return exit(-1); - } + non_blocking(sockfd); +/* 地址重用 */ #ifdef SO_REUSEADDR - /* 地址重用 */ ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &Enable, sizeof(Enable)); if (ret) { LOG("ERROR", "设置 SO_REUSEADDR 失败."); @@ -31,9 +27,9 @@ void SETSOCKETOPT(int sockfd, int mode){ } #endif +/* 端口重用 */ #ifdef SO_REUSEPORT if (mode == SERVER) { - /* 端口重用 */ ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &Enable, sizeof(Enable)); if (ret) { LOG("ERROR", "设置 SO_REUSEPORT 失败."); @@ -42,23 +38,27 @@ void SETSOCKETOPT(int sockfd, int mode){ } #endif - /* 关闭小包延迟合并算法 */ +/* 关闭小包延迟合并算法 */ +#ifdef TCP_NODELAY ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &Enable, sizeof(Enable)); if (ret){ LOG("ERROR", "TCP_NODELAY 设置失败."); return exit(-1); } +#endif - /* 开启 TCP keepalive */ +/* 开启 TCP keepalive */ +#ifdef SO_KEEPALIVE ret = setsockopt(sockfd, IPPROTO_TCP, SO_KEEPALIVE, &Enable , sizeof(Enable)); if (ret){ LOG("ERROR", "SO_KEEPALIVE 设置失败."); return exit(-1); } +#endif +/* 开启延迟Accept, 没数据来之前不回调accept */ #ifdef TCP_DEFER_ACCEPT if (mode == SERVER) { - /* 开启延迟Accept, 没数据来之前不回调accept */ ret = setsockopt(sockfd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &Enable, sizeof(Enable)); if (ret){ LOG("ERROR", "TCP_DEFER_ACCEPT 设置失败."); @@ -67,8 +67,8 @@ void SETSOCKETOPT(int sockfd, int mode){ } #endif +/* 设置 TCP keepalive 空闲时间 */ #ifdef TCP_KEEPIDLE - /* 设置 TCP keepalive 空闲时间 */ int keepidle = 30; ret = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle , sizeof(keepidle)); if (ret){ @@ -77,8 +77,8 @@ void SETSOCKETOPT(int sockfd, int mode){ } #endif +/* 设置 TCP keepalive 探测总次数 */ #ifdef TCP_KEEPCNT - /* 设置 TCP keepalive 探测总次数 */ int keepcount = 3; ret = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, &keepcount , sizeof(keepcount)); if (ret){ @@ -87,8 +87,8 @@ void SETSOCKETOPT(int sockfd, int mode){ } #endif +/* 设置 TCP keepalive 每次探测间隔时间 */ #ifdef TCP_KEEPINTVL - /* 设置 TCP keepalive 每次探测间隔时间 */ int keepinterval = 5; ret = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, &keepinterval , sizeof(keepinterval)); if (ret){ @@ -97,6 +97,16 @@ void SETSOCKETOPT(int sockfd, int mode){ } #endif +/* 开启IPV6与ipv4双栈 */ +#ifdef IPV6_V6ONLY + int No = 0; + ret = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&No, sizeof(No)); + if (ret){ + LOG("ERROR", "IPV6_V6ONLY 关闭失败."); + return exit(-1); + } +#endif + } /* server fd */ @@ -163,8 +173,6 @@ create_client_fd(const char *ipaddr, int port){ static void TCP_IO_CB(CORE_P_ core_io *io, int revents) { - int status = 0; - if (revents & EV_ERROR) { LOG("ERROR", "Recevied a core_io object internal error from libev."); return ; @@ -172,7 +180,7 @@ TCP_IO_CB(CORE_P_ core_io *io, int revents) { lua_State *co = (lua_State *)core_get_watcher_userdata(io); if (lua_status(co) == LUA_YIELD || lua_status(co) == LUA_OK){ - status = lua_resume(co, NULL, 0); + int status = lua_resume(co, NULL, 0); if (status != LUA_YIELD && status != LUA_OK){ LOG("ERROR", lua_tostring(co, -1)); core_io_stop(CORE_LOOP_ io); @@ -191,11 +199,10 @@ IO_CONNECT(CORE_P_ core_io *io, int revents){ if (revents & EV_WRITE){ lua_State *co = (lua_State *)core_get_watcher_userdata(io); if (lua_status(co) == LUA_YIELD || lua_status(co) == LUA_OK){ - socklen_t len; - int status = 0, CONNECTED = 0, err = 0; - if(getsockopt(io->fd, SOL_SOCKET, SO_ERROR, &err, &len) == 0 && err == 0) CONNECTED = 1; + int CONNECTED = 0, err = 0, len = sizeof(socklen_t); + if(getsockopt(io->fd, SOL_SOCKET, SO_ERROR, &err, (socklen_t*)&len) == 0 && err == 0) CONNECTED = 1; lua_pushboolean(co, CONNECTED); - status = lua_resume(co, NULL, 1); + int status = lua_resume(co, NULL, 1); if (status != LUA_YIELD && status != LUA_OK){ LOG("ERROR", lua_tostring(co, -1)); core_io_stop(CORE_LOOP_ io); diff --git a/luaclib/src/ludp.c b/luaclib/src/ludp.c index 106c33ee..5fab71aa 100644 --- a/luaclib/src/ludp.c +++ b/luaclib/src/ludp.c @@ -11,8 +11,8 @@ void SETSOCKETOPT(int sockfd) { /* 设置非阻塞 */ non_blocking(sockfd); +/* 地址重用 */ #ifdef SO_REUSEADDR - /* 端口重用 */ ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &Enable, sizeof(Enable)); if (ret) { LOG("ERROR", "设置 SO_REUSEADDR 失败."); @@ -20,6 +20,7 @@ void SETSOCKETOPT(int sockfd) { } #endif +/* 端口重用 */ #ifdef SO_REUSEPORT ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &Enable, sizeof(Enable)); if (ret) { @@ -28,6 +29,16 @@ void SETSOCKETOPT(int sockfd) { } #endif +/* 开启IPV6与ipv4双栈 */ +#ifdef IPV6_V6ONLY + int No = 0; + ret = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&No, sizeof(No)); + if (ret){ + LOG("ERROR", "IPV6_V6ONLY 关闭失败."); + return exit(-1); + } +#endif + } static int From c65edef92fdddf61f736dce3f3a56ef737547d66 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 6 Jul 2019 07:58:25 +0800 Subject: [PATCH 215/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltask.c | 15 ++++++--------- luaclib/src/ltcp.c | 7 ++++--- luaclib/src/ltimer.c | 27 ++++++++++++--------------- luaclib/src/ludp.c | 2 +- 4 files changed, 23 insertions(+), 28 deletions(-) diff --git a/luaclib/src/ltask.c b/luaclib/src/ltask.c index 172fde9f..a5e84bce 100644 --- a/luaclib/src/ltask.c +++ b/luaclib/src/ltask.c @@ -14,7 +14,7 @@ TASK_CB(CORE_P_ core_task *task, int revents){ } } -int +static int task_new(lua_State *L){ core_task *task = lua_newuserdata(L, sizeof(core_task)); if (!task) return 0; @@ -26,7 +26,7 @@ task_new(lua_State *L){ return 1; } -int +static int task_start(lua_State *L){ core_task *task = (core_task *) luaL_testudata(L, 1, "__Task__"); if (!task) return luaL_error(L, "attemp to pass a invaild core_task value."); @@ -44,7 +44,7 @@ task_start(lua_State *L){ return 1; } -int +static int task_stop(lua_State *L){ core_task *task = (core_task *) luaL_testudata(L, 1, "__Task__"); if (!task) return luaL_error(L, "attemp to pass a invaild core_task value."); @@ -57,17 +57,14 @@ task_stop(lua_State *L){ LUAMOD_API int luaopen_task(lua_State *L){ - luaL_checkversion(L); - luaL_newmetatable(L, "__Task__"); lua_pushstring (L, "__index"); lua_pushvalue(L, -2); lua_rawset(L, -3); - lua_pushliteral(L, "__mode"); - lua_pushliteral(L, "kv"); - lua_rawset(L, -3); - + lua_pushliteral(L, "__mode"); + lua_pushliteral(L, "kv"); + lua_rawset(L, -3); luaL_Reg task_libs[] = { {"new", task_new}, {"start", task_start}, diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 5a8046de..0cf81348 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -155,7 +155,7 @@ create_client_fd(const char *ipaddr, int port){ SA.sin6_family = AF_INET6; SA.sin6_port = htons(port); int error = inet_pton(AF_INET6, ipaddr, &SA.sin6_addr); - if (0 >= error) { + if (1 != error) { LOG("ERROR", strerror(errno)); close(sockfd); return -1; @@ -220,9 +220,10 @@ IO_ACCEPT(CORE_P_ core_io *io, int revents){ errno = 0; struct sockaddr_in6 SA; socklen_t slen = sizeof(SA); + memset(&SA, 0x0, slen); int client = accept(io->fd, (struct sockaddr*)&SA, &slen); if (0 >= client) { - if (errno != EAGAIN) + if (errno != EWOULDBLOCK) LOG("INFO", strerror(errno)); return ; } @@ -329,7 +330,7 @@ tcp_write(lua_State *L){ if (wsize < 0){ if (errno == EINTR) continue; - if (errno == EAGAIN){ lua_pushinteger(L, 0); return 1;} + if (errno == EWOULDBLOCK){ lua_pushinteger(L, 0); return 1;} } } while (0); diff --git a/luaclib/src/ltimer.c b/luaclib/src/ltimer.c index e6b500bd..6d2018fe 100644 --- a/luaclib/src/ltimer.c +++ b/luaclib/src/ltimer.c @@ -22,7 +22,7 @@ TIMEOUT_CB(CORE_P_ core_timer *timer, int revents){ } } -int +static int timer_stop(lua_State *L){ core_timer *timer = (core_timer *) luaL_testudata(L, 1, "__TIMER__"); @@ -33,7 +33,7 @@ timer_stop(lua_State *L){ return 0; } -int +static int timer_start(lua_State *L){ core_timer *timer = (core_timer *) luaL_testudata(L, 1, "__TIMER__"); @@ -53,7 +53,7 @@ timer_start(lua_State *L){ } -int +static int timer_new(lua_State *L){ core_timer *timer = (core_timer *) lua_newuserdata(L, sizeof(core_timer)); @@ -69,24 +69,21 @@ timer_new(lua_State *L){ LUAMOD_API int luaopen_timer(lua_State *L){ - luaL_checkversion(L); - - luaL_newmetatable(L, "__TIMER__"); - lua_pushstring (L, "__index"); - lua_pushvalue(L, -2); - lua_rawset(L, -3); - lua_pushliteral(L, "__mode"); - lua_pushliteral(L, "kv"); - lua_rawset(L, -3); - + luaL_newmetatable(L, "__TIMER__"); + lua_pushstring (L, "__index"); + lua_pushvalue(L, -2); + lua_rawset(L, -3); + lua_pushliteral(L, "__mode"); + lua_pushliteral(L, "kv"); + lua_rawset(L, -3); luaL_Reg timer_libs[] = { {"new", timer_new}, {"stop", timer_stop}, {"start", timer_start}, {NULL, NULL}, }; - luaL_setfuncs(L, timer_libs, 0); + luaL_setfuncs(L, timer_libs, 0); luaL_newlib(L, timer_libs); - return 1; + return 1; } diff --git a/luaclib/src/ludp.c b/luaclib/src/ludp.c index 5fab71aa..fb818a3e 100644 --- a/luaclib/src/ludp.c +++ b/luaclib/src/ludp.c @@ -56,7 +56,7 @@ udp_socket_new(const char *ipaddr, int port){ SA.sin6_family = AF_INET6; SA.sin6_port = htons(port); int error = inet_pton(AF_INET6, ipaddr, &SA.sin6_addr); - if (0 >= error) { + if (1 != error) { LOG("ERROR", strerror(errno)); close(sockfd); return -1; From 7b8af196cadb1ca5ecccb8a312320658e8f842e0 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 6 Jul 2019 07:58:42 +0800 Subject: [PATCH 216/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/core.c b/src/core.c index 1adc614f..2b2e9c6f 100644 --- a/src/core.c +++ b/src/core.c @@ -59,30 +59,25 @@ ERROR_CB(const char *msg){ static void * EV_ALLOC(void *ptr, long nsize){ // 为libev内存hook注入日志; - if (ptr && 0 > nsize){ - LOG("ERROR", "attemp to pass a negative number to malloc or free") - return NULL; - } if (nsize == 0) return xfree(ptr), NULL; for (;;) { void *newptr = xrealloc(ptr, nsize); if (newptr) return newptr; - LOG("WARN", "Allocate failed, Sleep sometime.."); - sleep(1); + LOG("WARN", "Allocate memory failed, Sleep a Second"); + ev_sleep(1); } } static void * L_ALLOC(void *ud, void *ptr, size_t osize, size_t nsize){ // 为lua内存hook注入日志; - /* 用户自定义数据 */ - (void)ud; (void)osize; + (void)ud; (void)osize; /* 用户自定义数据 */ if (nsize == 0) return xfree(ptr), NULL; for (;;) { void *newptr = xrealloc(ptr, nsize); if (newptr) return newptr; - LOG("WARN", "Allocate failed, Sleep sometime.."); - sleep(1); + LOG("WARN", "Allocate memory failed, Sleep a Second."); + ev_sleep(1); } } @@ -157,16 +152,19 @@ signal_init(){ void init_main(){ - int status; L = lua_newstate(L_ALLOC, NULL); - if (!L) return ; + if (!L) { + LOG("ERROR", "Create Lua State failed."); + return ; + } init_lua_libs(L); + int status = 0; status = luaL_loadfile(L, "script/main.lua"); // 停止GC - lua_gc(L, LUA_GCSTOP, 0); + status = lua_gc(L, LUA_GCSTOP, 0); // 设置 GC间歇率 = 每次开启一次新的GC所需的等待时间与条件; 默认为:200 // lua_gc(L, LUA_GCSETPAUSE, 200); From fb653960b967945d403c43381013b757c06277f2 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 6 Jul 2019 08:14:31 +0800 Subject: [PATCH 217/956] =?UTF-8?q?=E8=B0=83=E6=95=B4now=E7=9A=84=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E6=97=B6=E9=97=B4=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_sys.c | 8 ++++---- src/core_sys.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core_sys.c b/src/core_sys.c index f8308163..030cb535 100644 --- a/src/core_sys.c +++ b/src/core_sys.c @@ -1,10 +1,10 @@ #include "core_sys.h" double -now(){ - struct timeval now; - gettimeofday(&now, NULL); - return (double)((double)now.tv_sec + (double)now.tv_usec / 1000000); +now(void){ + struct timespec now; + clock_gettime(CLOCK_REALTIME, &now); + return (double)((double)now.tv_sec + (double)now.tv_nsec / 1000000000); } int /* 此方法可用于检查是否为有效ipv4地址*/ diff --git a/src/core_sys.h b/src/core_sys.h index 49096435..0e1f6d4b 100644 --- a/src/core_sys.h +++ b/src/core_sys.h @@ -44,7 +44,7 @@ content);} /* 微秒级double时间戳 */ -double now(); +double now(void); /* 检查是否为有效ipv4地址 */ int ipv4(const char *IP); From 9da976ee560494518ed47f38f33e8f54aff04248 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 12 Jul 2019 23:46:17 +0800 Subject: [PATCH 218/956] =?UTF-8?q?=E7=B2=BE=E7=AE=80=E4=BB=A3=E7=A0=81,?= =?UTF-8?q?=20=E6=9B=B4=E6=96=B0now=E7=9A=84=E5=AE=9E=E7=8E=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lsys.c | 2 +- src/core_sys.c | 4 ++-- src/core_sys.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/luaclib/src/lsys.c b/luaclib/src/lsys.c index 1c2853ea..f468a67b 100644 --- a/luaclib/src/lsys.c +++ b/luaclib/src/lsys.c @@ -6,7 +6,7 @@ #define is_normal_char(ch) ({((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || ((ch) >= '0' && (ch) <= '9') ? 1 : 0;}) -// 提供一个精确到毫秒的时间戳 +// 提供一个精确到微秒的时间戳 static int lnow(lua_State *L){ lua_pushnumber(L, now()); diff --git a/src/core_sys.c b/src/core_sys.c index 030cb535..dfb6f2a7 100644 --- a/src/core_sys.c +++ b/src/core_sys.c @@ -1,10 +1,10 @@ #include "core_sys.h" -double +double /* 此方法提供一个精确到微秒级的时间戳 */ now(void){ struct timespec now; clock_gettime(CLOCK_REALTIME, &now); - return (double)((double)now.tv_sec + (double)now.tv_nsec / 1000000000); + return now.tv_sec + now.tv_nsec * 1e-9; } int /* 此方法可用于检查是否为有效ipv4地址*/ diff --git a/src/core_sys.h b/src/core_sys.h index 0e1f6d4b..c6cbfd0a 100644 --- a/src/core_sys.h +++ b/src/core_sys.h @@ -43,7 +43,7 @@ __FILE__, __FUNCTION__, __LINE__, \ content);} -/* 微秒级double时间戳 */ +/* 微秒级时间戳函数 */ double now(void); /* 检查是否为有效ipv4地址 */ From 4e17e1d42a2f05dfbeaba9de15c0826ad91ce007 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 13 Jul 2019 04:37:37 +0800 Subject: [PATCH 219/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9Makefile=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lhttpparser/Makefile | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/luaclib/src/lhttpparser/Makefile b/luaclib/src/lhttpparser/Makefile index 89c4ca6c..1002035d 100644 --- a/luaclib/src/lhttpparser/Makefile +++ b/luaclib/src/lhttpparser/Makefile @@ -9,14 +9,13 @@ default : CC = cc -# 如果确认是intel的CPU并且有SSE4可以自行开启这个编译参数, 经过测试http解析速度至少快2倍 -# 这里为了兼容AMD CPU不作为默认开启选项 -# CFLAGS = -O3 -w -shared -fPIC -msse4 +# 开启请先确认是否支持指令集, 经过测试http解析速度至少快2倍 +# CFLAGS = -O3 -Wall -shared -fPIC -msse4 INCLUDE = -I/usr/local/include LIB = -L/usr/local/lib -L../ -L../../../ -CFLAGS = -O3 -w -shared -fPIC +CFLAGS = -O3 -Wall -shared -fPIC DLL = -lcore From 4c19dcdef32812fc29569cc013efc60ac682997f Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 14 Jul 2019 08:00:31 +0800 Subject: [PATCH 220/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0main.lua=E6=96=B0?= =?UTF-8?q?=E5=85=A5=E5=8F=A3=E6=96=87=E4=BB=B6=E5=8A=A0=E8=BD=BD=E5=88=A4?= =?UTF-8?q?=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/core.c b/src/core.c index 2b2e9c6f..dcafaaef 100644 --- a/src/core.c +++ b/src/core.c @@ -108,8 +108,6 @@ init_lua_libs(lua_State *L){ lua_settop(L, 0); } -static lua_State *L; - /* 注册需要忽略的信号 */ core_signal sighup; core_signal sigpipe; @@ -149,6 +147,8 @@ signal_init(){ } +static lua_State *L; + void init_main(){ @@ -160,18 +160,17 @@ init_main(){ init_lua_libs(L); - int status = 0; - status = luaL_loadfile(L, "script/main.lua"); - // 停止GC - status = lua_gc(L, LUA_GCSTOP, 0); - + lua_gc(L, LUA_GCSTOP, 0); // 设置 GC间歇率 = 每次开启一次新的GC所需的等待时间与条件; 默认为:200 // lua_gc(L, LUA_GCSETPAUSE, 200); // 设置 GC步进率倍率 = 控制垃圾收集器相对于内存分配速度的倍数; 默认为:200 // lua_gc(L, LUA_GCSETSTEPMUL, 200); + int status = 0; + status = luaL_loadfile(L, "script/main.lua"); + if(status != LUA_OK) { switch(status){ case LUA_ERRFILE : From 53fca92650ceec2ee958b7a7ff2af161e4286709 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 14 Jul 2019 08:09:43 +0800 Subject: [PATCH 221/956] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=97=A0=E7=94=A8?= =?UTF-8?q?=E7=9A=84=E5=86=97=E4=BD=99=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 48 ++++++++++++++++++------------------------------ 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/src/core.c b/src/core.c index dcafaaef..3fca24ed 100644 --- a/src/core.c +++ b/src/core.c @@ -59,25 +59,30 @@ ERROR_CB(const char *msg){ static void * EV_ALLOC(void *ptr, long nsize){ // 为libev内存hook注入日志; + if (ptr && 0 > nsize){ + LOG("ERROR", "attemp to pass a negative number to malloc or free") + return NULL; + } if (nsize == 0) return xfree(ptr), NULL; for (;;) { void *newptr = xrealloc(ptr, nsize); if (newptr) return newptr; - LOG("WARN", "Allocate memory failed, Sleep a Second"); - ev_sleep(1); + LOG("WARN", "Allocate failed, Sleep sometime.."); + sleep(1); } } static void * L_ALLOC(void *ud, void *ptr, size_t osize, size_t nsize){ // 为lua内存hook注入日志; - (void)ud; (void)osize; /* 用户自定义数据 */ + /* 用户自定义数据 */ + (void)ud; (void)osize; if (nsize == 0) return xfree(ptr), NULL; for (;;) { void *newptr = xrealloc(ptr, nsize); if (newptr) return newptr; - LOG("WARN", "Allocate memory failed, Sleep a Second."); - ev_sleep(1); + LOG("WARN", "Allocate failed, Sleep sometime.."); + sleep(1); } } @@ -147,46 +152,29 @@ signal_init(){ } -static lua_State *L; - void init_main(){ - L = lua_newstate(L_ALLOC, NULL); - if (!L) { - LOG("ERROR", "Create Lua State failed."); - return ; - } + int status = 0; + + lua_State *L = lua_newstate(L_ALLOC, NULL); + if (!L) return ; init_lua_libs(L); // 停止GC lua_gc(L, LUA_GCSTOP, 0); + // 设置 GC间歇率 = 每次开启一次新的GC所需的等待时间与条件; 默认为:200 // lua_gc(L, LUA_GCSETPAUSE, 200); // 设置 GC步进率倍率 = 控制垃圾收集器相对于内存分配速度的倍数; 默认为:200 // lua_gc(L, LUA_GCSETSTEPMUL, 200); - int status = 0; status = luaL_loadfile(L, "script/main.lua"); - - if(status != LUA_OK) { - switch(status){ - case LUA_ERRFILE : - LOG("ERROR", "Can't find file or load file Error."); - exit(-1); - case LUA_ERRSYNTAX: - LOG("ERROR", lua_tostring(L, -1)); - exit(-1); - case LUA_ERRMEM: - LOG("ERROR", "Memory Allocated faild."); - exit(-1); - case LUA_ERRGCMM: - LOG("ERROR", "An Error from lua GC Machine."); - exit(-1); - } - return ; + if (status > 1){ + LOG("ERROR", lua_tostring(L, -1)); + return lua_close(L), exit(-1); } status = lua_resume(L, NULL, 0); From 828a5412d2bc68a616e1c6f3dd241ed1299c817e Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 14 Jul 2019 11:11:48 +0800 Subject: [PATCH 222/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0core.c?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core.c b/src/core.c index 3fca24ed..451f20b1 100644 --- a/src/core.c +++ b/src/core.c @@ -107,7 +107,7 @@ init_lua_libs(lua_State *L){ lua_pushliteral(L, "lualib/?.lua;lualib/?/init.lua;./?.lua;./?/init.lua;script/?.lua;script/?/init.lua;"); lua_setfield(L, 1, "path"); - lua_pushliteral(L, "luaclib/?.so;luaclib/?/init.so;./?.so;./?/init.so;"); + lua_pushliteral(L, "luaclib/?.so;luaclib/lib?.dylib;luaclib/?.dll;./?.so;./lib?.dylib;./?.dll"); lua_setfield(L, 1, "cpath"); lua_settop(L, 0); From 4bae554a17d4991b2f06124adea46d40831fb7e7 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 14 Jul 2019 11:56:22 +0800 Subject: [PATCH 223/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0makefile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core.c b/src/core.c index 451f20b1..31f7a7b7 100644 --- a/src/core.c +++ b/src/core.c @@ -104,10 +104,12 @@ init_lua_libs(lua_State *L){ /* 注入lua搜索域 */ lua_getglobal(L, "package"); + /* 注入lualib搜索路径 */ lua_pushliteral(L, "lualib/?.lua;lualib/?/init.lua;./?.lua;./?/init.lua;script/?.lua;script/?/init.lua;"); lua_setfield(L, 1, "path"); - lua_pushliteral(L, "luaclib/?.so;luaclib/lib?.dylib;luaclib/?.dll;./?.so;./lib?.dylib;./?.dll"); + /* 注入luaclib搜索路径 */ + lua_pushliteral(L, "luaclib/?.so;luaclib/lib?.dylib;./?.so;./lib?.dylib;"); lua_setfield(L, 1, "cpath"); lua_settop(L, 0); From 9cd1ce9a2c9ed401fd4707fdfa2137136a85581e Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 14 Jul 2019 13:55:13 +0800 Subject: [PATCH 224/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=BA=93=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=9F=A5=E6=89=BE=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core.c b/src/core.c index 31f7a7b7..11973772 100644 --- a/src/core.c +++ b/src/core.c @@ -109,7 +109,7 @@ init_lua_libs(lua_State *L){ lua_setfield(L, 1, "path"); /* 注入luaclib搜索路径 */ - lua_pushliteral(L, "luaclib/?.so;luaclib/lib?.dylib;./?.so;./lib?.dylib;"); + lua_pushliteral(L, "luaclib/?.so;./?.so;"); lua_setfield(L, 1, "cpath"); lua_settop(L, 0); From e75993551ffa55871eff5334876330f2ca81e45d Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 14 Jul 2019 15:56:10 +0800 Subject: [PATCH 225/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dhttpc=20timeout?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lualib/httpc/init.lua b/lualib/httpc/init.lua index db96c9f3..87314e5e 100644 --- a/lualib/httpc/init.lua +++ b/lualib/httpc/init.lua @@ -135,7 +135,7 @@ local function json(domain, headers, json, timeout) return code, msg end -local function file(domain, headers, files, times) +local function file(domain, headers, files, timeout) local opt, err = splite_protocol(domain) if not opt then @@ -148,7 +148,7 @@ local function file(domain, headers, files, times) local REQ = build_file_req(opt) - local sock = sock_new():timeout(TIMEOUT or __TIMEOUT__) + local sock = sock_new():timeout(timeout or __TIMEOUT__) local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) if not ok then sock:close() From aa2002adeb4778a71053f04d3b141ced90855804 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 17 Jul 2019 02:19:13 +0800 Subject: [PATCH 226/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0Makefile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcjson/Makefile | 4 ++-- luaclib/src/lcrypt/Makefile | 4 ++-- luaclib/src/lhttpparser/Makefile | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/luaclib/src/lcjson/Makefile b/luaclib/src/lcjson/Makefile index 45bbae03..29d2a038 100644 --- a/luaclib/src/lcjson/Makefile +++ b/luaclib/src/lcjson/Makefile @@ -17,11 +17,11 @@ LIB = -L/usr/local/lib -L../ -L../../../ build: $(CC) -o cjson.so lua_cjson.c fpconv.c strbuf.c $(INCLUDE) $(LIB) $(CFLAGS) -lcore - mv cjson.so ../../ + mv *.so ../../ rebuild: $(OBJS) $(CC) -o cjson.so lua_cjson.c fpconv.c strbuf.c $(INCLUDE) $(LIB) $(CFLAGS) -lcore - mv cjson.so ../../ + mv *.so ../../ clean: rm -rf *.o *.so diff --git a/luaclib/src/lcrypt/Makefile b/luaclib/src/lcrypt/Makefile index cdfe5304..e67ced2e 100644 --- a/luaclib/src/lcrypt/Makefile +++ b/luaclib/src/lcrypt/Makefile @@ -18,11 +18,11 @@ LIB = -L/usr/local/lib -L../ -L../../../ build: $(CC) -o lcrypt.so lcrypt.c crc.c md5.c sha1.c sha2.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) - mv lcrypt.so ../../ + mv *.so ../../ rebuild: $(CC) -o lcrypt.so lcrypt.c crc.c md5.c sha1.c sha2.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) - mv lcrypt.so ../../ + mv *.so ../../ clean: rm -rf *.o *.so diff --git a/luaclib/src/lhttpparser/Makefile b/luaclib/src/lhttpparser/Makefile index 1002035d..6483fc51 100644 --- a/luaclib/src/lhttpparser/Makefile +++ b/luaclib/src/lhttpparser/Makefile @@ -21,11 +21,11 @@ DLL = -lcore build: $(CC) -o httpparser.so httpparser.c lhttpparser.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) - mv httpparser.so ../../ + mv *.so ../../ rebuild: $(CC) -o httpparser.so httpparser.c lhttpparser.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) - mv httpparser.so ../../ + mv *.so ../../ clean: rm -rf *.o *.so From e16feadf8e005f62ebd39db5a29d086115f89c83 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 17 Jul 2019 03:24:53 +0800 Subject: [PATCH 227/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0protobuffer=E4=B8=8Em?= =?UTF-8?q?essagepack=E5=BA=93,=20=E5=AE=8C=E5=96=84=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=B8=8E=E7=A4=BA=E4=BE=8B=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/Makefile | 9 +- luaclib/src/lmsgpack/Makefile | 27 + luaclib/src/lmsgpack/lmsgpack.c | 981 ++++++++++++++++++ luaclib/src/lpbc/Makefile | 27 + luaclib/src/lpbc/lpb.c | 1502 ++++++++++++++++++++++++++++ luaclib/src/lpbc/lpb.h | 1641 +++++++++++++++++++++++++++++++ lualib/msgpack.lua | 28 + lualib/protobuf.lua | 50 + script/test_msgpack.lua | 8 + script/test_protobuf.lua | 23 + 10 files changed, 4294 insertions(+), 2 deletions(-) create mode 100644 luaclib/src/lmsgpack/Makefile create mode 100644 luaclib/src/lmsgpack/lmsgpack.c create mode 100644 luaclib/src/lpbc/Makefile create mode 100644 luaclib/src/lpbc/lpb.c create mode 100644 luaclib/src/lpbc/lpb.h create mode 100644 lualib/msgpack.lua create mode 100644 lualib/protobuf.lua create mode 100644 script/test_msgpack.lua create mode 100644 script/test_protobuf.lua diff --git a/luaclib/Makefile b/luaclib/Makefile index f3bb4db3..b1b38b5f 100644 --- a/luaclib/Makefile +++ b/luaclib/Makefile @@ -20,9 +20,11 @@ build : $(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore $(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore ### 以下为内置第三方库编译位置 ### + cd src/lpbc && rm -rf *.o *.so && make build # 增加lpbc库 cd src/lpeg && rm -rf *.o *.so && make build # 增加lpeg库 - cd src/lcrypt && rm -rf *.o *.so && make build # 增加crypt库 + cd src/lcrypt && rm -rf *.o *.so && make build # 增加lcrypt库 cd src/lcjson && rm -rf *.o *.so && make build # 增加cjson库 + cd src/lmsgpack && rm -rf *.o *.so && make build # 增加lmsgpack库 cd src/lhttpparser && rm -rf *.o *.so && make build # 增加PicoHTTPParser库 rebuild : @@ -32,10 +34,13 @@ rebuild : $(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore $(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore ### 以下为内置第三方库编译位置 ### + cd src/lpbc && rm -rf *.o *.so && make build # 增加lpbc库 cd src/lpeg && rm -rf *.o *.so && make build # 增加lpeg库 - cd src/lcrypt && rm -rf *.o *.so && make build # 增加crypt库 + cd src/lcrypt && rm -rf *.o *.so && make build # 增加lcrypt库 cd src/lcjson && rm -rf *.o *.so && make build # 增加cjson库 + cd src/lmsgpack && rm -rf *.o *.so && make build # 增加lmsgpack库 cd src/lhttpparser && rm -rf *.o *.so && make build # 增加PicoHTTPParser库 + clean : rm -rf *.so diff --git a/luaclib/src/lmsgpack/Makefile b/luaclib/src/lmsgpack/Makefile new file mode 100644 index 00000000..85ad0eef --- /dev/null +++ b/luaclib/src/lmsgpack/Makefile @@ -0,0 +1,27 @@ +.PHONY : build rebuild clean + +default : + @echo "=======================================" + @echo "Please use 'make build' command to build it.." + @echo "Please use 'make rebuild' command to build it.." + @echo "Please use 'make clean' command to clean all." + @echo "=======================================" + +CC = cc + +INCLUDE = -I/usr/local/include +LIB = -L/usr/local/lib -L../ -L../../../ + +CFLAGS = -O3 -Wall -shared -fPIC +DLL = -lcore + +build: + $(CC) -o lmsgpack.so lmsgpack.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) + mv *.so ../../ + +rebuild: + $(CC) -o lmsgpack.so lmsgpack.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) + mv *.so ../../ + +clean: + rm -rf *.o *.so diff --git a/luaclib/src/lmsgpack/lmsgpack.c b/luaclib/src/lmsgpack/lmsgpack.c new file mode 100644 index 00000000..77db1c25 --- /dev/null +++ b/luaclib/src/lmsgpack/lmsgpack.c @@ -0,0 +1,981 @@ +#define LUA_LIB + +#include "../../../src/core.h" + +// #include +// #include +// #include +// #include +// #include +// +// #include "lua.h" +// #include "lauxlib.h" + +#define LUACMSGPACK_NAME "cmsgpack" +#define LUACMSGPACK_SAFE_NAME "cmsgpack_safe" +#define LUACMSGPACK_VERSION "lua-cmsgpack 0.4.1" +#define LUACMSGPACK_COPYRIGHT "Copyright (C) 2018, Salvatore Sanfilippo" +#define LUACMSGPACK_DESCRIPTION "MessagePack C implementation for Lua" + +/* Allows a preprocessor directive to override MAX_NESTING */ +#ifndef LUACMSGPACK_MAX_NESTING + #define LUACMSGPACK_MAX_NESTING 16 /* Max tables nesting. */ +#endif + +/* Check if float or double can be an integer without loss of precision */ +#define IS_INT_TYPE_EQUIVALENT(x, T) (!isinf(x) && (T)(x) == (x)) + +#define IS_INT64_EQUIVALENT(x) IS_INT_TYPE_EQUIVALENT(x, int64_t) +#define IS_INT_EQUIVALENT(x) IS_INT_TYPE_EQUIVALENT(x, int) + +/* If size of pointer is equal to a 4 byte integer, we're on 32 bits. */ +#if UINTPTR_MAX == UINT_MAX + #define BITS_32 1 +#else + #define BITS_32 0 +#endif + +#if BITS_32 + #define lua_pushunsigned(L, n) lua_pushnumber(L, n) +#else + #define lua_pushunsigned(L, n) lua_pushinteger(L, n) +#endif + +/* ============================================================================= + * MessagePack implementation and bindings for Lua 5.1/5.2. + * Copyright(C) 2012 Salvatore Sanfilippo + * + * http://github.com/antirez/lua-cmsgpack + * + * For MessagePack specification check the following web site: + * http://wiki.msgpack.org/display/MSGPACK/Format+specification + * + * See Copyright Notice at the end of this file. + * + * CHANGELOG: + * 19-Feb-2012 (ver 0.1.0): Initial release. + * 20-Feb-2012 (ver 0.2.0): Tables encoding improved. + * 20-Feb-2012 (ver 0.2.1): Minor bug fixing. + * 20-Feb-2012 (ver 0.3.0): Module renamed lua-cmsgpack (was lua-msgpack). + * 04-Apr-2014 (ver 0.3.1): Lua 5.2 support and minor bug fix. + * 07-Apr-2014 (ver 0.4.0): Multiple pack/unpack, lua allocator, efficiency. + * ========================================================================== */ + +/* -------------------------- Endian conversion -------------------------------- + * We use it only for floats and doubles, all the other conversions performed + * in an endian independent fashion. So the only thing we need is a function + * that swaps a binary string if arch is little endian (and left it untouched + * otherwise). */ + +/* Reverse memory bytes if arch is little endian. Given the conceptual + * simplicity of the Lua build system we prefer check for endianess at runtime. + * The performance difference should be acceptable. */ +void memrevifle(void *ptr, size_t len) { + unsigned char *p = (unsigned char *)ptr, + *e = (unsigned char *)p+len-1, + aux; + int test = 1; + unsigned char *testp = (unsigned char*) &test; + + if (testp[0] == 0) return; /* Big endian, nothing to do. */ + len /= 2; + while(len--) { + aux = *p; + *p = *e; + *e = aux; + p++; + e--; + } +} + +/* ---------------------------- String buffer ---------------------------------- + * This is a simple implementation of string buffers. The only operation + * supported is creating empty buffers and appending bytes to it. + * The string buffer uses 2x preallocation on every realloc for O(N) append + * behavior. */ + +typedef struct mp_buf { + unsigned char *b; + size_t len, free; +} mp_buf; + +void *mp_realloc(lua_State *L, void *target, size_t osize,size_t nsize) { + void *(*local_realloc) (void *, void *, size_t osize, size_t nsize) = NULL; + void *ud; + + local_realloc = lua_getallocf(L, &ud); + + return local_realloc(ud, target, osize, nsize); +} + +mp_buf *mp_buf_new(lua_State *L) { + mp_buf *buf = NULL; + + /* Old size = 0; new size = sizeof(*buf) */ + buf = (mp_buf*)mp_realloc(L, NULL, 0, sizeof(*buf)); + + buf->b = NULL; + buf->len = buf->free = 0; + return buf; +} + +void mp_buf_append(lua_State *L, mp_buf *buf, const unsigned char *s, size_t len) { + if (buf->free < len) { + size_t newsize = (buf->len+len)*2; + + buf->b = (unsigned char*)mp_realloc(L, buf->b, buf->len + buf->free, newsize); + buf->free = newsize - buf->len; + } + memcpy(buf->b+buf->len,s,len); + buf->len += len; + buf->free -= len; +} + +void mp_buf_free(lua_State *L, mp_buf *buf) { + mp_realloc(L, buf->b, buf->len + buf->free, 0); /* realloc to 0 = free */ + mp_realloc(L, buf, sizeof(*buf), 0); +} + +/* ---------------------------- String cursor ---------------------------------- + * This simple data structure is used for parsing. Basically you create a cursor + * using a string pointer and a length, then it is possible to access the + * current string position with cursor->p, check the remaining length + * in cursor->left, and finally consume more string using + * mp_cur_consume(cursor,len), to advance 'p' and subtract 'left'. + * An additional field cursor->error is set to zero on initialization and can + * be used to report errors. */ + +#define MP_CUR_ERROR_NONE 0 +#define MP_CUR_ERROR_EOF 1 /* Not enough data to complete operation. */ +#define MP_CUR_ERROR_BADFMT 2 /* Bad data format */ + +typedef struct mp_cur { + const unsigned char *p; + size_t left; + int err; +} mp_cur; + +void mp_cur_init(mp_cur *cursor, const unsigned char *s, size_t len) { + cursor->p = s; + cursor->left = len; + cursor->err = MP_CUR_ERROR_NONE; +} + +#define mp_cur_consume(_c,_len) do { _c->p += _len; _c->left -= _len; } while(0) + +/* When there is not enough room we set an error in the cursor and return. This + * is very common across the code so we have a macro to make the code look + * a bit simpler. */ +#define mp_cur_need(_c,_len) do { \ + if (_c->left < _len) { \ + _c->err = MP_CUR_ERROR_EOF; \ + return; \ + } \ +} while(0) + +/* ------------------------- Low level MP encoding -------------------------- */ + +void mp_encode_bytes(lua_State *L, mp_buf *buf, const unsigned char *s, size_t len) { + unsigned char hdr[5]; + int hdrlen; + + if (len < 32) { + hdr[0] = 0xa0 | (len&0xff); /* fix raw */ + hdrlen = 1; + } else if (len <= 0xff) { + hdr[0] = 0xd9; + hdr[1] = len; + hdrlen = 2; + } else if (len <= 0xffff) { + hdr[0] = 0xda; + hdr[1] = (len&0xff00)>>8; + hdr[2] = len&0xff; + hdrlen = 3; + } else { + hdr[0] = 0xdb; + hdr[1] = (len&0xff000000)>>24; + hdr[2] = (len&0xff0000)>>16; + hdr[3] = (len&0xff00)>>8; + hdr[4] = len&0xff; + hdrlen = 5; + } + mp_buf_append(L,buf,hdr,hdrlen); + mp_buf_append(L,buf,s,len); +} + +/* we assume IEEE 754 internal format for single and double precision floats. */ +void mp_encode_double(lua_State *L, mp_buf *buf, double d) { + unsigned char b[9]; + float f = d; + + assert(sizeof(f) == 4 && sizeof(d) == 8); + if (d == (double)f) { + b[0] = 0xca; /* float IEEE 754 */ + memcpy(b+1,&f,4); + memrevifle(b+1,4); + mp_buf_append(L,buf,b,5); + } else if (sizeof(d) == 8) { + b[0] = 0xcb; /* double IEEE 754 */ + memcpy(b+1,&d,8); + memrevifle(b+1,8); + mp_buf_append(L,buf,b,9); + } +} + +void mp_encode_int(lua_State *L, mp_buf *buf, int64_t n) { + unsigned char b[9]; + int enclen; + + if (n >= 0) { + if (n <= 127) { + b[0] = n & 0x7f; /* positive fixnum */ + enclen = 1; + } else if (n <= 0xff) { + b[0] = 0xcc; /* uint 8 */ + b[1] = n & 0xff; + enclen = 2; + } else if (n <= 0xffff) { + b[0] = 0xcd; /* uint 16 */ + b[1] = (n & 0xff00) >> 8; + b[2] = n & 0xff; + enclen = 3; + } else if (n <= 0xffffffffLL) { + b[0] = 0xce; /* uint 32 */ + b[1] = (n & 0xff000000) >> 24; + b[2] = (n & 0xff0000) >> 16; + b[3] = (n & 0xff00) >> 8; + b[4] = n & 0xff; + enclen = 5; + } else { + b[0] = 0xcf; /* uint 64 */ + b[1] = (n & 0xff00000000000000LL) >> 56; + b[2] = (n & 0xff000000000000LL) >> 48; + b[3] = (n & 0xff0000000000LL) >> 40; + b[4] = (n & 0xff00000000LL) >> 32; + b[5] = (n & 0xff000000) >> 24; + b[6] = (n & 0xff0000) >> 16; + b[7] = (n & 0xff00) >> 8; + b[8] = n & 0xff; + enclen = 9; + } + } else { + if (n >= -32) { + b[0] = ((signed char)n); /* negative fixnum */ + enclen = 1; + } else if (n >= -128) { + b[0] = 0xd0; /* int 8 */ + b[1] = n & 0xff; + enclen = 2; + } else if (n >= -32768) { + b[0] = 0xd1; /* int 16 */ + b[1] = (n & 0xff00) >> 8; + b[2] = n & 0xff; + enclen = 3; + } else if (n >= -2147483648LL) { + b[0] = 0xd2; /* int 32 */ + b[1] = (n & 0xff000000) >> 24; + b[2] = (n & 0xff0000) >> 16; + b[3] = (n & 0xff00) >> 8; + b[4] = n & 0xff; + enclen = 5; + } else { + b[0] = 0xd3; /* int 64 */ + b[1] = (n & 0xff00000000000000LL) >> 56; + b[2] = (n & 0xff000000000000LL) >> 48; + b[3] = (n & 0xff0000000000LL) >> 40; + b[4] = (n & 0xff00000000LL) >> 32; + b[5] = (n & 0xff000000) >> 24; + b[6] = (n & 0xff0000) >> 16; + b[7] = (n & 0xff00) >> 8; + b[8] = n & 0xff; + enclen = 9; + } + } + mp_buf_append(L,buf,b,enclen); +} + +void mp_encode_array(lua_State *L, mp_buf *buf, int64_t n) { + unsigned char b[5]; + int enclen; + + if (n <= 15) { + b[0] = 0x90 | (n & 0xf); /* fix array */ + enclen = 1; + } else if (n <= 65535) { + b[0] = 0xdc; /* array 16 */ + b[1] = (n & 0xff00) >> 8; + b[2] = n & 0xff; + enclen = 3; + } else { + b[0] = 0xdd; /* array 32 */ + b[1] = (n & 0xff000000) >> 24; + b[2] = (n & 0xff0000) >> 16; + b[3] = (n & 0xff00) >> 8; + b[4] = n & 0xff; + enclen = 5; + } + mp_buf_append(L,buf,b,enclen); +} + +void mp_encode_map(lua_State *L, mp_buf *buf, int64_t n) { + unsigned char b[5]; + int enclen; + + if (n <= 15) { + b[0] = 0x80 | (n & 0xf); /* fix map */ + enclen = 1; + } else if (n <= 65535) { + b[0] = 0xde; /* map 16 */ + b[1] = (n & 0xff00) >> 8; + b[2] = n & 0xff; + enclen = 3; + } else { + b[0] = 0xdf; /* map 32 */ + b[1] = (n & 0xff000000) >> 24; + b[2] = (n & 0xff0000) >> 16; + b[3] = (n & 0xff00) >> 8; + b[4] = n & 0xff; + enclen = 5; + } + mp_buf_append(L,buf,b,enclen); +} + +/* --------------------------- Lua types encoding --------------------------- */ + +void mp_encode_lua_string(lua_State *L, mp_buf *buf) { + size_t len; + const char *s; + + s = lua_tolstring(L,-1,&len); + mp_encode_bytes(L,buf,(const unsigned char*)s,len); +} + +void mp_encode_lua_bool(lua_State *L, mp_buf *buf) { + unsigned char b = lua_toboolean(L,-1) ? 0xc3 : 0xc2; + mp_buf_append(L,buf,&b,1); +} + +/* Lua 5.3 has a built in 64-bit integer type */ +void mp_encode_lua_integer(lua_State *L, mp_buf *buf) { +#if (LUA_VERSION_NUM < 503) && BITS_32 + lua_Number i = lua_tonumber(L,-1); +#else + lua_Integer i = lua_tointeger(L,-1); +#endif + mp_encode_int(L, buf, (int64_t)i); +} + +/* Lua 5.2 and lower only has 64-bit doubles, so we need to + * detect if the double may be representable as an int + * for Lua < 5.3 */ +void mp_encode_lua_number(lua_State *L, mp_buf *buf) { + lua_Number n = lua_tonumber(L,-1); + + if (IS_INT64_EQUIVALENT(n)) { + mp_encode_lua_integer(L, buf); + } else { + mp_encode_double(L,buf,(double)n); + } +} + +void mp_encode_lua_type(lua_State *L, mp_buf *buf, int level); + +/* Convert a lua table into a message pack list. */ +void mp_encode_lua_table_as_array(lua_State *L, mp_buf *buf, int level) { +#if LUA_VERSION_NUM < 502 + size_t len = lua_objlen(L,-1), j; +#else + size_t len = lua_rawlen(L,-1), j; +#endif + + mp_encode_array(L,buf,len); + luaL_checkstack(L, 1, "in function mp_encode_lua_table_as_array"); + for (j = 1; j <= len; j++) { + lua_pushnumber(L,j); + lua_gettable(L,-2); + mp_encode_lua_type(L,buf,level+1); + } +} + +/* Convert a lua table into a message pack key-value map. */ +void mp_encode_lua_table_as_map(lua_State *L, mp_buf *buf, int level) { + size_t len = 0; + + /* First step: count keys into table. No other way to do it with the + * Lua API, we need to iterate a first time. Note that an alternative + * would be to do a single run, and then hack the buffer to insert the + * map opcodes for message pack. Too hackish for this lib. */ + luaL_checkstack(L, 3, "in function mp_encode_lua_table_as_map"); + lua_pushnil(L); + while(lua_next(L,-2)) { + lua_pop(L,1); /* remove value, keep key for next iteration. */ + len++; + } + + /* Step two: actually encoding of the map. */ + mp_encode_map(L,buf,len); + lua_pushnil(L); + while(lua_next(L,-2)) { + /* Stack: ... key value */ + lua_pushvalue(L,-2); /* Stack: ... key value key */ + mp_encode_lua_type(L,buf,level+1); /* encode key */ + mp_encode_lua_type(L,buf,level+1); /* encode val */ + } +} + +/* Returns true if the Lua table on top of the stack is exclusively composed + * of keys from numerical keys from 1 up to N, with N being the total number + * of elements, without any hole in the middle. */ +int table_is_an_array(lua_State *L) { + int count = 0, max = 0; +#if LUA_VERSION_NUM < 503 + lua_Number n; +#else + lua_Integer n; +#endif + + /* Stack top on function entry */ + int stacktop; + + stacktop = lua_gettop(L); + + lua_pushnil(L); + while(lua_next(L,-2)) { + /* Stack: ... key value */ + lua_pop(L,1); /* Stack: ... key */ + /* The <= 0 check is valid here because we're comparing indexes. */ +#if LUA_VERSION_NUM < 503 + if ((LUA_TNUMBER != lua_type(L,-1)) || (n = lua_tonumber(L, -1)) <= 0 || + !IS_INT_EQUIVALENT(n)) +#else + if (!lua_isinteger(L,-1) || (n = lua_tointeger(L, -1)) <= 0) +#endif + { + lua_settop(L, stacktop); + return 0; + } + max = (n > max ? n : max); + count++; + } + /* We have the total number of elements in "count". Also we have + * the max index encountered in "max". We can't reach this code + * if there are indexes <= 0. If you also note that there can not be + * repeated keys into a table, you have that if max==count you are sure + * that there are all the keys form 1 to count (both included). */ + lua_settop(L, stacktop); + return max == count; +} + +/* If the length operator returns non-zero, that is, there is at least + * an object at key '1', we serialize to message pack list. Otherwise + * we use a map. */ +void mp_encode_lua_table(lua_State *L, mp_buf *buf, int level) { + if (table_is_an_array(L)) + mp_encode_lua_table_as_array(L,buf,level); + else + mp_encode_lua_table_as_map(L,buf,level); +} + +void mp_encode_lua_null(lua_State *L, mp_buf *buf) { + unsigned char b[1]; + + b[0] = 0xc0; + mp_buf_append(L,buf,b,1); +} + +void mp_encode_lua_type(lua_State *L, mp_buf *buf, int level) { + int t = lua_type(L,-1); + + /* Limit the encoding of nested tables to a specified maximum depth, so that + * we survive when called against circular references in tables. */ + if (t == LUA_TTABLE && level == LUACMSGPACK_MAX_NESTING) t = LUA_TNIL; + switch(t) { + case LUA_TSTRING: mp_encode_lua_string(L,buf); break; + case LUA_TBOOLEAN: mp_encode_lua_bool(L,buf); break; + case LUA_TNUMBER: + #if LUA_VERSION_NUM < 503 + mp_encode_lua_number(L,buf); break; + #else + if (lua_isinteger(L, -1)) { + mp_encode_lua_integer(L, buf); + } else { + mp_encode_lua_number(L, buf); + } + break; + #endif + case LUA_TTABLE: mp_encode_lua_table(L,buf,level); break; + default: mp_encode_lua_null(L,buf); break; + } + lua_pop(L,1); +} + +/* + * Packs all arguments as a stream for multiple upacking later. + * Returns error if no arguments provided. + */ +int mp_pack(lua_State *L) { + int nargs = lua_gettop(L); + int i; + mp_buf *buf; + + if (nargs == 0) + return luaL_argerror(L, 0, "MessagePack pack needs input."); + + if (!lua_checkstack(L, nargs)) + return luaL_argerror(L, 0, "Too many arguments for MessagePack pack."); + + buf = mp_buf_new(L); + for(i = 1; i <= nargs; i++) { + /* Copy argument i to top of stack for _encode processing; + * the encode function pops it from the stack when complete. */ + luaL_checkstack(L, 1, "in function mp_check"); + lua_pushvalue(L, i); + + mp_encode_lua_type(L,buf,0); + + lua_pushlstring(L,(char*)buf->b,buf->len); + + /* Reuse the buffer for the next operation by + * setting its free count to the total buffer size + * and the current position to zero. */ + buf->free += buf->len; + buf->len = 0; + } + mp_buf_free(L, buf); + + /* Concatenate all nargs buffers together */ + lua_concat(L, nargs); + return 1; +} + +/* ------------------------------- Decoding --------------------------------- */ + +void mp_decode_to_lua_type(lua_State *L, mp_cur *c); + +void mp_decode_to_lua_array(lua_State *L, mp_cur *c, size_t len) { + assert(len <= UINT_MAX); + int index = 1; + + lua_newtable(L); + luaL_checkstack(L, 1, "in function mp_decode_to_lua_array"); + while(len--) { + lua_pushnumber(L,index++); + mp_decode_to_lua_type(L,c); + if (c->err) return; + lua_settable(L,-3); + } +} + +void mp_decode_to_lua_hash(lua_State *L, mp_cur *c, size_t len) { + assert(len <= UINT_MAX); + lua_newtable(L); + while(len--) { + mp_decode_to_lua_type(L,c); /* key */ + if (c->err) return; + mp_decode_to_lua_type(L,c); /* value */ + if (c->err) return; + lua_settable(L,-3); + } +} + +/* Decode a Message Pack raw object pointed by the string cursor 'c' to + * a Lua type, that is left as the only result on the stack. */ +void mp_decode_to_lua_type(lua_State *L, mp_cur *c) { + mp_cur_need(c,1); + + /* If we return more than 18 elements, we must resize the stack to + * fit all our return values. But, there is no way to + * determine how many objects a msgpack will unpack to up front, so + * we request a +1 larger stack on each iteration (noop if stack is + * big enough, and when stack does require resize it doubles in size) */ + luaL_checkstack(L, 1, + "too many return values at once; " + "use unpack_one or unpack_limit instead."); + + switch(c->p[0]) { + case 0xcc: /* uint 8 */ + mp_cur_need(c,2); + lua_pushunsigned(L,c->p[1]); + mp_cur_consume(c,2); + break; + case 0xd0: /* int 8 */ + mp_cur_need(c,2); + lua_pushinteger(L,(signed char)c->p[1]); + mp_cur_consume(c,2); + break; + case 0xcd: /* uint 16 */ + mp_cur_need(c,3); + lua_pushunsigned(L, + (c->p[1] << 8) | + c->p[2]); + mp_cur_consume(c,3); + break; + case 0xd1: /* int 16 */ + mp_cur_need(c,3); + lua_pushinteger(L,(int16_t) + (c->p[1] << 8) | + c->p[2]); + mp_cur_consume(c,3); + break; + case 0xce: /* uint 32 */ + mp_cur_need(c,5); + lua_pushunsigned(L, + ((uint32_t)c->p[1] << 24) | + ((uint32_t)c->p[2] << 16) | + ((uint32_t)c->p[3] << 8) | + (uint32_t)c->p[4]); + mp_cur_consume(c,5); + break; + case 0xd2: /* int 32 */ + mp_cur_need(c,5); + lua_pushinteger(L, + ((int32_t)c->p[1] << 24) | + ((int32_t)c->p[2] << 16) | + ((int32_t)c->p[3] << 8) | + (int32_t)c->p[4]); + mp_cur_consume(c,5); + break; + case 0xcf: /* uint 64 */ + mp_cur_need(c,9); + lua_pushunsigned(L, + ((uint64_t)c->p[1] << 56) | + ((uint64_t)c->p[2] << 48) | + ((uint64_t)c->p[3] << 40) | + ((uint64_t)c->p[4] << 32) | + ((uint64_t)c->p[5] << 24) | + ((uint64_t)c->p[6] << 16) | + ((uint64_t)c->p[7] << 8) | + (uint64_t)c->p[8]); + mp_cur_consume(c,9); + break; + case 0xd3: /* int 64 */ + mp_cur_need(c,9); +#if LUA_VERSION_NUM < 503 + lua_pushnumber(L, +#else + lua_pushinteger(L, +#endif + ((int64_t)c->p[1] << 56) | + ((int64_t)c->p[2] << 48) | + ((int64_t)c->p[3] << 40) | + ((int64_t)c->p[4] << 32) | + ((int64_t)c->p[5] << 24) | + ((int64_t)c->p[6] << 16) | + ((int64_t)c->p[7] << 8) | + (int64_t)c->p[8]); + mp_cur_consume(c,9); + break; + case 0xc0: /* nil */ + lua_pushnil(L); + mp_cur_consume(c,1); + break; + case 0xc3: /* true */ + lua_pushboolean(L,1); + mp_cur_consume(c,1); + break; + case 0xc2: /* false */ + lua_pushboolean(L,0); + mp_cur_consume(c,1); + break; + case 0xca: /* float */ + mp_cur_need(c,5); + assert(sizeof(float) == 4); + { + float f; + memcpy(&f,c->p+1,4); + memrevifle(&f,4); + lua_pushnumber(L,f); + mp_cur_consume(c,5); + } + break; + case 0xcb: /* double */ + mp_cur_need(c,9); + assert(sizeof(double) == 8); + { + double d; + memcpy(&d,c->p+1,8); + memrevifle(&d,8); + lua_pushnumber(L,d); + mp_cur_consume(c,9); + } + break; + case 0xd9: /* raw 8 */ + mp_cur_need(c,2); + { + size_t l = c->p[1]; + mp_cur_need(c,2+l); + lua_pushlstring(L,(char*)c->p+2,l); + mp_cur_consume(c,2+l); + } + break; + case 0xda: /* raw 16 */ + mp_cur_need(c,3); + { + size_t l = (c->p[1] << 8) | c->p[2]; + mp_cur_need(c,3+l); + lua_pushlstring(L,(char*)c->p+3,l); + mp_cur_consume(c,3+l); + } + break; + case 0xdb: /* raw 32 */ + mp_cur_need(c,5); + { + size_t l = ((size_t)c->p[1] << 24) | + ((size_t)c->p[2] << 16) | + ((size_t)c->p[3] << 8) | + (size_t)c->p[4]; + mp_cur_consume(c,5); + mp_cur_need(c,l); + lua_pushlstring(L,(char*)c->p,l); + mp_cur_consume(c,l); + } + break; + case 0xdc: /* array 16 */ + mp_cur_need(c,3); + { + size_t l = (c->p[1] << 8) | c->p[2]; + mp_cur_consume(c,3); + mp_decode_to_lua_array(L,c,l); + } + break; + case 0xdd: /* array 32 */ + mp_cur_need(c,5); + { + size_t l = ((size_t)c->p[1] << 24) | + ((size_t)c->p[2] << 16) | + ((size_t)c->p[3] << 8) | + (size_t)c->p[4]; + mp_cur_consume(c,5); + mp_decode_to_lua_array(L,c,l); + } + break; + case 0xde: /* map 16 */ + mp_cur_need(c,3); + { + size_t l = (c->p[1] << 8) | c->p[2]; + mp_cur_consume(c,3); + mp_decode_to_lua_hash(L,c,l); + } + break; + case 0xdf: /* map 32 */ + mp_cur_need(c,5); + { + size_t l = ((size_t)c->p[1] << 24) | + ((size_t)c->p[2] << 16) | + ((size_t)c->p[3] << 8) | + (size_t)c->p[4]; + mp_cur_consume(c,5); + mp_decode_to_lua_hash(L,c,l); + } + break; + default: /* types that can't be idenitified by first byte value. */ + if ((c->p[0] & 0x80) == 0) { /* positive fixnum */ + lua_pushunsigned(L,c->p[0]); + mp_cur_consume(c,1); + } else if ((c->p[0] & 0xe0) == 0xe0) { /* negative fixnum */ + lua_pushinteger(L,(signed char)c->p[0]); + mp_cur_consume(c,1); + } else if ((c->p[0] & 0xe0) == 0xa0) { /* fix raw */ + size_t l = c->p[0] & 0x1f; + mp_cur_need(c,1+l); + lua_pushlstring(L,(char*)c->p+1,l); + mp_cur_consume(c,1+l); + } else if ((c->p[0] & 0xf0) == 0x90) { /* fix map */ + size_t l = c->p[0] & 0xf; + mp_cur_consume(c,1); + mp_decode_to_lua_array(L,c,l); + } else if ((c->p[0] & 0xf0) == 0x80) { /* fix map */ + size_t l = c->p[0] & 0xf; + mp_cur_consume(c,1); + mp_decode_to_lua_hash(L,c,l); + } else { + c->err = MP_CUR_ERROR_BADFMT; + } + } +} + +int mp_unpack_full(lua_State *L, int limit, int offset) { + size_t len; + const char *s; + mp_cur c; + int cnt; /* Number of objects unpacked */ + int decode_all = (!limit && !offset); + + s = luaL_checklstring(L,1,&len); /* if no match, exits */ + + if (offset < 0 || limit < 0) /* requesting negative off or lim is invalid */ + return luaL_error(L, + "Invalid request to unpack with offset of %d and limit of %d.", + offset, len); + else if (offset > len) + return luaL_error(L, + "Start offset %d greater than input length %d.", offset, len); + + if (decode_all) limit = INT_MAX; + + mp_cur_init(&c,(const unsigned char *)s+offset,len-offset); + + /* We loop over the decode because this could be a stream + * of multiple top-level values serialized together */ + for(cnt = 0; c.left > 0 && cnt < limit; cnt++) { + mp_decode_to_lua_type(L,&c); + + if (c.err == MP_CUR_ERROR_EOF) { + return luaL_error(L,"Missing bytes in input."); + } else if (c.err == MP_CUR_ERROR_BADFMT) { + return luaL_error(L,"Bad data format in input."); + } + } + + if (!decode_all) { + /* c->left is the remaining size of the input buffer. + * subtract the entire buffer size from the unprocessed size + * to get our next start offset */ + int offset = len - c.left; + + luaL_checkstack(L, 1, "in function mp_unpack_full"); + + /* Return offset -1 when we have have processed the entire buffer. */ + lua_pushinteger(L, c.left == 0 ? -1 : offset); + /* Results are returned with the arg elements still + * in place. Lua takes care of only returning + * elements above the args for us. + * In this case, we have one arg on the stack + * for this function, so we insert our first return + * value at position 2. */ + lua_insert(L, 2); + cnt += 1; /* increase return count by one to make room for offset */ + } + + return cnt; +} + +int mp_unpack(lua_State *L) { + return mp_unpack_full(L, 0, 0); +} + +int mp_unpack_one(lua_State *L) { + int offset = luaL_optinteger(L, 2, 0); + /* Variable pop because offset may not exist */ + lua_pop(L, lua_gettop(L)-1); + return mp_unpack_full(L, 1, offset); +} + +int mp_unpack_limit(lua_State *L) { + int limit = luaL_checkinteger(L, 2); + int offset = luaL_optinteger(L, 3, 0); + /* Variable pop because offset may not exist */ + lua_pop(L, lua_gettop(L)-1); + + return mp_unpack_full(L, limit, offset); +} + +int mp_safe(lua_State *L) { + int argc, err, total_results; + + argc = lua_gettop(L); + + /* This adds our function to the bottom of the stack + * (the "call this function" position) */ + lua_pushvalue(L, lua_upvalueindex(1)); + lua_insert(L, 1); + + err = lua_pcall(L, argc, LUA_MULTRET, 0); + total_results = lua_gettop(L); + + if (!err) { + return total_results; + } else { + lua_pushnil(L); + lua_insert(L,-2); + return 2; + } +} + +/* -------------------------------------------------------------------------- */ +const struct luaL_Reg cmds[] = { + {"pack", mp_pack}, + {"unpack", mp_unpack}, + {"unpack_one", mp_unpack_one}, + {"unpack_limit", mp_unpack_limit}, + {0} +}; + +int luaopen_create(lua_State *L) { + int i; + /* Manually construct our module table instead of + * relying on _register or _newlib */ + lua_newtable(L); + + for (i = 0; i < (sizeof(cmds)/sizeof(*cmds) - 1); i++) { + lua_pushcfunction(L, cmds[i].func); + lua_setfield(L, -2, cmds[i].name); + } + + /* Add metadata */ + lua_pushliteral(L, LUACMSGPACK_NAME); + lua_setfield(L, -2, "_NAME"); + lua_pushliteral(L, LUACMSGPACK_VERSION); + lua_setfield(L, -2, "_VERSION"); + lua_pushliteral(L, LUACMSGPACK_COPYRIGHT); + lua_setfield(L, -2, "_COPYRIGHT"); + lua_pushliteral(L, LUACMSGPACK_DESCRIPTION); + lua_setfield(L, -2, "_DESCRIPTION"); + return 1; +} + +LUALIB_API int luaopen_lmsgpack(lua_State *L) { + luaopen_create(L); + +#if LUA_VERSION_NUM < 502 + /* Register name globally for 5.1 */ + lua_pushvalue(L, -1); + lua_setglobal(L, LUACMSGPACK_NAME); +#endif + + return 1; +} + +LUALIB_API int luaopen_lmsgpack_safe(lua_State *L) { + int i; + + luaopen_lmsgpack(L); + + /* Wrap all functions in the safe handler */ + for (i = 0; i < (sizeof(cmds)/sizeof(*cmds) - 1); i++) { + lua_getfield(L, -1, cmds[i].name); + lua_pushcclosure(L, mp_safe, 1); + lua_setfield(L, -2, cmds[i].name); + } + +#if LUA_VERSION_NUM < 502 + /* Register name globally for 5.1 */ + lua_pushvalue(L, -1); + lua_setglobal(L, LUACMSGPACK_SAFE_NAME); +#endif + + return 1; +} + +/****************************************************************************** +* Copyright (C) 2012 Salvatore Sanfilippo. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +******************************************************************************/ diff --git a/luaclib/src/lpbc/Makefile b/luaclib/src/lpbc/Makefile new file mode 100644 index 00000000..e1e578a5 --- /dev/null +++ b/luaclib/src/lpbc/Makefile @@ -0,0 +1,27 @@ +.PHONY : build rebuild clean + +default : + @echo "=======================================" + @echo "Please use 'make build' command to build it.." + @echo "Please use 'make rebuild' command to build it.." + @echo "Please use 'make clean' command to clean all." + @echo "=======================================" + +CC = cc + +INCLUDE = -I/usr/local/include +LIB = -L/usr/local/lib -L../ -L../../../ + +CFLAGS = -O3 -Wall -shared -fPIC +DLL = -lcore + +build: + $(CC) -o lprotobuf.so lpb.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) + mv lprotobuf.so ../../ + +rebuild: + $(CC) -o lprotobuf.so lpb.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) + mv lprotobuf.so ../../ + +clean: + rm -rf *.o *.so diff --git a/luaclib/src/lpbc/lpb.c b/luaclib/src/lpbc/lpb.c new file mode 100644 index 00000000..42c9fa1e --- /dev/null +++ b/luaclib/src/lpbc/lpb.c @@ -0,0 +1,1502 @@ +#define PB_STATIC_API + +#include "lpb.h" + +#define LUA_LIB + +#define PB_STATE "pb.State" +#define PB_BUFFER "pb.Buffer" +#define PB_SLICE "pb.Slice" + +#define check_buffer(L,idx) ((pb_Buffer*)checkudata(L,idx,PB_BUFFER)) +#define test_buffer(L,idx) ((pb_Buffer*)testudata(L,idx,PB_BUFFER)) +#define check_slice(L,idx) ((pb_SliceExt*)checkudata(L,idx,PB_SLICE)) +#define test_slice(L,idx) ((pb_SliceExt*)testudata(L,idx,PB_SLICE)) +#define return_self(L) { lua_settop(L, 1); return 1; } + +#if LUA_VERSION_NUM < 502 +#include + +# define LUA_OK 0 +# define lua_rawlen lua_objlen +# define luaL_setfuncs(L,l,n) (assert(n==0), luaL_register(L,NULL,l)) +# define luaL_setmetatable(L, name) \ + (luaL_getmetatable((L), (name)), lua_setmetatable(L, -2)) + +static int relindex(int idx, int offset) +{ return idx < 0 && idx > LUA_REGISTRYINDEX ? idx + offset : idx; } + +void lua_rawgetp(lua_State *L, int idx, const void *p) { + lua_pushlightuserdata(L, (void*)p); + lua_rawget(L, relindex(idx, 1)); +} + +void lua_rawsetp(lua_State *L, int idx, const void *p) { + lua_pushlightuserdata(L, (void*)p); + lua_insert(L, -2); + lua_rawset(L, relindex(idx, 1)); +} + +#ifndef luaL_newlib /* not LuaJIT 2.1 */ +#define luaL_newlib(L,l) (lua_newtable(L), luaL_register(L,NULL,l)) + +static lua_Integer lua_tointegerx(lua_State *L, int idx, int *isint) { + lua_Integer i = lua_tointeger(L, idx); + if (isint) *isint = (i != 0 || lua_type(L, idx) == LUA_TNUMBER); + return i; +} + +static lua_Number lua_tonumberx(lua_State *L, int idx, int *isnum) { + lua_Number i = lua_tonumber(L, idx); + if (isnum) *isnum = (i != 0 || lua_type(L, idx) == LUA_TNUMBER); + return i; +} +#endif + +#ifdef LUAI_BITSINT /* not LuaJIT */ +#include +static int luaL_fileresult(lua_State *L, int stat, const char *fname) { + int en = errno; + if (stat) { lua_pushboolean(L, 1); return 1; } + lua_pushnil(L); + lua_pushfstring(L, "%s: %s", fname, strerror(en)); + /*if (fname) lua_pushfstring(L, "%s: %s", fname, strerror(en)); + else lua_pushstring(L, strerror(en));*//* NOT USED */ + lua_pushinteger(L, en); + return 3; +} +#endif /* not LuaJIT */ + +#endif + +#if LUA_VERSION_NUM >= 503 +# define lua53_getfield lua_getfield +# define lua53_rawgeti lua_rawgeti +# define lua53_rawgetp lua_rawgetp +#else +static int lua53_getfield(lua_State *L, int idx, const char *field) +{ lua_getfield(L, idx, field); return lua_type(L, -1); } +static int lua53_rawgeti(lua_State *L, int idx, lua_Integer i) +{ lua_rawgeti(L, idx, i); return lua_type(L, -1); } +static int lua53_rawgetp(lua_State *L, int idx, const void *p) +{ lua_rawgetp(L, idx, p); return lua_type(L, -1); } +#endif + + +typedef struct pb_SliceExt { + pb_Slice base; + const char *head; +} pb_SliceExt; + +static int lpb_offset(pb_SliceExt *s) { return (int)(s->base.p-s->head) + 1; } + +static pb_SliceExt lpb_initext(pb_Slice s) +{ pb_SliceExt ext; ext.base = s, ext.head = s.p; return ext; } + +static void lpb_addlength(lua_State *L, pb_Buffer *b, size_t len) +{ if (pb_addlength(b, len) == 0) luaL_error(L, "encode bytes fail"); } + +static int typeerror(lua_State *L, int idx, const char *type) { + lua_pushfstring(L, "%s expected, got %s", type, luaL_typename(L, idx)); + return luaL_argerror(L, idx, lua_tostring(L, -1)); +} + +static void *testudata(lua_State *L, int idx, const char *type) { + void *p = lua_touserdata(L, idx); + if (p != NULL && lua_getmetatable(L, idx)) { + lua_getfield(L, LUA_REGISTRYINDEX, type); + if (!lua_rawequal(L, -2, -1)) + p = NULL; + lua_pop(L, 2); + return p; + } + return NULL; +} + +static void *checkudata(lua_State *L, int idx, const char *type) { + void *p = testudata(L, idx, type); + if (p == NULL) typeerror(L, idx, type); + return p; +} + +static lua_Integer posrelat(lua_Integer pos, size_t len) { + if (pos >= 0) return pos; + else if (0u - (size_t)pos > len) return 0; + else return (lua_Integer)len + pos + 1; +} + +static lua_Integer rangerelat(lua_State *L, int idx, lua_Integer *i, lua_Integer *j, size_t len) { + *i = posrelat(luaL_optinteger(L, idx, 1), len); + *j = posrelat(luaL_optinteger(L, idx+1, len), len); + if (*i < 1) *i = 1; + if (*j > (lua_Integer)len) *j = len; + return *i <= *j ? *j - *i + 1 : 0; +} + +static int argerror(lua_State *L, int idx, const char *fmt, ...) { + va_list l; + va_start(l, fmt); + lua_pushvfstring(L, fmt, l); + va_end(l); + return luaL_argerror(L, idx, lua_tostring(L, -1)); +} + +static pb_Slice lpb_toslice(lua_State *L, int idx) { + int type = lua_type(L, idx); + pb_Slice ret = { NULL, NULL }; + if (type == LUA_TSTRING) { + size_t len; + const char *s = lua_tolstring(L, idx, &len); + ret = pb_lslice(s, len); + } + else if (type == LUA_TUSERDATA) { + pb_Buffer *buffer; + pb_SliceExt *s; + if ((buffer = test_buffer(L, idx)) != NULL) + ret = pb_result(buffer); + else if ((s = test_slice(L, idx)) != NULL) + ret = s->base; + } + return ret; +} + +static pb_Slice lpb_checkslice(lua_State *L, int idx) { + pb_Slice ret = lpb_toslice(L, idx); + if (ret.p == NULL) typeerror(L, idx, "string/buffer/slice"); + return ret; +} + +static void lpb_readbytes(lua_State *L, pb_SliceExt *s, pb_SliceExt *pv) { + uint64_t len = 0; + if (pb_readvarint64(&s->base, &len) == 0 || len > PB_MAX_SIZET) + luaL_error(L, "invalid bytes length: %d (at offset %d)", + (int)len, lpb_offset(s)); + if (pb_readslice(&s->base, (size_t)len, &pv->base) == 0 && len != 0) + luaL_error(L, "un-finished bytes (len %d at offset %d)", + (int)len, lpb_offset(s)); + pv->head = pv->base.p; +} + +typedef union lpb_Value { + pb_SliceExt s[1]; + uint32_t u32; + uint64_t u64; + lua_Integer lint; + lua_Number lnum; +} lpb_Value; + +static int lpb_addtype(lua_State *L, pb_Buffer *b, int idx, int type) { + int ret = 0, expected = LUA_TNUMBER; + lpb_Value v; + switch (type) { + case PB_Tbool: + pb_addvarint32(b, lua_toboolean(L, idx)); + ret = 1; + break; + case PB_Tdouble: + v.lnum = lua_tonumberx(L, idx, &ret); + if (ret) pb_addfixed64(b, pb_encode_double((double)v.lnum)); + break; + case PB_Tfloat: + v.lnum = lua_tonumberx(L, idx, &ret); + if (ret) pb_addfixed32(b, pb_encode_float((float)v.lnum)); + break; + case PB_Tfixed32: + v.lint = lua_tointegerx(L, idx, &ret); + if (ret) pb_addfixed32(b, (uint32_t)v.lint); + break; + case PB_Tsfixed32: + v.lint = lua_tointegerx(L, idx, &ret); + if (ret) pb_addfixed32(b, (int32_t)v.lint); + break; + case PB_Tint32: case PB_Tuint32: + v.lint = lua_tointegerx(L, idx, &ret); + if (ret) pb_addvarint32(b, (uint32_t)lua_tointeger(L, idx)); + break; + case PB_Tsint32: + v.lint = lua_tointegerx(L, idx, &ret); + if (ret) pb_addvarint32(b, pb_encode_sint32((int32_t)v.lint)); + break; + case PB_Tfixed64: + v.lint = lua_tointegerx(L, idx, &ret); + if (ret) pb_addfixed64(b, (uint64_t)v.lint); + break; + case PB_Tsfixed64: + v.lint = lua_tointegerx(L, idx, &ret); + if (ret) pb_addfixed64(b, (int64_t)v.lint); + break; + case PB_Tint64: case PB_Tuint64: + v.lint = lua_tointegerx(L, idx, &ret); + if (ret) pb_addvarint64(b, (uint64_t)v.lint); + break; + case PB_Tsint64: + v.lint = lua_tointegerx(L, idx, &ret); + if (ret) pb_addvarint64(b, pb_encode_sint64((int64_t)v.lint)); + break; + case PB_Tbytes: case PB_Tstring: + v.s->base = lpb_toslice(L, idx); + if ((ret = v.s->base.p != NULL)) pb_addbytes(b, v.s->base); + expected = LUA_TSTRING; + break; + /* NOT REACHED */ + /* default: + * argerror(L, idx, "unknown type %s", pb_typename(type, "")); */ + } + return ret ? 0 : expected; +} + +static void lpb_readtype(lua_State *L, int type, pb_SliceExt *s) { + lpb_Value v; + switch (type) { + case PB_Tbool: case PB_Tenum: + case PB_Tint32: case PB_Tuint32: case PB_Tsint32: + case PB_Tint64: case PB_Tuint64: case PB_Tsint64: + if (pb_readvarint64(&s->base, &v.u64) == 0) + luaL_error(L, "invalid varint value at offset %d", lpb_offset(s)); + switch (type) { + case PB_Tbool: lua_pushboolean(L, v.u64 != 0); break; + /*case PB_Tenum: lua_pushinteger(L, v.u64); break; [> NOT REACHED <]*/ + case PB_Tint32: lua_pushinteger(L, (int32_t)v.u64); break; + case PB_Tuint32: lua_pushinteger(L, (uint32_t)v.u64); break; + case PB_Tsint32: lua_pushinteger(L, pb_decode_sint32((uint32_t)v.u64)); break; + case PB_Tint64: lua_pushinteger(L, (int64_t)v.u64); break; + case PB_Tuint64: lua_pushinteger(L, (uint64_t)v.u64); break; + case PB_Tsint64: lua_pushinteger(L, pb_decode_sint64(v.u64)); break; + } + break; + case PB_Tfloat: + case PB_Tfixed32: + case PB_Tsfixed32: + if (pb_readfixed32(&s->base, &v.u32) == 0) + luaL_error(L, "invalid fixed32 value at offset %d", lpb_offset(s)); + switch (type) { + case PB_Tfloat: lua_pushnumber(L, pb_decode_float(v.u32)); break; + case PB_Tfixed32: lua_pushinteger(L, v.u32); break; + case PB_Tsfixed32: lua_pushinteger(L, (int32_t)v.u32); break; + } + break; + case PB_Tdouble: + case PB_Tfixed64: + case PB_Tsfixed64: + if (pb_readfixed64(&s->base, &v.u64) == 0) + luaL_error(L, "invalid fixed64 value at offset %d", lpb_offset(s)); + switch (type) { + case PB_Tdouble: lua_pushnumber(L, pb_decode_double(v.u64)); break; + case PB_Tfixed64: lua_pushinteger(L, v.u64); break; + case PB_Tsfixed64: lua_pushinteger(L, (int64_t)v.u64); break; + } + break; + case PB_Tbytes: + case PB_Tstring: + case PB_Tmessage: + lpb_readbytes(L, s, v.s); + lua_pushlstring(L, v.s->base.p, pb_len(v.s->base)); + break; + /* NOT REACHED */ + /*default: + * luaL_error(L, "unknown type %s", pb_typename(type, NULL));*/ + } +} + + +/* io routines */ + +#ifdef _WIN32 +# include +# include +#else +# define setmode(a,b) ((void)0) +#endif + +static int io_read(lua_State *L) { + FILE *fp = (FILE*)lua_touserdata(L, 1); + size_t nr; + luaL_Buffer b; + luaL_buffinit(L, &b); + do { /* read file in chunks of LUAL_BUFFERSIZE bytes */ + char *p = luaL_prepbuffer(&b); + nr = fread(p, sizeof(char), LUAL_BUFFERSIZE, fp); + luaL_addsize(&b, nr); + } while (nr == LUAL_BUFFERSIZE); + luaL_pushresult(&b); /* close buffer */ + return 1; +} + +static int io_write(lua_State *L, FILE *f, int idx) { + int nargs = lua_gettop(L) - idx + 1; + int status = 1; + for (; nargs--; idx++) { + pb_Slice s = lpb_checkslice(L, idx); + size_t l = pb_len(s); + status = status && (fwrite(s.p, sizeof(char), l, f) == l); + } + return status ? 1 : luaL_fileresult(L, 0, NULL); +} + +static int Lio_read(lua_State *L) { + const char *fname = luaL_optstring(L, 1, NULL); + FILE *fp = stdin; + int ret; + if (fname == NULL) + (void)setmode(fileno(stdin), O_BINARY); + else if ((fp = fopen(fname, "rb")) == NULL) + return luaL_fileresult(L, 0, fname); + lua_pushcfunction(L, io_read); + lua_pushlightuserdata(L, fp); + ret = lua_pcall(L, 1, 1, 0); + if (fp != stdin) fclose(fp); + else (void)setmode(fileno(stdin), O_TEXT); + if (ret != LUA_OK) { lua_pushnil(L); lua_insert(L, -2); return 2; } + return 1; +} + +static int Lio_write(lua_State *L) { + int res; + (void)setmode(fileno(stdout), O_BINARY); + res = io_write(L, stdout, 1); + fflush(stdout); + (void)setmode(fileno(stdout), O_TEXT); + return res; +} + +static int Lio_dump(lua_State *L) { + int res; + const char *fname = luaL_checkstring(L, 1); + FILE *fp = fopen(fname, "wb"); + if (fp == NULL) return luaL_fileresult(L, 0, fname); + res = io_write(L, fp, 2); + fclose(fp); + return res; +} + +LUALIB_API int luaopen_pb_io(lua_State *L) { + luaL_Reg libs[] = { +#define ENTRY(name) { #name, Lio_##name } + ENTRY(read), + ENTRY(write), + ENTRY(dump), +#undef ENTRY + { NULL, NULL } + }; + luaL_newlib(L, libs); + return 1; +} + + +/* protobuf integer conversion */ + +static int Lconv_encode_int32(lua_State *L) { + lua_pushinteger(L, pb_expandsig((int32_t)luaL_checkinteger(L, 1))); + return 1; +} + +static int Lconv_encode_uint32(lua_State *L) { + lua_pushinteger(L, (uint32_t)luaL_checkinteger(L, 1)); + return 1; +} + +static int Lconv_encode_sint32(lua_State *L) { + lua_pushinteger(L, pb_encode_sint32((int32_t)luaL_checkinteger(L, 1))); + return 1; +} + +static int Lconv_decode_sint32(lua_State *L) { + lua_pushinteger(L, pb_decode_sint32((uint32_t)luaL_checkinteger(L, 1))); + return 1; +} + +static int Lconv_encode_sint64(lua_State *L) { + lua_pushinteger(L, pb_encode_sint64(luaL_checkinteger(L, 1))); + return 1; +} + +static int Lconv_decode_sint64(lua_State *L) { + lua_pushinteger(L, pb_decode_sint64(luaL_checkinteger(L, 1))); + return 1; +} + +static int Lconv_encode_float(lua_State *L) { + lua_pushinteger(L, pb_encode_float((float)luaL_checknumber(L, 1))); + return 1; +} + +static int Lconv_decode_float(lua_State *L) { + lua_pushnumber(L, pb_decode_float((uint32_t)luaL_checkinteger(L, 1))); + return 1; +} + +static int Lconv_encode_double(lua_State *L) { + lua_pushinteger(L, pb_encode_double(luaL_checknumber(L, 1))); + return 1; +} + +static int Lconv_decode_double(lua_State *L) { + lua_pushnumber(L, pb_decode_double(luaL_checkinteger(L, 1))); + return 1; +} + +LUALIB_API int luaopen_pb_conv(lua_State *L) { + luaL_Reg libs[] = { + { "decode_uint32", Lconv_encode_uint32 }, + { "decode_int32", Lconv_encode_int32 }, +#define ENTRY(name) { #name, Lconv_##name } + ENTRY(encode_int32), + ENTRY(encode_uint32), + ENTRY(encode_sint32), + ENTRY(encode_sint64), + ENTRY(decode_sint32), + ENTRY(decode_sint64), + ENTRY(decode_float), + ENTRY(decode_double), + ENTRY(encode_float), + ENTRY(encode_double), +#undef ENTRY + { NULL, NULL } + }; + luaL_newlib(L, libs); + return 1; +} + + +/* protobuf encode routine */ + +static int lpb_typefmt(const char *fmt) { + switch (*fmt) { + case 'b': return PB_Tbool; + case 'f': return PB_Tfloat; + case 'F': return PB_Tdouble; + case 'i': return PB_Tint32; + case 'j': return PB_Tsint32; + case 'u': return PB_Tuint32; + case 'x': return PB_Tfixed32; + case 'y': return PB_Tsfixed32; + case 'I': return PB_Tint64; + case 'J': return PB_Tsint64; + case 'U': return PB_Tuint64; + case 'X': return PB_Tfixed64; + case 'Y': return PB_Tsfixed64; + } + return -1; +} + +static int lpb_packfmt(lua_State *L, int idx, pb_Buffer *b, const char **pfmt, int level) { + const char *fmt = *pfmt; + int type, ltype; + size_t len; + luaL_argcheck(L, 1, level <= 100, "format level overflow"); + for (; *fmt != '\0'; ++fmt) { + switch (*fmt) { + case 'v': pb_addvarint64(b, (uint64_t)luaL_checkinteger(L, idx++)); break; + case 'd': pb_addfixed32(b, (uint32_t)luaL_checkinteger(L, idx++)); break; + case 'q': pb_addfixed64(b, (uint64_t)luaL_checkinteger(L, idx++)); break; + case 'c': pb_addslice(b, lpb_checkslice(L, idx++)); break; + case 's': pb_addbytes(b, lpb_checkslice(L, idx++)); break; + case '#': lpb_addlength(L, b, (size_t)luaL_checkinteger(L, idx++)); break; + case '(': + len = pb_bufflen(b); + ++fmt; + idx = lpb_packfmt(L, idx, b, &fmt, level+1); + lpb_addlength(L, b, len); + break; + case ')': + if (level == 0) luaL_argerror(L, 1, "unexpected ')' in format"); + *pfmt = fmt; + return idx; + case '\0': + default: + if ((type = lpb_typefmt(fmt)) < 0) + argerror(L, 1, "invalid formater: '%c'", *fmt); + if ((ltype = lpb_addtype(L, b, idx, type)) != 0) + argerror(L, idx, "%s expected for type '%s', got %s", + lua_typename(L, ltype), pb_typename(type, ""), + luaL_typename(L, idx)); + ++idx; + } + } + if (level != 0) luaL_argerror(L, 1, "unmatch '(' in format"); + *pfmt = fmt; + return idx; +} + +static int Lpb_tohex(lua_State *L) { + pb_Slice s = lpb_checkslice(L, 1); + const char *hexa = "0123456789ABCDEF"; + char hex[4] = "XX "; + lua_Integer i = 1, j = -1; + luaL_Buffer lb; + rangerelat(L, 2, &i, &j, pb_len(s)); + luaL_buffinit(L, &lb); + for (; i <= j; ++i) { + unsigned int ch = s.p[i-1]; + hex[0] = hexa[(ch>>4)&0xF]; + hex[1] = hexa[(ch )&0xF]; + if (i == j) hex[2] = '\0'; + luaL_addstring(&lb, hex); + } + luaL_pushresult(&lb); + return 1; +} + +static int Lpb_result(lua_State *L) { + pb_Slice s = lpb_checkslice(L, 1); + lua_Integer i = 1, j = -1; + lua_Integer range = rangerelat(L, 2, &i, &j, pb_len(s)); + lua_pushlstring(L, s.p+i-1, (size_t)range); + return 1; +} + +static int Lbuf_new(lua_State *L) { + int i, top = lua_gettop(L); + pb_Buffer *buf = (pb_Buffer*)lua_newuserdata(L, sizeof(pb_Buffer)); + pb_initbuffer(buf); + luaL_setmetatable(L, PB_BUFFER); + for (i = 1; i <= top; ++i) + pb_addslice(buf, lpb_checkslice(L, i)); + return 1; +} + +static int Lbuf_libcall(lua_State *L) { + int i, top = lua_gettop(L); + pb_Buffer *buf = (pb_Buffer*)lua_newuserdata(L, sizeof(pb_Buffer)); + pb_initbuffer(buf); + luaL_setmetatable(L, PB_BUFFER); + for (i = 2; i <= top; ++i) + pb_addslice(buf, lpb_checkslice(L, i)); + return 1; +} + +static int Lbuf_tostring(lua_State *L) { + pb_Buffer *buf = check_buffer(L, 1); + lua_pushfstring(L, "pb.Buffer: %p", buf); + return 1; +} + +static int Lbuf_reset(lua_State *L) { + pb_Buffer *buf = check_buffer(L, 1); + int i, top = lua_gettop(L); + pb_resetbuffer(buf); + for (i = 2; i <= top; ++i) + pb_addslice(buf, lpb_checkslice(L, i)); + return_self(L); +} + +static int Lbuf_len(lua_State *L) { + pb_Buffer *buf = check_buffer(L, 1); + lua_pushinteger(L, (lua_Integer)buf->size); + return 1; +} + +static int Lbuf_pack(lua_State *L) { + pb_Buffer b, *pb = test_buffer(L, 1); + int idx = 1 + (pb != NULL); + const char *fmt = luaL_checkstring(L, idx++); + if (pb == NULL) pb_initbuffer(pb = &b); + lpb_packfmt(L, idx, pb, &fmt, 0); + if (pb != &b) + lua_settop(L, 1); + else { + pb_Slice ret = pb_result(pb); + lua_pushlstring(L, ret.p, pb_len(ret)); + pb_resetbuffer(pb); + } + return 1; +} + +LUALIB_API int luaopen_pb_buffer(lua_State *L) { + luaL_Reg libs[] = { + { "__tostring", Lbuf_tostring }, + { "__len", Lbuf_len }, + { "__gc", Lbuf_reset }, + { "delete", Lbuf_reset }, + { "tohex", Lpb_tohex }, + { "result", Lpb_result }, +#define ENTRY(name) { #name, Lbuf_##name } + ENTRY(new), + ENTRY(reset), + ENTRY(pack), +#undef ENTRY + { NULL, NULL } + }; + if (luaL_newmetatable(L, PB_BUFFER)) { + luaL_setfuncs(L, libs, 0); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + lua_createtable(L, 0, 1); + lua_pushcfunction(L, Lbuf_libcall); + lua_setfield(L, -2, "__call"); + lua_setmetatable(L, -2); + } + return 1; +} + + +/* protobuf decode routine */ + +#define LPB_INITSTACKLEN 16 + +typedef struct lpb_Slice { + pb_SliceExt curr; + pb_SliceExt *buff; + size_t used; + size_t size; + pb_SliceExt init_buff[LPB_INITSTACKLEN]; +} lpb_Slice; + +static void lpb_resetslice(lua_State *L, lpb_Slice *s) { + if (s->buff != s->init_buff) + xfree(s->buff); + memset(s, 0, sizeof(lpb_Slice)); + s->buff = s->init_buff; + s->size = LPB_INITSTACKLEN; + lua_pushnil(L); + lua_rawsetp(L, LUA_REGISTRYINDEX, s); +} + +static pb_SliceExt lpb_checkview(lua_State *L, int idx, pb_SliceExt *ps) { + pb_Slice src = lpb_checkslice(L, idx); + lua_Integer i = 1, j = -1; + lua_Integer range = rangerelat(L, idx+1, &i, &j, pb_len(src)); + pb_SliceExt ret; + if (ps) ps->base = src, ps->head = src.p; + ret.base.p = src.p + i - 1; + ret.base.end = ret.base.p + range; + ret.head = src.p; + return ret; +} + +static void lpb_enterview(lua_State *L, lpb_Slice *s, pb_SliceExt view) { + if (s->used >= s->size) { + size_t newsize = s->size * 2; + pb_SliceExt *oldp = s->buff != s->init_buff ? s->buff : NULL; + pb_SliceExt *newp = (pb_SliceExt*)xrealloc(oldp, newsize*sizeof(pb_SliceExt)); + if (newp == NULL) { luaL_error(L, "out of memory"); return; } + if (oldp == NULL) memcpy(newp, s->buff, s->used*sizeof(pb_SliceExt)); + s->buff = newp; + s->size = newsize; + } + s->buff[s->used++] = s->curr; + s->curr = view; +} + +static void lpb_initslice(lua_State *L, int idx, lpb_Slice *s) { + memset(s, 0, sizeof(lpb_Slice)); + s->buff = s->init_buff; + s->size = LPB_INITSTACKLEN; + if (!lua_isnoneornil(L, idx)) { + pb_SliceExt base, view = lpb_checkview(L, idx, &base); + s->curr = base; + lpb_enterview(L, s, view); + lua_pushvalue(L, idx); + lua_rawsetp(L, LUA_REGISTRYINDEX, s); + } +} + +static int lpb_unpackscalar(lua_State *L, int *pidx, int top, int fmt, pb_SliceExt *s) { + lua_Integer i; + lpb_Value v; + switch (fmt) { + case 'v': + if (pb_readvarint64(&s->base, &v.u64) == 0) + luaL_error(L, "invalid varint value at offset %d", lpb_offset(s)); + lua_pushinteger(L, v.u64); + break; + case 'd': + if (pb_readfixed32(&s->base, &v.u32) == 0) + luaL_error(L, "invalid fixed32 value at offset %d", lpb_offset(s)); + lua_pushinteger(L, v.u32); + break; + case 'q': + if (pb_readfixed64(&s->base, &v.u64) == 0) + luaL_error(L, "invalid fixed64 value at offset %d", lpb_offset(s)); + lua_pushinteger(L, v.u64); + break; + case 's': + if (pb_readbytes(&s->base, &v.s->base) == 0) + luaL_error(L, "invalid bytes value at offset %d", lpb_offset(s)); + lua_pushlstring(L, v.s->base.p, pb_len(v.s->base)); + break; + case 'c': + luaL_argcheck(L, 1, *pidx <= top, "format argument exceed"); + i = luaL_checkinteger(L, *pidx++); + if (pb_readslice(&s->base, (size_t)i, &v.s->base) == 0) + luaL_error(L, "invalid sub string at offset %d", lpb_offset(s)); + lua_pushlstring(L, v.s->base.p, pb_len(v.s->base)); + break; + default: + return 0; + } + return 1; +} + +static int lpb_unpackloc(lua_State *L, int *pidx, int top, int fmt, pb_SliceExt *s, int *prets) { + lua_Integer li; + size_t len = s->base.end - s->head; + switch (fmt) { + case '@': + lua_pushinteger(L, lpb_offset(s)); + ++*prets; + break; + + case '*': case '+': + luaL_argcheck(L, 1, *pidx <= top, "format argument exceed"); + if (fmt == '*') + li = posrelat(luaL_checkinteger(L, *pidx++), len); + else + li = lpb_offset(s) + luaL_checkinteger(L, *pidx++); + if (li == 0) li = 1; + if (li > (lua_Integer)len) li = len + 1; + s->base.p = s->head + li - 1; + break; + + default: + return 0; + } + return 1; +} + +static int lpb_unpackfmt(lua_State *L, int idx, const char *fmt, pb_SliceExt *s) { + int rets = 0, top = lua_gettop(L), type; + for (; *fmt != '\0'; ++fmt) { + if (lpb_unpackloc(L, &idx, top, *fmt, s, &rets)) + continue; + if (s->base.p >= s->base.end) { lua_pushnil(L); return rets + 1; } + luaL_checkstack(L, 1, "too many values"); + if (!lpb_unpackscalar(L, &idx, top, *fmt, s)) { + if ((type = lpb_typefmt(fmt)) < 0) + argerror(L, 1, "invalid formater: '%c'", *fmt); + lpb_readtype(L, type, s); + } + ++rets; + } + return rets; +} + +static int Lslice_new(lua_State *L) { + lpb_Slice *s; + lua_settop(L, 3); + s = (lpb_Slice*)lua_newuserdata(L, sizeof(lpb_Slice)); + lpb_initslice(L, 1, s); + luaL_setmetatable(L, PB_SLICE); + return 1; +} + +static int Lslice_libcall(lua_State *L) { + lpb_Slice *s; + lua_settop(L, 4); + s = (lpb_Slice*)lua_newuserdata(L, sizeof(lpb_Slice)); + lpb_initslice(L, 2, s); + luaL_setmetatable(L, PB_SLICE); + return 1; +} + +static int Lslice_reset(lua_State *L) { + lpb_Slice *s = (lpb_Slice*)check_slice(L, 1); + lpb_resetslice(L, s); + if (!lua_isnoneornil(L, 2)) + lpb_initslice(L, 2, s); + return_self(L); +} + +static int Lslice_tostring(lua_State *L) { + lpb_Slice *s = (lpb_Slice*)check_slice(L, 1); + lua_pushfstring(L, "pb.Slice: %p", s); + return 1; +} + +static int Lslice_len(lua_State *L) { + lpb_Slice *s = (lpb_Slice*)check_slice(L, 1); + lua_pushinteger(L, (lua_Integer)pb_len(s->curr.base)); + lua_pushinteger(L, (lua_Integer)lpb_offset(&s->curr)); + return 2; +} + +static int Lslice_level(lua_State *L) { + lpb_Slice *s = (lpb_Slice*)check_slice(L, 1); + if (!lua_isnoneornil(L, 2)) { + pb_SliceExt *se; + lua_Integer level = posrelat(luaL_checkinteger(L, 2), s->used); + if (level > (lua_Integer)s->used) + return 0; + else if (level == (lua_Integer)s->used) + se = &s->curr; + else + se = &s->buff[level]; + lua_pushinteger(L, se->base.p - s->buff[0].head + 1); + lua_pushinteger(L, se->head - s->buff[0].head + 1); + lua_pushinteger(L, se->base.end - s->buff[0].head); + return 3; + } + lua_pushinteger(L, s->used); + return 1; +} + +static int Lslice_enter(lua_State *L) { + lpb_Slice *s = (lpb_Slice*)check_slice(L, 1); + pb_SliceExt view; + if (lua_isnoneornil(L, 2)) { + if (pb_readbytes(&s->curr.base, &view.base) == 0) + return argerror(L, 1, "bytes wireformat expected at offset %d", + lpb_offset(&s->curr)); + view.head = view.base.p; + lpb_enterview(L, s, view); + } + else { + lua_Integer i = 1, j = -1; + lua_Integer range = rangerelat(L, 2, &i, &j, s->curr.base.end - s->curr.head); + view.base.p = s->curr.head + i - 1; + view.base.end = view.base.p + range; + view.head = s->curr.head; + lpb_enterview(L, s, view); + } + return_self(L); +} + +static int Lslice_leave(lua_State *L) { + lpb_Slice *s = (lpb_Slice*)check_slice(L, 1); + lua_Integer count = posrelat(luaL_optinteger(L, 2, 1), s->used); + if (count > (lua_Integer)s->used) + argerror(L, 2, "level (%d) exceed max level %d", + (int)count, (int)s->used); + else if (count == (lua_Integer)s->used) { + s->curr = s->buff[0]; + s->used = 1; + } + else { + s->used -= (size_t)count; + s->curr = s->buff[s->used]; + } + lua_settop(L, 1); + lua_pushinteger(L, s->used); + return 2; +} + +static int Lslice_unpack(lua_State *L) { + pb_SliceExt view, *s = test_slice(L, 1); + const char *fmt = luaL_checkstring(L, 2); + if (s == NULL) view = lpb_initext(lpb_checkslice(L, 1)), s = &view; + return lpb_unpackfmt(L, 3, fmt, s); +} + +LUALIB_API int luaopen_pb_slice(lua_State *L) { + luaL_Reg libs[] = { + { "__tostring", Lslice_tostring }, + { "__len", Lslice_len }, + { "__gc", Lslice_reset }, + { "delete", Lslice_reset }, + { "tohex", Lpb_tohex }, + { "result", Lpb_result }, +#define ENTRY(name) { #name, Lslice_##name } + ENTRY(new), + ENTRY(reset), + ENTRY(level), + ENTRY(enter), + ENTRY(leave), + ENTRY(unpack), +#undef ENTRY + { NULL, NULL } + }; + if (luaL_newmetatable(L, PB_SLICE)) { + luaL_setfuncs(L, libs, 0); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + lua_createtable(L, 0, 1); + lua_pushcfunction(L, Lslice_libcall); + lua_setfield(L, -2, "__call"); + lua_setmetatable(L, -2); + } + return 1; +} + + +/* high level typeinfo/encode/decode routines */ + +#define default_state(L) ((pb_State*)default_lstate(L)) + +static const char state_name[] = PB_STATE; + +typedef struct lpb_State { + pb_State base; + int enum_as_value; +} lpb_State; + +static int Lpb_delete(lua_State *L) { + if (lua53_rawgetp(L, LUA_REGISTRYINDEX, state_name) == LUA_TUSERDATA) { + lpb_State *LS = (lpb_State*)lua_touserdata(L, -1); + if (LS != NULL) { + pb_free(&LS->base); + lua_pushnil(L); + lua_setfield(L, LUA_REGISTRYINDEX, PB_STATE); + } + } + return 0; +} + +static lpb_State *default_lstate(lua_State *L) { + lpb_State *LS; + if (lua53_rawgetp(L, LUA_REGISTRYINDEX, state_name) == LUA_TUSERDATA) { + LS = (lpb_State*)lua_touserdata(L, -1); + lua_pop(L, 1); + } + else { + LS = lua_newuserdata(L, sizeof(lpb_State)); + lua_replace(L, -2); + memset(LS, 0, sizeof(lpb_State)); + pb_init(&LS->base); + lua_createtable(L, 0, 1); + lua_pushcfunction(L, Lpb_delete); + lua_setfield(L, -2, "__gc"); + lua_setmetatable(L, -2); + lua_rawsetp(L, LUA_REGISTRYINDEX, state_name); + } + return LS; +} + +static pb_Type *lpb_type(pb_State *S, const char *name) { + pb_Type *t; + if (name == NULL || *name == '.') + t = pb_type(S, pb_name(S, name)); + else { + pb_Buffer b; + pb_initbuffer(&b); + pb_addchar(&b, '.'); + pb_addslice(&b, pb_slice(name)); + pb_addchar(&b, '\0'); + t = pb_type(S, pb_name(S, pb_buffer(&b))); + pb_resetbuffer(&b); + } + return t; +} + +static pb_Field *lpb_checkfield(lua_State *L, int idx, pb_Type *t) { + int isint, number = (int)lua_tointegerx(L, idx, &isint); + if (isint) return pb_field(t, number); + return pb_fname(t, pb_name(default_state(L), luaL_checkstring(L, idx))); +} + +static int Lpb_clear(lua_State *L) { + pb_State *S = default_state(L); + if (lua_isnoneornil(L, 1)) + pb_free(S), pb_init(S); + else if (lua_isnoneornil(L, 2)) + pb_deltype(S, lpb_type(S, luaL_checkstring(L, 1))); + else { + pb_Type *t = lpb_type(S, luaL_checkstring(L, 1)); + pb_Field *f = lpb_checkfield(L, 2, t); + if (f) pb_delfield(S, t, f); + } + return 0; +} + +static int Lpb_load(lua_State *L) { + pb_State *S = default_state(L); + pb_SliceExt s = lpb_initext(lpb_checkslice(L, 1)); + lua_pushboolean(L, pb_load(S, &s.base) == PB_OK); + lua_pushinteger(L, lpb_offset(&s)); + return 2; +} + +static int Lpb_loadfile(lua_State *L) { + pb_State *S = default_state(L); + const char *filename = luaL_checkstring(L, 1); + size_t size; + pb_Buffer b; + pb_SliceExt s; + int ret; + FILE *fp = fopen(filename, "rb"); + if (fp == NULL) + return luaL_fileresult(L, 0, filename); + pb_initbuffer(&b); + do { + void *d = pb_prepbuffsize(&b, BUFSIZ); + if (d == NULL) { fclose(fp); return luaL_error(L, "out of memory"); } + size = fread(d, 1, BUFSIZ, fp); + pb_addsize(&b, size); + } while (size == BUFSIZ); + fclose(fp); + s = lpb_initext(pb_result(&b)); + ret = pb_load(S, &s.base); + pb_resetbuffer(&b); + lua_pushboolean(L, ret == PB_OK); + lua_pushinteger(L, lpb_offset(&s)); + return 2; +} + +static int lpb_pushtype(lua_State *L, pb_Type *t) { + if (t == NULL) return 0; + lua_pushstring(L, (char*)t->name); + lua_pushstring(L, (char*)t->basename); + lua_pushstring(L, t->is_map ? "map" : t->is_enum ? "enum" : "message"); + return 3; +} + +static int lpb_pushfield(lua_State *L, pb_Type *t, pb_Field *f) { + pb_OneofEntry *e; + if (f == NULL) return 0; + lua_pushstring(L, (char*)f->name); + lua_pushinteger(L, f->number); + lua_pushstring(L, f->type ? (char*)f->type->name : + pb_typename(f->type_id, "")); + lua_pushstring(L, (char*)f->default_value); + lua_pushstring(L, f->packed ? "packed" : + f->repeated ? "repeated" : "optional"); + e = (pb_OneofEntry*)pb_gettable(&t->oneof_index, (pb_Key)f); + if (e) { + lua_pushstring(L, (const char*)e->name); + lua_pushinteger(L, e->index-1); + return 7; + } + return 5; +} + +static int Lpb_typesiter(lua_State *L) { + pb_State *S = default_state(L); + pb_Type *t = lpb_type(S, lua_tostring(L, 2)); + if ((t == NULL && !lua_isnoneornil(L, 2))) + return 0; + while (pb_nexttype(S, &t) && t->field_count == 0) + continue; + return lpb_pushtype(L, t); +} + +static int Lpb_types(lua_State *L) { + lua_pushcfunction(L, Lpb_typesiter); + lua_pushnil(L); + lua_pushnil(L); + return 3; +} + +static int Lpb_fieldsiter(lua_State *L) { + pb_State *S = default_state(L); + pb_Type *t = lpb_type(S, luaL_checkstring(L, 1)); + pb_Field *f = pb_fname(t, pb_name(S, lua_tostring(L, 2))); + if ((f == NULL && !lua_isnoneornil(L, 2)) || !pb_nextfield(t, &f)) + return 0; + return lpb_pushfield(L, t, f); +} + +static int Lpb_fields(lua_State *L) { + lua_pushcfunction(L, Lpb_fieldsiter); + lua_pushvalue(L, 1); + lua_pushnil(L); + return 3; +} + +static int Lpb_type(lua_State *L) { + pb_State *S = default_state(L); + pb_Type *t = lpb_type(S, luaL_checkstring(L, 1)); + if (t == NULL || t->field_count == 0) + return 0; + return lpb_pushtype(L, t); +} + +static int Lpb_field(lua_State *L) { + pb_State *S = default_state(L); + pb_Type *t = lpb_type(S, luaL_checkstring(L, 1)); + return lpb_pushfield(L, t, lpb_checkfield(L, 2, t)); +} + +static int Lpb_enum(lua_State *L) { + pb_State *S = default_state(L); + pb_Type *t = lpb_type(S, luaL_checkstring(L, 1)); + pb_Field *f = lpb_checkfield(L, 2, t); + if (f == NULL) return 0; + if (lua_type(L, 2) == LUA_TNUMBER) + lua_pushstring(L, (char*)f->name); + else + lua_pushinteger(L, f->number); + return 1; +} + +static int lpb_pushdefault(lua_State *L, lpb_State *S, pb_Field *f) { + lua_Number ln; + lua_Integer li; + char *end; + switch (f->type_id) { + case PB_Tbytes: case PB_Tstring: + lua_pushstring(L, (char*)f->default_value); + break; + case PB_Tenum: + if (!(f = pb_fname(f->type, f->default_value))) return 0; + if (S->enum_as_value) lua_pushinteger(L, (lua_Integer)f->number); + else lua_pushstring(L, (char*)f->name); + break; + case PB_Tbool: + if (f->default_value == pb_name(&S->base, "true")) + lua_pushboolean(L, 1); + else if (f->default_value == pb_name(&S->base, "false")) + lua_pushboolean(L, 0); + else return 0; + break; + case PB_Tdouble: case PB_Tfloat: + ln = (lua_Number)strtod((char*)f->default_value, &end); + if ((char*)f->default_value == end) return 0; + lua_pushnumber(L, ln); + break; + default: + li = (lua_Integer)strtol((char*)f->default_value, &end, 10); + if ((char*)f->default_value == end) return 0; + lua_pushinteger(L, li); + break; + } + return 1; +} + +static int Lpb_defaults(lua_State *L) { + lpb_State *S = default_lstate(L); + pb_Type *t = lpb_type(&S->base, luaL_checkstring(L, 1)); + pb_Field *f = NULL; + if (!lua_istable(L, 2)) { + lua_settop(L, 1); + lua_newtable(L); + } + while (pb_nextfield(t, &f)) { + int type = lua53_getfield(L, 2, (char*)f->name); + if (f->default_value && type == LUA_TNIL && lpb_pushdefault(L, S, f)) + lua_setfield(L, 2, (char*)f->name); + lua_pop(L, 1); + } + return 1; +} + + +/* encode protobuf */ + +static void lpb_encode (lua_State *L, pb_Buffer *b, pb_Type *t); + +static void lpb_checktable(lua_State *L, pb_Field *f) { + if (!lua_istable(L, -1)) + argerror(L, 2, "table expected at field '%s', got %s", + (char*)f->name, luaL_typename(L, -1)); +} + +static void lpbE_enum(lua_State *L, pb_Buffer *b, pb_Field *f) { + int type = lua_type(L, -1); + if (type == LUA_TNUMBER) { + lua_Integer v = lua_tointeger(L, -1); + pb_addvarint64(b, (uint64_t)v); + } + else if (type != LUA_TSTRING && type != LUA_TUSERDATA) + argerror(L, 2, "number/string expected at field '%s', got %s", + (char*)f->name, luaL_typename(L, -1)); + else { + pb_State *S = default_state(L); + pb_Field *ev = pb_fname(f->type, pb_name(S, lua_tostring(L, -1))); + if (ev == NULL) + argerror(L, 2, "can not encode unknown enum '%s' at field '%s'", + lua_tostring(L, -1), (char*)f->name); + pb_addvarint32(b, ev->number); + } +} + +static void lpbE_field(lua_State *L, pb_Buffer *b, pb_Field *f, int hastag) { + size_t len; + int ltype; + if (hastag) pb_addvarint32(b, pb_pair(f->number, pb_wtypebytype(f->type_id))); + switch (f->type_id) { + case PB_Tenum: + lpbE_enum(L, b, f); + break; + + case PB_Tmessage: + lpb_checktable(L, f); + len = pb_bufflen(b); + lpb_encode(L, b, f->type); + lpb_addlength(L, b, len); + break; + + default: + if ((ltype = lpb_addtype(L, b, -1, f->type_id)) != 0) + argerror(L, 2, "%s expected at field '%s', got %s", + lua_typename(L, ltype), + (char*)f->name, luaL_typename(L, -1)); + } +} + +static void lpbE_map(lua_State *L, pb_Buffer *b, pb_Field *f) { + pb_Field *kf = pb_field(f->type, 1); + pb_Field *vf = pb_field(f->type, 2); + size_t len; + if (kf == NULL || vf == NULL) return; + lpb_checktable(L, f); + lua_pushnil(L); + while (lua_next(L, -2)) { + pb_addvarint32(b, pb_pair(f->number, PB_TBYTES)); + len = pb_bufflen(b); + lpbE_field(L, b, vf, 1); + lua_pop(L, 1); + lpbE_field(L, b, kf, 1); + lpb_addlength(L, b, len); + } +} + +static void lpbE_repeated(lua_State *L, pb_Buffer *b, pb_Field *f) { + int i; + lpb_checktable(L, f); + if (f->packed) { + size_t len; + pb_addvarint32(b, pb_pair(f->number, PB_TBYTES)); + len = pb_bufflen(b); + for (i = 1; lua53_rawgeti(L, -1, i) != LUA_TNIL; ++i) { + lpbE_field(L, b, f, 0); + lua_pop(L, 1); + } + lpb_addlength(L, b, len); + } + else { + for (i = 1; lua53_rawgeti(L, -1, i) != LUA_TNIL; ++i) { + lpbE_field(L, b, f, 1); + lua_pop(L, 1); + } + } + lua_pop(L, 1); +} + +static void lpb_encode(lua_State *L, pb_Buffer *b, pb_Type *t) { + luaL_checkstack(L, 3, "message too many levels"); + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_type(L, -2) == LUA_TSTRING) { + pb_State *S = default_state(L); + pb_Field *f = pb_fname(t, pb_name(S, lua_tostring(L, -2))); + if (f == NULL) + /* skip */; + else if (f->type && f->type->is_map) + lpbE_map(L, b, f); + else if (f->repeated) + lpbE_repeated(L, b, f); + else if (!f->type || f->type->field_count != 0) + lpbE_field(L, b, f, 1); + } + lua_pop(L, 1); + } +} + +static int lpb_encode_helper(lua_State *L) { + pb_Buffer *b = (pb_Buffer*)lua_touserdata(L, 1); + pb_Type *t = (pb_Type*)lua_touserdata(L, 2); + pb_Buffer *r = test_buffer(L, 3); + lpb_encode(L, b, t); + if (r != b) + lua_pushlstring(L, b->buff, b->size); + else + lua_pop(L, 1); + return 1; +} + +static int Lpb_encode(lua_State *L) { + pb_State *S = default_state(L); + pb_Type *t = lpb_type(S, luaL_checkstring(L, 1)); + pb_Buffer buf, *b = test_buffer(L, 3); + luaL_checktype(L, 2, LUA_TTABLE); + if (t == NULL) { + lua_pushnil(L); + lua_pushfstring(L, "type '%s' does not exists", lua_tostring(L, 1)); + return 2; + } + if (b == NULL) pb_initbuffer(&buf), b = &buf; + lua_pushcfunction(L, lpb_encode_helper); + lua_pushlightuserdata(L, b); + lua_pushlightuserdata(L, t); + lua_pushvalue(L, 3); + lua_pushvalue(L, 2); + if (lua_pcall(L, 4, 1, 0) != LUA_OK) { + lua_pushnil(L); + lua_insert(L, -2); + return 2; + } + if (b == &buf) pb_resetbuffer(&buf); + return 1; +} + + +/* decode protobuf */ + +static int lpb_decode(lua_State *L, pb_SliceExt *s, pb_Type *t); + +static void lpb_fetchtable(lua_State *L, pb_Field *f) { + if (lua53_getfield(L, -1, (char*)f->name) == LUA_TNIL) { + lua_pop(L, 1); + lua_newtable(L); + lua_pushvalue(L, -1); + lua_setfield(L, -3, (char*)f->name); + } +} + +static void lpbD_field(lua_State *L, pb_SliceExt *s, pb_Field *f, uint32_t tag) { + pb_SliceExt sv; + pb_Field *ev; + uint64_t u64; + + switch (f->type_id) { + case PB_Tenum: + if (pb_readvarint64(&s->base, &u64) == 0) + luaL_error(L, "invalid varint value at offset %d", lpb_offset(s)); + ev = pb_field(f->type, (int32_t)u64); + if (default_lstate(L)->enum_as_value) ev = NULL; + if (ev) lua_pushstring(L, (char*)ev->name); + else lua_pushinteger(L, (lua_Integer)u64); + break; + + case PB_Tmessage: + lpb_readbytes(L, s, &sv); + if (f->type) { + lua_newtable(L); + lpb_decode(L, &sv, f->type); + } + break; + + default: + if (!f->packed && pb_wtypebytype(f->type_id) != (int)pb_gettype(tag)) + luaL_error(L, "type mismatch at offset %d, %s expected for type %s, got %s", + lpb_offset(s), + pb_wtypename(pb_wtypebytype(f->type_id), NULL), + pb_typename(f->type_id, NULL), + pb_wtypename(pb_gettype(tag), NULL)); + lpb_readtype(L, f->type_id, s); + } +} + +static void lpbD_map(lua_State *L, pb_SliceExt *s, pb_Field *f) { + pb_SliceExt p; + int mask = 0, top = lua_gettop(L) + 1; + uint32_t tag; + lpb_fetchtable(L, f); + lpb_readbytes(L, s, &p); + if (f->type == NULL) return; + lua_pushnil(L); + lua_pushnil(L); + while (pb_readvarint32(&p.base, &tag)) { + int n = pb_gettag(tag); + if (n == 1 || n == 2) { + mask |= n; + lpbD_field(L, &p, pb_field(f->type, n), tag); + lua_replace(L, top+n); + } + } + if (mask == 3) lua_rawset(L, -3); + else lua_pop(L, 2); + lua_pop(L, 1); +} + +static void lpbD_repeated(lua_State *L, pb_SliceExt *s, pb_Field *f, uint32_t tag) { + lpb_fetchtable(L, f); + if (f->packed) { + int len = lua_rawlen(L, -1); + pb_SliceExt p; + lpb_readbytes(L, s, &p); + while (p.base.p < p.base.end) { + lpbD_field(L, &p, f, tag); + lua_rawseti(L, -2, ++len); + } + } + else { + lpbD_field(L, s, f, tag); + lua_rawseti(L, -2, lua_rawlen(L, -2) + 1); + } + lua_pop(L, 1); +} + +static int lpb_decode(lua_State *L, pb_SliceExt *s, pb_Type *t) { + uint32_t tag; + while (pb_readvarint32(&s->base, &tag)) { + pb_Field *f = pb_field(t, pb_gettag(tag)); + if (f == NULL) + pb_skipvalue(&s->base, tag); + else if (f->type && f->type->is_map) + lpbD_map(L, s, f); + else if (f->repeated) + lpbD_repeated(L, s, f, tag); + else if (!f->type || f->type->field_count != 0) { + if (f->type && f->type_id == PB_Tmessage) { + pb_SliceExt sv; + lpb_fetchtable(L, f); + lpb_readbytes(L, s, &sv); + lpb_decode(L, &sv, f->type); + lua_pop(L, 1); + } + else { + lua_pushstring(L, (char*)f->name); + lpbD_field(L, s, f, tag); + lua_rawset(L, -3); + } + } + } + return 1; +} + +static int lpb_decode_helper(lua_State *L) { + return lpb_decode(L, + (pb_SliceExt*)lua_touserdata(L, 1), + (pb_Type*)lua_touserdata(L, 2)); +} + +static int Lpb_decode(lua_State *L) { + pb_State *S = default_state(L); + pb_Type *t = lpb_type(S, luaL_checkstring(L, 1)); + pb_SliceExt s; + if (t == NULL) { + lua_pushnil(L); + lua_pushfstring(L, "type '%s' does not exists", lua_tostring(L, 1)); + return 2; + } + s = lpb_initext(lpb_checkslice(L, 2)); + if (!lua_istable(L, 3)) { + lua_settop(L, 2); + lua_newtable(L); + } + lua_pushcfunction(L, lpb_decode_helper); + lua_pushlightuserdata(L, &s); + lua_pushlightuserdata(L, t); + lua_pushvalue(L, 3); + if (lua_pcall(L, 3, 1, 0) != LUA_OK) { + lua_pushnil(L); + lua_insert(L, -2); + return 2; + } + return 1; +} + + +/* pb module interface */ + +static int Lpb_option(lua_State *L) { + static const char *opts[] = { + "enum_as_value", "enum_as_name", NULL + }; + lpb_State *LS = default_lstate(L); + switch (luaL_checkoption(L, 1, NULL, opts)) { + case 0: /* enum_as_value */ + LS->enum_as_value = 1; + break; + case 1: /* enum_as_name */ + LS->enum_as_value = 0; + break; + } + return 0; +} + +LUALIB_API int luaopen_lprotobuf(lua_State *L) { + luaL_Reg libs[] = { + { "pack", Lbuf_pack }, + { "unpack", Lslice_unpack }, +#define ENTRY(name) { #name, Lpb_##name } + ENTRY(clear), + ENTRY(load), + ENTRY(loadfile), + ENTRY(encode), + ENTRY(decode), + ENTRY(types), + ENTRY(fields), + ENTRY(type), + ENTRY(field), + ENTRY(enum), + ENTRY(defaults), + ENTRY(tohex), + ENTRY(result), + ENTRY(option), +#undef ENTRY + { NULL, NULL } + }; + luaL_newlib(L, libs); + return 1; +} diff --git a/luaclib/src/lpbc/lpb.h b/luaclib/src/lpbc/lpb.h new file mode 100644 index 00000000..bd7ebbb0 --- /dev/null +++ b/luaclib/src/lpbc/lpb.h @@ -0,0 +1,1641 @@ +#ifndef pb_h +#define pb_h + +#ifndef PB_NS_BEGIN +# ifdef __cplusplus +# define PB_NS_BEGIN extern "C" { +# define PB_NS_END } +# else +# define PB_NS_BEGIN +# define PB_NS_END +# endif +#endif /* PB_NS_BEGIN */ + +#ifndef PB_STATIC +# if __GNUC__ +# define PB_STATIC static __attribute((unused)) +# else +# define PB_STATIC static +# endif +#endif + +#ifdef PB_STATIC_API +# ifndef PB_IMPLEMENTATION +# define PB_IMPLEMENTATION +# endif +# define PB_API PB_STATIC +#endif + +#if !defined(PB_API) && defined(_WIN32) +# ifdef PB_IMPLEMENTATION +# define PB_API __declspec(dllexport) +# else +# define PB_API __declspec(dllimport) +# endif +#endif + +#ifndef PB_API +# define PB_API extern +#endif + +#if defined(_MSC_VER) || defined(__UNIXOS2__) || defined(__SOL64__) +typedef unsigned char uint8_t; +typedef signed char int8_t; +typedef unsigned short uint16_t; +typedef signed short int16_t; +typedef unsigned int uint32_t; +typedef signed int int32_t; +typedef unsigned long long uint64_t; +typedef signed long long int64_t; + +#elif defined(__SCO__) || defined(__USLC__) || defined(__MINGW32__) +# include +#else +# include +# if (defined(__sun__) || defined(__digital__)) +# if defined(__STDC__) && (defined(__arch64__) || defined(_LP64)) +typedef unsigned long int uint64_t; +typedef signed long int int64_t; +# else +typedef unsigned long long uint64_t; +typedef signed long long int64_t; +# endif /* LP64 */ +# endif /* __sun__ || __digital__ */ +#endif + +// #include +// #include +#include "../../../src/core.h" + +PB_NS_BEGIN + + +/* types */ + +#define PB_WIRETYPES(X) /* X(name, index) */\ + X(VARINT, "varint", 0) X(64BIT, "64bit", 1) X(BYTES, "bytes", 2) \ + X(GSTART, "gstart", 3) X(GEND, "gend", 4) X(32BIT, "32bit", 5) \ + +#define PB_TYPES(X) /* X(name, type, index) */\ + X(double, double, 1) X(float, float, 2) \ + X(int64, int64_t, 3) X(uint64, uint64_t, 4) \ + X(int32, int32_t, 5) X(fixed64, uint64_t, 6) \ + X(fixed32, uint32_t, 7) X(bool, int, 8) \ + X(string, pb_Slice, 9) X(group, pb_Slice, 10) \ + X(message, pb_Slice, 11) X(bytes, pb_Slice, 12) \ + X(uint32, uint32_t, 13) X(enum, int32_t, 14) \ + X(sfixed32, int32_t, 15) X(sfixed64, int64_t, 16) \ + X(sint32, int32_t, 17) X(sint64, int64_t, 18) \ + +typedef enum pb_WireType { +#define X(name, s, index) PB_T##name, + PB_WIRETYPES(X) +#undef X + PB_TWIRECOUNT +} pb_WireType; + +typedef enum pb_FieldType { +#define X(name, type, index) PB_T##name = index, + PB_TYPES(X) +#undef X + PB_TYPECOUNT +} pb_FieldType; + + +/* conversions */ + +PB_API uint64_t pb_expandsig ( int32_t v); +PB_API uint32_t pb_encode_sint32 ( int32_t v); +PB_API int32_t pb_decode_sint32 (uint32_t v); +PB_API uint64_t pb_encode_sint64 ( int64_t v); +PB_API int64_t pb_decode_sint64 (uint64_t v); +PB_API uint32_t pb_encode_float (float v); +PB_API float pb_decode_float (uint32_t v); +PB_API uint64_t pb_encode_double (double v); +PB_API double pb_decode_double (uint64_t v); + + +/* decode */ + +typedef struct pb_Slice { const char *p, *end; } pb_Slice; +#define pb_gettype(v) ((v) & 7) +#define pb_gettag(v) ((v) >> 3) +#define pb_pair(tag, type) ((tag) << 3 | ((type) & 7)) + +PB_API pb_Slice pb_slice (const char *p); +PB_API pb_Slice pb_lslice (const char *p, size_t len); +PB_API size_t pb_len (pb_Slice s); + +PB_API size_t pb_readvarint32 (pb_Slice *s, uint32_t *pv); +PB_API size_t pb_readvarint64 (pb_Slice *s, uint64_t *pv); +PB_API size_t pb_readfixed32 (pb_Slice *s, uint32_t *pv); +PB_API size_t pb_readfixed64 (pb_Slice *s, uint64_t *pv); + +PB_API size_t pb_readslice (pb_Slice *s, size_t len, pb_Slice *pv); +PB_API size_t pb_readbytes (pb_Slice *s, pb_Slice *pv); +PB_API size_t pb_readgroup (pb_Slice *s, uint32_t tag, pb_Slice *pv); + +PB_API size_t pb_skipvarint (pb_Slice *s); +PB_API size_t pb_skipbytes (pb_Slice *s); +PB_API size_t pb_skipslice (pb_Slice *s, size_t len); +PB_API size_t pb_skipvalue (pb_Slice *s, uint32_t tag); + +PB_API const char *pb_wtypename (int wiretype, const char *def); +PB_API const char *pb_typename (int type, const char *def); + +PB_API int pb_typebyname (const char *name, int def); +PB_API int pb_wtypebyname (const char *name, int def); +PB_API int pb_wtypebytype (int type); + + +/* encode */ + +#define PB_BUFFERSIZE (1024) + +typedef struct pb_Buffer { + size_t size; + size_t capacity; + char *buff; + char init_buff[PB_BUFFERSIZE]; +} pb_Buffer; + +#define pb_buffer(b) ((b)->buff) +#define pb_bufflen(b) ((b)->size) +#define pb_addsize(b, sz) ((b)->size += (sz)) +#define pb_addchar(b, ch) \ + ((void)((b)->size < (b)->capacity || pb_prepbuffsize((b), 1)), \ + ((b)->buff[(b)->size++] = (ch))) + +PB_API void pb_initbuffer (pb_Buffer *b); +PB_API void pb_resetbuffer (pb_Buffer *b); +PB_API size_t pb_resizebuffer (pb_Buffer *b, size_t len); +PB_API void *pb_prepbuffsize (pb_Buffer *b, size_t len); + +PB_API pb_Slice pb_result (pb_Buffer *b); + +PB_API size_t pb_addvarint32 (pb_Buffer *b, uint32_t v); +PB_API size_t pb_addvarint64 (pb_Buffer *b, uint64_t v); +PB_API size_t pb_addfixed32 (pb_Buffer *b, uint32_t v); +PB_API size_t pb_addfixed64 (pb_Buffer *b, uint64_t v); + +PB_API size_t pb_addslice (pb_Buffer *b, pb_Slice s); +PB_API size_t pb_addbytes (pb_Buffer *b, pb_Slice s); +PB_API size_t pb_addlength (pb_Buffer *b, size_t len); + + +/* type info database state and name table */ + +typedef struct pb_State pb_State; +typedef struct pb_Name pb_Name; + +PB_API void pb_init (pb_State *S); +PB_API void pb_free (pb_State *S); + +PB_API pb_Name *pb_newname (pb_State *S, pb_Slice s); +PB_API void pb_delname (pb_State *S, pb_Name *name); +PB_API pb_Name *pb_name (pb_State *S, const char *name); + +PB_API pb_Name *pb_usename (pb_Name *name); + + +/* type info */ + +typedef struct pb_Type pb_Type; +typedef struct pb_Field pb_Field; + +#define PB_OK 0 +#define PB_ERROR 1 +#define PB_ENOMEM 2 + +PB_API int pb_load (pb_State *S, pb_Slice *s); + +PB_API pb_Type *pb_newtype (pb_State *S, pb_Name *tname); +PB_API void pb_deltype (pb_State *S, pb_Type *t); +PB_API pb_Field *pb_newfield (pb_State *S, pb_Type *t, pb_Name *fname, int32_t number); +PB_API void pb_delfield (pb_State *S, pb_Type *t, pb_Field *f); + +PB_API pb_Type *pb_type (pb_State *S, pb_Name *tname); +PB_API pb_Field *pb_fname (pb_Type *t, pb_Name *tname); +PB_API pb_Field *pb_field (pb_Type *t, int32_t number); + +PB_API int pb_nexttype (pb_State *S, pb_Type **ptype); +PB_API int pb_nextfield (pb_Type *t, pb_Field **pfield); + + +/* util: memory pool */ + +#define PB_POOLSIZE 4096 + +typedef struct pb_Pool { + void *pages; + void *freed; + size_t obj_size; +} pb_Pool; + +PB_API void pb_initpool (pb_Pool *pool, size_t obj_size); +PB_API void pb_freepool (pb_Pool *pool); + +PB_API void *pb_poolalloc (pb_Pool *pool); +PB_API void pb_poolfree (pb_Pool *pool, void *obj); + +/* util: hash table */ + +typedef struct pb_Table pb_Table; +typedef struct pb_Entry pb_Entry; +typedef ptrdiff_t pb_Key; + +PB_API void pb_inittable (pb_Table *t, size_t entrysize); +PB_API void pb_freetable (pb_Table *t); + +PB_API size_t pb_resizetable (pb_Table *t, size_t size); + +PB_API pb_Entry *pb_gettable (pb_Table *t, pb_Key key); +PB_API pb_Entry *pb_settable (pb_Table *t, pb_Key key); +PB_API int pb_nextentry (pb_Table *t, pb_Entry **pentry); + +struct pb_Table { + size_t size; + size_t lastfree; + unsigned entry_size : sizeof(unsigned)*CHAR_BIT - 1; + unsigned has_zero : 1; + pb_Entry *hash; +}; + +struct pb_Entry { + ptrdiff_t next; + pb_Key key; +}; + + +/* fields */ + +typedef struct pb_NameEntry { + struct pb_NameEntry *next; + unsigned hash : 32; + unsigned length : 16; + unsigned refcount : 16; +} pb_NameEntry; + +typedef struct pb_NameTable { + size_t size; + size_t count; + pb_NameEntry **hash; +} pb_NameTable; + +struct pb_State { + pb_Table types; + pb_NameTable nametable; + pb_Pool typepool; + pb_Pool fieldpool; +}; + +struct pb_Field { + pb_Name *name; + pb_Type *type; + pb_Name *default_value; + int32_t number; + unsigned type_id : 29; /* PB_T* enum */ + unsigned repeated : 1; + unsigned packed : 1; + unsigned scalar : 1; +}; + +struct pb_Type { + pb_Name *name; + const char *basename; + pb_Table field_tags; + pb_Table field_names; + pb_Table oneof_index; + unsigned field_count : 30; + unsigned is_enum : 1; + unsigned is_map : 1; +}; + + +PB_NS_END + +#endif /* pb_h */ + + +#if defined(PB_IMPLEMENTATION) && !defined(pb_implemented) +#define pb_implemented + +#define PB_MAX_SIZET ((size_t)~0 - 100) +#define PB_MIN_STRTABLE_SIZE 16 +#define PB_MIN_HASHTABLE_SIZE 8 +#define PB_HASHLIMIT 5 + +#include +#include +#include + +PB_NS_BEGIN + + +/* conversions */ + +PB_API uint32_t pb_encode_sint32(int32_t value) +{ return ((uint32_t)value << 1) ^ (value >> 31); } + +PB_API int32_t pb_decode_sint32(uint32_t value) +{ return (value >> 1) ^ -(int32_t)(value & 1); } + +PB_API uint64_t pb_encode_sint64(int64_t value) +{ return ((uint64_t)value << 1) ^ (value >> 63); } + +PB_API int64_t pb_decode_sint64(uint64_t value) +{ return (value >> 1) ^ -(int64_t)(value & 1); } + +PB_API uint64_t pb_expandsig(int32_t value) +{ return (int64_t)value; } + +PB_API uint32_t pb_encode_float(float value) +{ union { uint32_t u32; float f; } u; u.f = value; return u.u32; } + +PB_API float pb_decode_float(uint32_t value) +{ union { uint32_t u32; float f; } u; u.u32 = value; return u.f; } + +PB_API uint64_t pb_encode_double(double value) +{ union { uint64_t u64; double d; } u; u.d = value; return u.u64; } + +PB_API double pb_decode_double(uint64_t value) +{ union { uint64_t u64; double d; } u; u.u64 = value; return u.d; } + + +/* decode */ + +PB_API pb_Slice pb_slice(const char *s) +{ return pb_lslice(s, strlen(s)); } + +PB_API pb_Slice pb_lslice(const char *s, size_t len) +{ pb_Slice slice; slice.p = s; slice.end = s + len; return slice; } + +PB_API size_t pb_len(pb_Slice s) +{ return s.end - s.p; } + +static size_t pb_readvarint_slow(pb_Slice *s, uint64_t *pv) { + const char *p = s->p; + uint64_t n = 0; + int i = 0; + while (s->p < s->end && i < 10) { + int b = *s->p++; + n |= ((uint64_t)b & 0x7F) << (7*i++); + if ((b & 0x80) == 0) { + *pv = n; + return i; + } + } + s->p = p; + return 0; +} + +static size_t pb_readvarint32_fallback(pb_Slice *s, uint32_t *pv) { + const uint8_t *p = (const uint8_t*)s->p, *o = p; + uint32_t b, n; + for (;;) { + n = *p++ - 0x80, n += (b = *p++) << 7; if (!(b & 0x80)) break; + n -= 0x80 << 7, n += (b = *p++) << 14; if (!(b & 0x80)) break; + n -= 0x80 << 14, n += (b = *p++) << 21; if (!(b & 0x80)) break; + n -= 0x80 << 21, n += (b = *p++) << 28; if (!(b & 0x80)) break; + /* n -= 0x80 << 28; */ + if (!(*p++ & 0x80)) break; + if (!(*p++ & 0x80)) break; + if (!(*p++ & 0x80)) break; + if (!(*p++ & 0x80)) break; + if (!(*p++ & 0x80)) break; + return 0; + } + *pv = n; + s->p = (const char*)p; + return p - o; +} + +static size_t pb_readvarint64_fallback(pb_Slice *s, uint64_t *pv) { + const uint8_t *p = (const uint8_t*)s->p, *o = p; + uint32_t b, n1, n2 = 0, n3 = 0; + for (;;) { + n1 = *p++ - 0x80, n1 += (b = *p++) << 7; if (!(b & 0x80)) break; + n1 -= 0x80 << 7, n1 += (b = *p++) << 14; if (!(b & 0x80)) break; + n1 -= 0x80 << 14, n1 += (b = *p++) << 21; if (!(b & 0x80)) break; + n1 -= 0x80 << 21, n2 += (b = *p++) ; if (!(b & 0x80)) break; + n2 -= 0x80 , n2 += (b = *p++) << 7; if (!(b & 0x80)) break; + n2 -= 0x80 << 7, n2 += (b = *p++) << 14; if (!(b & 0x80)) break; + n2 -= 0x80 << 14, n2 += (b = *p++) << 21; if (!(b & 0x80)) break; + n2 -= 0x80 << 21, n3 += (b = *p++) ; if (!(b & 0x80)) break; + n3 -= 0x80 , n3 += (b = *p++) << 7; if (!(b & 0x80)) break; + return 0; + } + *pv = n1 | ((uint64_t)n2 << 28) | ((uint64_t)n3 << 56); + s->p = (const char*)p; + return p - o; +} + +PB_API size_t pb_readvarint32(pb_Slice *s, uint32_t *pv) { + uint64_t u64; + size_t ret; + if (s->p >= s->end) return 0; + if (!(*s->p & 0x80)) { *pv = *s->p++; return 1; } + if (pb_len(*s) >= 10 || !(s->end[-1] & 0x80)) + return pb_readvarint32_fallback(s, pv); + if ((ret = pb_readvarint_slow(s, &u64)) != 0) + *pv = (uint32_t)u64; + return ret; +} + +PB_API size_t pb_readvarint64(pb_Slice *s, uint64_t *pv) { + if (s->p >= s->end) return 0; + if (!(*s->p & 0x80)) { *pv = *s->p++; return 1; } + if (pb_len(*s) >= 10 || !(s->end[-1] & 0x80)) + return pb_readvarint64_fallback(s, pv); + return pb_readvarint_slow(s, pv); +} + +PB_API size_t pb_readfixed32(pb_Slice *s, uint32_t *pv) { + int i; + uint32_t n = 0; + if (s->p + 4 > s->end) + return 0; + for (i = 3; i >= 0; --i) { + n <<= 8; + n |= s->p[i] & 0xFF; + } + s->p += 4; + *pv = n; + return 4; +} + +PB_API size_t pb_readfixed64(pb_Slice *s, uint64_t *pv) { + int i; + uint64_t n = 0; + if (s->p + 8 > s->end) + return 0; + for (i = 7; i >= 0; --i) { + n <<= 8; + n |= s->p[i] & 0xFF; + } + s->p += 8; + *pv = n; + return 8; +} + +PB_API size_t pb_readslice(pb_Slice *s, size_t len, pb_Slice *pv) { + if (pb_len(*s) < len) + return 0; + pv->p = s->p; + pv->end = s->p + len; + s->p = pv->end; + return len; +} + +PB_API size_t pb_readbytes(pb_Slice *s, pb_Slice *pv) { + const char *p = s->p; + uint64_t len; + if (pb_readvarint64(s, &len) == 0 || pb_len(*s) < len) { + s->p = p; + return 0; + } + pv->p = s->p; + pv->end = s->p + len; + s->p = pv->end; + return s->p - p; +} + +PB_API size_t pb_readgroup(pb_Slice *s, uint32_t tag, pb_Slice *pv) { + const char *p = s->p; + uint32_t newtag; + size_t count; + assert(pb_gettype(tag) == PB_TGSTART); + while ((count = pb_readvarint32(s, &newtag)) != 0) { + if (pb_gettype(newtag) == PB_TGEND) { + if (pb_gettag(newtag) != pb_gettag(tag)) + break; + pv->p = p; + pv->end = s->p - count; + return s->p - p; + } + pb_skipvalue(s, newtag); + } + s->p = p; + return 0; +} + +PB_API size_t pb_skipvalue(pb_Slice *s, uint32_t tag) { + const char *p = s->p; + size_t ret = 0; + pb_Slice data; + switch (pb_gettype(tag)) { + default: break; + case PB_TVARINT: ret = pb_skipvarint(s); break; + case PB_T64BIT: ret = pb_skipslice(s, 8); break; + case PB_TBYTES: ret = pb_skipbytes(s); break; + case PB_T32BIT: ret = pb_skipslice(s, 4); break; + case PB_TGSTART: ret = pb_readgroup(s, tag, &data); break; + } + if (!ret) s->p = p; + return ret; +} + +PB_API size_t pb_skipvarint(pb_Slice *s) { + const char *p = s->p, *op = p; + while (p < s->end && (*p & 0x80) != 0) ++p; + if (p >= s->end) return 0; + s->p = ++p; + return p - op; +} + +PB_API size_t pb_skipbytes(pb_Slice *s) { + const char *p = s->p; + uint64_t var; + if (!pb_readvarint64(s, &var)) return 0; + if (pb_len(*s) < var) { + s->p = p; + return 0; + } + s->p += var; + return s->p - p; +} + +PB_API size_t pb_skipslice(pb_Slice *s, size_t len) { + if (s->p + len > s->end) return 0; + s->p += len; + return len; +} + +PB_API int pb_wtypebytype(int type) { + switch (type) { + case PB_Tdouble: return PB_T64BIT; + case PB_Tfloat: return PB_T32BIT; + case PB_Tint64: return PB_TVARINT; + case PB_Tuint64: return PB_TVARINT; + case PB_Tint32: return PB_TVARINT; + case PB_Tfixed64: return PB_T64BIT; + case PB_Tfixed32: return PB_T32BIT; + case PB_Tbool: return PB_TVARINT; + case PB_Tstring: return PB_TBYTES; + case PB_Tmessage: return PB_TBYTES; + case PB_Tbytes: return PB_TBYTES; + case PB_Tuint32: return PB_TVARINT; + case PB_Tenum: return PB_TVARINT; + case PB_Tsfixed32: return PB_T32BIT; + case PB_Tsfixed64: return PB_T64BIT; + case PB_Tsint32: return PB_TVARINT; + case PB_Tsint64: return PB_TVARINT; + default: return PB_TWIRECOUNT; + } +} + +PB_API const char *pb_wtypename(int wiretype, const char *def) { + switch (wiretype) { +#define X(id, name, v) case v: return name; + PB_WIRETYPES(X) +#undef X + default: return def ? def : ""; + } +} + +PB_API const char *pb_typename(int type, const char *def) { + switch (type) { +#define X(name, t, v) case v: return #name; + PB_TYPES(X) +#undef X + default: return def ? def : ""; + } +} + +PB_API int pb_typebyname(const char *name, int def) { + static struct entry { const char *name; int value; } names[] = { +#define X(name, t, v) { #name, v }, + PB_TYPES(X) +#undef X + { NULL, 0 } + }; + struct entry *p; + for (p = names; p->name != NULL; ++p) + if (strcmp(p->name, name) == 0) + return p->value; + return def; +} + +PB_API int pb_wtypebyname(const char *name, int def) { + static struct entry { const char *name; int value; } names[] = { +#define X(id, name, v) { name, v }, + PB_WIRETYPES(X) +#undef X + { NULL, 0 } + }; + struct entry *p; + for (p = names; p->name != NULL; ++p) + if (strcmp(p->name, name) == 0) + return p->value; + return def; +} + + +/* encode */ + +PB_API pb_Slice pb_result(pb_Buffer *b) +{ pb_Slice slice = pb_lslice(b->buff, b->size); return slice; } + +PB_API void pb_initbuffer(pb_Buffer *b) +{ b->buff = b->init_buff, b->capacity = PB_BUFFERSIZE, b->size = 0; } + +PB_API void pb_resetbuffer(pb_Buffer *b) +{ if (b->buff != b->init_buff) xfree(b->buff); pb_initbuffer(b); } + +static int pb_write32(char *buff, uint32_t n) { + int p, c = 0; + do { + p = n & 0x7F; if ((n >>= 7) == 0) break; *buff++ = p | 0x80, ++c; + p = n & 0x7F; if ((n >>= 7) == 0) break; *buff++ = p | 0x80, ++c; + p = n & 0x7F; if ((n >>= 7) == 0) break; *buff++ = p | 0x80, ++c; + p = n & 0x7F; if ((n >>= 7) == 0) break; *buff++ = p | 0x80, ++c; + p = n; + } while (0); + return *buff++ = p, ++c; +} + +static int pb_write64(char *buff, uint64_t n) { + int p, c = 0; + do { + p = n & 0x7F; if ((n >>= 7) == 0) break; *buff++ = p | 0x80, ++c; + p = n & 0x7F; if ((n >>= 7) == 0) break; *buff++ = p | 0x80, ++c; + p = n & 0x7F; if ((n >>= 7) == 0) break; *buff++ = p | 0x80, ++c; + p = n & 0x7F; if ((n >>= 7) == 0) break; *buff++ = p | 0x80, ++c; + p = n & 0x7F; if ((n >>= 7) == 0) break; *buff++ = p | 0x80, ++c; + p = n & 0x7F; if ((n >>= 7) == 0) break; *buff++ = p | 0x80, ++c; + p = n & 0x7F; if ((n >>= 7) == 0) break; *buff++ = p | 0x80, ++c; + p = n & 0x7F; if ((n >>= 7) == 0) break; *buff++ = p | 0x80, ++c; + p = n & 0x7F; if ((n >>= 7) == 0) break; *buff++ = p | 0x80, ++c; + p = n & 0x7F; + } while (0); + return *buff++ = p, ++c; +} + +PB_API size_t pb_resizebuffer(pb_Buffer *b, size_t len) { + size_t newsize = PB_BUFFERSIZE; + while (newsize < PB_MAX_SIZET/2 && newsize < len) + newsize += newsize >> 1; + if (newsize > b->size) { + char *newbuff = b->buff == b->init_buff ? NULL : b->buff; + newbuff = (char*)realloc(newbuff, newsize); + if (newbuff == NULL) return 0; + if (b->buff == b->init_buff) + memcpy(newbuff, b->buff, b->size); + b->buff = newbuff; + b->capacity = newsize; + } + return b->capacity; +} + +PB_API void* pb_prepbuffsize(pb_Buffer *b, size_t len) { + if (b->size + len > b->capacity) { + size_t oldsize = b->size; + if (pb_resizebuffer(b, oldsize + len) == 0) + return NULL; + } + return &b->buff[b->size]; +} + +PB_API size_t pb_addslice(pb_Buffer *b, pb_Slice s) { + size_t len = pb_len(s); + void *p = pb_prepbuffsize(b, len); + memcpy(p, s.p, len); + return pb_addsize(b, len); +} + +PB_API size_t pb_addlength(pb_Buffer *b, size_t len) { + char buff[10], *s; + size_t bl, ml; + if ((bl = pb_bufflen(b)) < len) + return 0; + ml = pb_write64(buff, bl - len); + if (pb_prepbuffsize(b, ml) == 0) return 0; + s = b->buff + len; + memmove(s+ml, s, bl - len); + memcpy(s, buff, ml); + pb_addsize(b, ml); + return ml; +} + +PB_API size_t pb_addbytes(pb_Buffer *b, pb_Slice s) { + size_t ret, len = pb_len(s); + if (pb_prepbuffsize(b, len+5) == NULL) return 0; + ret = pb_addvarint32(b, (uint32_t)len); + return ret + pb_addslice(b, s); +} + +PB_API size_t pb_addvarint32(pb_Buffer *b, uint32_t n) { + char *buff = (char*)pb_prepbuffsize(b, 5); + if (buff == NULL) return 0; + return pb_addsize(b, pb_write32(buff, n)); +} + +PB_API size_t pb_addvarint64(pb_Buffer *b, uint64_t n) { + char *buff = (char*)pb_prepbuffsize(b, 10); + if (buff == NULL) return 0; + return pb_addsize(b, pb_write64(buff, n)); +} + +PB_API size_t pb_addfixed32(pb_Buffer *b, uint32_t n) { + char *ch = (char*)pb_prepbuffsize(b, 4); + if (ch == NULL) return 0; + *ch++ = n & 0xFF; n >>= 8; + *ch++ = n & 0xFF; n >>= 8; + *ch++ = n & 0xFF; n >>= 8; + *ch = n & 0xFF; + return pb_addsize(b, 4); +} + +PB_API size_t pb_addfixed64(pb_Buffer *b, uint64_t n) { + char *ch = (char*)pb_prepbuffsize(b, 8); + if (ch == NULL) return 0; + *ch++ = n & 0xFF; n >>= 8; + *ch++ = n & 0xFF; n >>= 8; + *ch++ = n & 0xFF; n >>= 8; + *ch++ = n & 0xFF; n >>= 8; + *ch++ = n & 0xFF; n >>= 8; + *ch++ = n & 0xFF; n >>= 8; + *ch++ = n & 0xFF; n >>= 8; + *ch = n & 0xFF; + return pb_addsize(b, 8); +} + + +/* memory pool */ + +PB_API void pb_initpool(pb_Pool *pool, size_t obj_size) { + memset(pool, 0, sizeof(pb_Pool)); + pool->obj_size = obj_size; + assert(obj_size > sizeof(void*) && obj_size < PB_POOLSIZE/4); +} + +PB_API void pb_freepool(pb_Pool *pool) { + void *page = pool->pages; + while (page) { + void *next = *(void**)((char*)page + PB_POOLSIZE - sizeof(void*)); + xfree(page); + page = next; + } + pb_initpool(pool, pool->obj_size); +} + +PB_API void *pb_poolalloc(pb_Pool *pool) { + void *obj = pool->freed; + if (obj == NULL) { + size_t objsize = pool->obj_size, offset; + void *newpage = malloc(PB_POOLSIZE); + if (newpage == NULL) return NULL; + offset = ((PB_POOLSIZE - sizeof(void*)) / objsize - 1) * objsize; + for (; offset > 0; offset -= objsize) { + void **entry = (void**)((char*)newpage + offset); + *entry = pool->freed, pool->freed = (void*)entry; + } + *(void**)((char*)newpage + PB_POOLSIZE - sizeof(void*)) = pool->pages; + pool->pages = newpage; + return newpage; + } + pool->freed = *(void**)obj; + return obj; +} + +PB_API void pb_poolfree(pb_Pool *pool, void *obj) +{ *(void**)obj = pool->freed, pool->freed = obj; } + + +/* hash table */ + +#define pbT_offset(a, b) ((char*)(a) - (char*)(b)) +#define pbT_index(a, b) ((pb_Entry*)((char*)(a) + (b))) + +PB_API void pb_inittable(pb_Table *t, size_t entrysize) +{ memset(t, 0, sizeof(pb_Table)), t->entry_size = entrysize; } + +PB_API void pb_freetable(pb_Table *t) +{ xfree(t->hash); pb_inittable(t, t->entry_size); } + +static pb_Entry *pbT_hash(pb_Table *t, pb_Key key) { + size_t h = ((size_t)key*2654435761)&(t->size-1); + if (key && h == 0) h = 1; + return pbT_index(t->hash, h*t->entry_size); +} + +static pb_Entry *pbT_newkey(pb_Table *t, pb_Key key) { + pb_Entry *mp, *othern, *next, *f = NULL; + if (t->size == 0 && pb_resizetable(t, t->size*2) == 0) return NULL; + if (key == 0) { + mp = t->hash; + t->has_zero = 1; + } + else if ((mp = pbT_hash(t, key))->key != 0) { + while (t->lastfree > t->entry_size) { + pb_Entry *cur = pbT_index(t->hash, t->lastfree -= t->entry_size); + if (cur->key == 0 && cur->next == 0) { f = cur; break; } + } + if (f == NULL) return pb_resizetable(t, t->size*2) ? + pbT_newkey(t, key) : NULL; + if ((othern = pbT_hash(t, mp->key)) != mp) { + while ((next = pbT_index(othern, othern->next)) != mp) + othern = next; + othern->next = pbT_offset(f, othern); + memcpy(f, mp, t->entry_size); + if (mp->next != 0) f->next += pbT_offset(mp, f), mp->next = 0; + } + else { + if (mp->next != 0) f->next = pbT_offset(mp, f) + mp->next; + else assert(f->next == 0); + mp->next = pbT_offset(f, mp); + mp = f; + } + } + mp->key = key; + if (t->entry_size != sizeof(pb_Entry)) + memset(mp+1, 0, t->entry_size - sizeof(pb_Entry)); + return mp; +} + +PB_API size_t pb_resizetable(pb_Table *t, size_t size) { + pb_Table nt = *t; + size_t i, rawsize = t->size*t->entry_size; + size_t newsize = PB_MIN_HASHTABLE_SIZE; + while (newsize < PB_MAX_SIZET/t->entry_size && newsize < size) + newsize <<= 1; + if (newsize < size) return 0; + nt.size = newsize; + nt.lastfree = nt.entry_size * newsize; + nt.hash = (pb_Entry*)malloc(nt.lastfree); + if (nt.hash == NULL) return 0; + memset(nt.hash, 0, nt.lastfree); + for (i = 0; i < rawsize; i += t->entry_size) { + pb_Entry *olde = (pb_Entry*)((char*)t->hash + i); + pb_Entry *newe = pbT_newkey(&nt, olde->key); + if (nt.entry_size > sizeof(pb_Entry)) + memcpy(newe+1, olde+1, nt.entry_size - sizeof(pb_Entry)); + } + xfree(t->hash); + *t = nt; + return newsize; +} + +PB_API pb_Entry *pb_gettable(pb_Table *t, pb_Key key) { + pb_Entry *entry; + if (t == NULL || t->size == 0) + return NULL; + if (key == 0) + return t->has_zero ? t->hash : NULL; + for (entry = pbT_hash(t, key); + entry->key != key; + entry = pbT_index(entry, entry->next)) + if (entry->next == 0) return NULL; + return entry; +} + +PB_API pb_Entry *pb_settable(pb_Table *t, pb_Key key) { + pb_Entry *entry; + if ((entry = pb_gettable(t, key)) != NULL) + return entry; + return pbT_newkey(t, key); +} + +PB_API int pb_nextentry(pb_Table *t, pb_Entry **pentry) { + size_t i = *pentry ? pbT_offset(*pentry, t->hash) : 0; + size_t size = t->size*t->entry_size; + if (*pentry == NULL && t->has_zero) { + *pentry = t->hash; + return 1; + } + while (i += t->entry_size, i < size) { + pb_Entry *entry = pbT_index(t->hash, i); + if (entry->key != 0) { + *pentry = entry; + return 1; + } + } + *pentry = NULL; + return 0; +} + + +/* name table */ + +static void pbN_init(pb_State *S) +{ memset(&S->nametable, 0, sizeof(pb_NameTable)); } + +static void pbN_free(pb_State *S) { + pb_NameTable *nt = &S->nametable; + size_t i; + for (i = 0; i < nt->size; ++i) { + pb_NameEntry *ne = nt->hash[i]; + while (ne != NULL) { + pb_NameEntry *next = ne->next; + xfree(ne); + ne = next; + } + } + xfree(nt->hash); + pbN_init(S); +} + +static unsigned pbN_calchash(const char *s, size_t len) { + unsigned h = (unsigned)len; + size_t step = (len >> PB_HASHLIMIT) + 1; + for (; len >= step; len -= step) + h ^= ((h<<5) + (h>>2) + (unsigned char)(s[len - 1])); + return h; +} + +static size_t pbN_resize(pb_State *S, size_t size) { + pb_NameTable *nt = &S->nametable; + pb_NameEntry **hash; + size_t i, newsize = PB_MIN_STRTABLE_SIZE; + while (newsize < PB_MAX_SIZET/sizeof(pb_NameEntry*) && newsize < size) + newsize <<= 1; + if (newsize < size) return 0; + hash = (pb_NameEntry**)malloc(newsize * sizeof(pb_NameEntry*)); + if (hash == NULL) return 0; + memset(hash, 0, newsize * sizeof(pb_NameEntry*)); + for (i = 0; i < nt->size; ++i) { + pb_NameEntry *entry = nt->hash[i]; + while (entry != NULL) { + pb_NameEntry *next = entry->next; + pb_NameEntry **newh = &hash[entry->hash & (newsize - 1)]; + entry->next = *newh, *newh = entry; + entry = next; + } + } + xfree(nt->hash); + nt->hash = hash; + nt->size = newsize; + return newsize; +} + +static pb_NameEntry *pbN_newname(pb_State *S, const char *name, size_t len, unsigned hash) { + pb_NameTable *nt = &S->nametable; + pb_NameEntry **list, *newobj; + if (nt->count >= nt->size && !pbN_resize(S, nt->size * 2)) return NULL; + list = &nt->hash[hash & (nt->size - 1)]; + newobj = (pb_NameEntry*)malloc(sizeof(pb_NameEntry) + len + 1); + if (newobj == NULL) return NULL; + newobj->next = *list; + newobj->length = (unsigned)len; + newobj->refcount = 1; + newobj->hash = hash; + memcpy(newobj+1, name, len); + ((char*)(newobj+1))[len] = '\0'; + *list = newobj; + ++nt->count; + return newobj; +} + +static void pbN_delname(pb_State *S, pb_NameEntry *name) { + pb_NameTable *nt = &S->nametable; + pb_NameEntry **list = &nt->hash[name->hash & (nt->size - 1)]; + while (*list != NULL) { + if (*list != name) + list = &(*list)->next; + else { + *list = (*list)->next; + xfree(name); + --nt->count; + break; + } + } +} + +static pb_NameEntry *pbN_getname(pb_State *S, const char *name, size_t len, unsigned hash) { + pb_NameTable *nt = &S->nametable; + if (nt->hash) { + pb_NameEntry *entry = nt->hash[hash & (nt->size - 1)]; + for (; entry != NULL; entry = entry->next) + if (entry->hash == hash && entry->length == len + && memcmp(name, entry + 1, len) == 0) + return entry; + } + return NULL; +} + +PB_API pb_Name *pb_newname(pb_State *S, pb_Slice s) { + if (s.p != NULL) { + size_t size = pb_len(s); + const char *name = s.p; + unsigned hash = pbN_calchash(name, size); + pb_NameEntry *entry = pbN_getname(S, name, size, hash); + if (entry) return pb_usename((pb_Name*)(entry + 1)); + entry = pbN_newname(S, name, size, hash); + return entry ? (pb_Name*)(entry + 1) : NULL; + } + return NULL; +} + +PB_API pb_Name *pb_usename(pb_Name *name) { + if (name != NULL) + ++((pb_NameEntry*)name-1)->refcount; + return name; +} + +PB_API void pb_delname(pb_State *S, pb_Name *name) { + pb_NameEntry *ne = (pb_NameEntry*)name - 1; + if (name != NULL) { + if (ne->refcount <= 1) + { pbN_delname(S, ne); return; } + --ne->refcount; + } +} + +PB_API pb_Name *pb_name(pb_State *S, const char *name) { + if (name != NULL) { + size_t size = strlen(name); + unsigned hash = pbN_calchash(name, size); + pb_NameEntry *entry = pbN_getname(S, name, size, hash); + return entry ? (pb_Name*)(entry + 1) : NULL; + } + return NULL; +} + + +/* state */ + +typedef struct pb_TypeEntry { pb_Entry entry; pb_Type *value; } pb_TypeEntry; +typedef struct pb_FieldEntry { pb_Entry entry; pb_Field *value; } pb_FieldEntry; + +typedef struct pb_OneofEntry { + pb_Entry entry; + pb_Name *name; + unsigned index; +} pb_OneofEntry; + +PB_API void pb_init(pb_State *S) { + memset(S, 0, sizeof(pb_State)); + S->types.entry_size = sizeof(pb_TypeEntry); + pb_initpool(&S->typepool, sizeof(pb_Type)); + pb_initpool(&S->fieldpool, sizeof(pb_Field)); +} + +PB_API void pb_free(pb_State *S) { + if (S != NULL) { + pb_TypeEntry *te = NULL; + while (pb_nextentry(&S->types, (pb_Entry**)&te)) + if (te->value != NULL) pb_deltype(S, te->value); + pb_freetable(&S->types); + pb_freepool(&S->typepool); + pb_freepool(&S->fieldpool); + pbN_free(S); + } +} + +PB_API pb_Type *pb_type(pb_State *S, pb_Name *tname) { + pb_TypeEntry *te = NULL; + if (S != NULL && tname != NULL) + te = (pb_TypeEntry*)pb_gettable(&S->types, (pb_Key)tname); + return te ? te->value : NULL; +} + +PB_API pb_Field *pb_fname(pb_Type *t, pb_Name *name) { + pb_FieldEntry *fe = NULL; + if (t != NULL && name != NULL) + fe = (pb_FieldEntry*)pb_gettable(&t->field_names, (pb_Key)name); + return fe ? fe->value : NULL; +} + +PB_API pb_Field *pb_field(pb_Type *t, int32_t number) { + pb_FieldEntry *fe = NULL; + if (t != NULL) fe = (pb_FieldEntry*)pb_gettable(&t->field_tags, number); + return fe ? fe->value : NULL; +} + +PB_API int pb_nexttype(pb_State *S, pb_Type **ptype) { + pb_TypeEntry *e = NULL; + if (S != NULL) { + if (*ptype != NULL) + e = (pb_TypeEntry*)pb_gettable(&S->types, (pb_Key)(*ptype)->name); + while (pb_nextentry(&S->types, (pb_Entry**)&e)) + if ((*ptype = e->value) != NULL) + return 1; + } + *ptype = NULL; + return 0; +} + +PB_API int pb_nextfield(pb_Type *t, pb_Field **pfield) { + pb_FieldEntry *e = NULL; + if (t != NULL) { + if (*pfield != NULL) + e = (pb_FieldEntry*)pb_gettable(&t->field_tags, (*pfield)->number); + while (pb_nextentry(&t->field_tags, (pb_Entry**)&e)) + if ((*pfield = e->value) != NULL) + return 1; + } + *pfield = NULL; + return 0; +} + + +/* new type/field */ + +static const char *pbT_basename(const char *tname) { + const char *end = tname + strlen(tname); + while (tname < end && *--end != '.') + ; + return *end != '.' ? end : end + 1; +} + +static void pbT_inittype(pb_Type *t) { + memset(t, 0, sizeof(pb_Type)); + pb_inittable(&t->field_names, sizeof(pb_FieldEntry)); + pb_inittable(&t->field_tags, sizeof(pb_FieldEntry)); + pb_inittable(&t->oneof_index, sizeof(pb_OneofEntry)); +} + +static void pbT_freefield(pb_State *S, pb_Field *f) { + pb_delname(S, f->default_value); + pb_delname(S, f->name); + pb_poolfree(&S->fieldpool, f); +} + +PB_API pb_Type *pb_newtype(pb_State *S, pb_Name *tname) { + if (tname != NULL) { + pb_TypeEntry *te = (pb_TypeEntry*)pb_settable( + &S->types, (pb_Key)tname); + pb_Type *t; + if (te == NULL) return NULL; + if ((t = te->value) != NULL) return t; + if (!(t = (pb_Type*)pb_poolalloc(&S->typepool))) return NULL; + pbT_inittype(t); + t->name = tname; + t->basename = pbT_basename((const char*)tname); + return te->value = t; + } + return NULL; +} + +PB_API void pb_deltype(pb_State *S, pb_Type *t) { + pb_FieldEntry *nf = NULL; + pb_OneofEntry *ne = NULL; + while (pb_nextentry(&t->field_names, (pb_Entry**)&nf)) { + if (nf->value != NULL) { + pb_FieldEntry *of = (pb_FieldEntry*)pb_gettable( + &t->field_tags, nf->value->number); + if (of && of->value == nf->value) + of->entry.key = 0, of->value = NULL; + pbT_freefield(S, nf->value); + } + } + while (pb_nextentry(&t->field_tags, (pb_Entry**)&nf)) + if (nf->value != NULL) pbT_freefield(S, nf->value); + while (pb_nextentry(&t->oneof_index, (pb_Entry**)&ne)) + pb_delname(S, ne->name); + pb_freetable(&t->field_tags); + pb_freetable(&t->field_names); + pb_freetable(&t->oneof_index); + t->field_count = 0; + /*pb_delname(S, t->name); */ + /*pb_poolfree(&S->typepool, t); */ +} + +PB_API pb_Field *pb_newfield(pb_State *S, pb_Type *t, pb_Name *fname, int32_t number) { + if (fname != NULL) { + pb_FieldEntry *nf = (pb_FieldEntry*)pb_settable( + &t->field_names, (pb_Key)fname); + pb_FieldEntry *tf = (pb_FieldEntry*)pb_settable( + &t->field_tags, number); + pb_Field *f; + if (nf == NULL || tf == NULL) return NULL; + if ((f = nf->value) != NULL && tf->value == f) { + pb_delname(S, f->default_value); + f->default_value = NULL; + return f; + } + if (!(f = (pb_Field*)pb_poolalloc(&S->typepool))) return NULL; + memset(f, 0, sizeof(pb_Field)); + f->name = fname; + f->type = t; + f->number = number; + if (nf->value && pb_field(t, nf->value->number) != nf->value) + pbT_freefield(S, nf->value), --t->field_count; + if (tf->value && pb_fname(t, tf->value->name) != tf->value) + pbT_freefield(S, tf->value), --t->field_count; + ++t->field_count; + return nf->value = tf->value = f; + } + return NULL; +} + +PB_API void pb_delfield(pb_State *S, pb_Type *t, pb_Field *f) { + pb_FieldEntry *nf = (pb_FieldEntry*)pb_gettable(&t->field_names, + (pb_Key)f->name); + pb_FieldEntry *tf = (pb_FieldEntry*)pb_gettable(&t->field_tags, + (pb_Key)f->number); + int count = 0; + if (nf && nf->value == f) nf->entry.key = 0, nf->value = NULL, ++count; + if (tf && tf->value == f) tf->entry.key = 0, nf->value = NULL, ++count; + if (count) pbT_freefield(S, f), --t->field_count; +} + + +/* .pb proto loader */ + +#include + +typedef struct pb_Loader pb_Loader; +typedef struct pbL_FieldInfo pbL_FieldInfo; +typedef struct pbL_EnumValueInfo pbL_EnumValueInfo; +typedef struct pbL_EnumInfo pbL_EnumInfo; +typedef struct pbL_TypeInfo pbL_TypeInfo; +typedef struct pbL_FileInfo pbL_FileInfo; + +#define pbL_rawh(A) ((size_t*)(A) - 2) +#define pbL_size(A) ((A) ? pbL_rawh(A)[0] : 0) +#define pbL_count(A) ((A) ? pbL_rawh(A)[1] : 0) +#define pbL_add(A) (pbL_grow(L, (void**)&(A), sizeof(*(A))), &(A)[pbL_rawh(A)[1]++]) +#define pbL_delete(A) ((A) ? (void)xfree(pbL_rawh(A)) : (void)0) + +static void pbL_DescriptorProto (pb_Loader *L, pbL_TypeInfo *info); + +struct pb_Loader { + jmp_buf jbuf; + pb_Slice s; + pb_Buffer b; + int is_proto3; +}; + +/* parsers */ + +struct pbL_EnumValueInfo { + pb_Slice name; + int32_t number; +}; + +struct pbL_EnumInfo { + pb_Slice name; + pbL_EnumValueInfo *value; +}; + +struct pbL_FieldInfo { + pb_Slice name; + pb_Slice type_name; + pb_Slice extendee; + pb_Slice default_value; + int32_t number; + int32_t label; + int32_t type; + int32_t oneof_index; + int32_t packed; +}; + +struct pbL_TypeInfo { + pb_Slice name; + int32_t is_map; + pbL_FieldInfo *field; + pbL_FieldInfo *extension; + pbL_EnumInfo *enum_type; + pbL_TypeInfo *nested_type; + pb_Slice *oneof_decl; +}; + +struct pbL_FileInfo { + pb_Slice package; + pb_Slice syntax; + pbL_EnumInfo *enum_type; + pbL_TypeInfo *message_type; + pbL_FieldInfo *extension; +}; + +static void pbL_grow(pb_Loader *L, void **pp, size_t obj_size) { + enum { SIZE, COUNT, FIELDS }; + size_t *h = *pp ? pbL_rawh(*pp) : NULL; + if (h == NULL || h[SIZE] <= h[COUNT]) { + size_t newsize = (h ? h[SIZE] : 1) * 2; + size_t used = (h ? h[COUNT] : 0); + size_t *nh = (size_t*)realloc(h, sizeof(size_t)*2 + newsize*obj_size); + if (nh == NULL) longjmp(L->jbuf, PB_ENOMEM); + nh[SIZE] = newsize; + nh[COUNT] = used; + *pp = nh + FIELDS; + memset((char*)*pp + used*obj_size, 0, (newsize - used)*obj_size); + } +} + +static void pbL_readbytes(pb_Loader *L, pb_Slice *pv) { + if (pb_readbytes(&L->s, pv) == 0) + longjmp(L->jbuf, 1); +} + +static void pbL_readint32(pb_Loader *L, int32_t *pv) { + uint32_t v; + if (pb_readvarint32(&L->s, &v) == 0) + longjmp(L->jbuf, 1); + *pv = (int32_t)v; +} + +static void pbL_beginmsg(pb_Loader *L, pb_Slice *pv) { + pb_Slice v; + pbL_readbytes(L, &v); + *pv = L->s, L->s = v; +} + +static void pbL_endmsg(pb_Loader *L, pb_Slice *pv) { + L->s = *pv; +} + +static void pbL_FieldOptions(pb_Loader *L, pbL_FieldInfo *info) { + pb_Slice s; + uint32_t tag; + pbL_beginmsg(L, &s); + while (pb_readvarint32(&L->s, &tag)) { + switch (tag) { + case pb_pair(2, PB_TVARINT): /* bool packed */ + pbL_readint32(L, &info->packed); break; + default: pb_skipvalue(&L->s, tag); + } + } + pbL_endmsg(L, &s); +} + +static void pbL_FieldDescriptorProto(pb_Loader *L, pbL_FieldInfo *info) { + pb_Slice s; + uint32_t tag; + pbL_beginmsg(L, &s); + info->packed = -1; + while (pb_readvarint32(&L->s, &tag)) { + switch (tag) { + case pb_pair(1, PB_TBYTES): /* string name */ + pbL_readbytes(L, &info->name); break; + case pb_pair(3, PB_TVARINT): /* int32 number */ + pbL_readint32(L, &info->number); break; + case pb_pair(4, PB_TVARINT): /* Label label */ + pbL_readint32(L, &info->label); break; + case pb_pair(5, PB_TVARINT): /* Type type */ + pbL_readint32(L, &info->type); break; + case pb_pair(6, PB_TBYTES): /* string type_name */ + pbL_readbytes(L, &info->type_name); break; + case pb_pair(2, PB_TBYTES): /* string extendee */ + pbL_readbytes(L, &info->extendee); break; + case pb_pair(7, PB_TBYTES): /* string default_value */ + pbL_readbytes(L, &info->default_value); break; + case pb_pair(8, PB_TBYTES): /* FieldOptions options */ + pbL_FieldOptions(L, info); break; + case pb_pair(9, PB_TVARINT): /* int32 oneof_index */ + pbL_readint32(L, &info->oneof_index); + ++info->oneof_index; break; + default: pb_skipvalue(&L->s, tag); + } + } + pbL_endmsg(L, &s); +} + +static void pbL_EnumValueDescriptorProto(pb_Loader *L, pbL_EnumValueInfo *info) { + pb_Slice s; + uint32_t tag; + pbL_beginmsg(L, &s); + while (pb_readvarint32(&L->s, &tag)) { + switch (tag) { + case pb_pair(1, PB_TBYTES): /* string name */ + pbL_readbytes(L, &info->name); break; + case pb_pair(2, PB_TVARINT): /* int32 number */ + pbL_readint32(L, &info->number); break; + default: pb_skipvalue(&L->s, tag); + } + } + pbL_endmsg(L, &s); +} + +static void pbL_EnumDescriptorProto(pb_Loader *L, pbL_EnumInfo *info) { + pb_Slice s; + uint32_t tag; + pbL_beginmsg(L, &s); + while (pb_readvarint32(&L->s, &tag)) { + switch (tag) { + case pb_pair(1, PB_TBYTES): /* string name */ + pbL_readbytes(L, &info->name); break; + case pb_pair(2, PB_TBYTES): /* EnumValueDescriptorProto value */ + pbL_EnumValueDescriptorProto(L, pbL_add(info->value)); break; + default: pb_skipvalue(&L->s, tag); + } + } + pbL_endmsg(L, &s); +} + +static void pbL_MessageOptions(pb_Loader *L, pbL_TypeInfo *info) { + pb_Slice s; + uint32_t tag; + pbL_beginmsg(L, &s); + while (pb_readvarint32(&L->s, &tag)) { + switch (tag) { + case pb_pair(7, PB_TVARINT): /* bool map_entry */ + pbL_readint32(L, &info->is_map); break; + default: pb_skipvalue(&L->s, tag); + } + } + pbL_endmsg(L, &s); +} + +static void pbL_OneofDescriptorProto(pb_Loader *L, pbL_TypeInfo *info) { + pb_Slice s; + uint32_t tag; + pbL_beginmsg(L, &s); + while (pb_readvarint32(&L->s, &tag)) { + switch (tag) { + case pb_pair(1, PB_TBYTES): /* string name */ + pbL_readbytes(L, pbL_add(info->oneof_decl)); break; + default: pb_skipvalue(&L->s, tag); + } + } + pbL_endmsg(L, &s); +} + +static void pbL_DescriptorProto(pb_Loader *L, pbL_TypeInfo *info) { + pb_Slice s; + uint32_t tag; + pbL_beginmsg(L, &s); + while (pb_readvarint32(&L->s, &tag)) { + switch (tag) { + case pb_pair(1, PB_TBYTES): /* string name */ + pbL_readbytes(L, &info->name); break; + case pb_pair(2, PB_TBYTES): /* FieldDescriptorProto field */ + pbL_FieldDescriptorProto(L, pbL_add(info->field)); break; + case pb_pair(6, PB_TBYTES): /* FieldDescriptorProto extension */ + pbL_FieldDescriptorProto(L, pbL_add(info->extension)); break; + case pb_pair(3, PB_TBYTES): /* DescriptorProto nested_type */ + pbL_DescriptorProto(L, pbL_add(info->nested_type)); break; + case pb_pair(4, PB_TBYTES): /* EnumDescriptorProto enum_type */ + pbL_EnumDescriptorProto(L, pbL_add(info->enum_type)); break; + case pb_pair(8, PB_TBYTES): /* OneofDescriptorProto oneof_decl */ + pbL_OneofDescriptorProto(L, info); break; + case pb_pair(7, PB_TBYTES): /* MessageOptions options */ + pbL_MessageOptions(L, info); break; + default: pb_skipvalue(&L->s, tag); + } + } + pbL_endmsg(L, &s); +} + +static void pbL_FileDescriptorProto(pb_Loader *L, pbL_FileInfo *info) { + pb_Slice s; + uint32_t tag; + pbL_beginmsg(L, &s); + while (pb_readvarint32(&L->s, &tag)) { + switch (tag) { + case pb_pair(2, PB_TBYTES): /* string package */ + pbL_readbytes(L, &info->package); break; + case pb_pair(4, PB_TBYTES): /* DescriptorProto message_type */ + pbL_DescriptorProto(L, pbL_add(info->message_type)); break; + case pb_pair(5, PB_TBYTES): /* EnumDescriptorProto enum_type */ + pbL_EnumDescriptorProto(L, pbL_add(info->enum_type)); break; + case pb_pair(7, PB_TBYTES): /* FieldDescriptorProto extension */ + pbL_FieldDescriptorProto(L, pbL_add(info->extension)); break; + case pb_pair(12, PB_TBYTES): /* string syntax */ + pbL_readbytes(L, &info->syntax); break; + default: pb_skipvalue(&L->s, tag); + } + } + pbL_endmsg(L, &s); +} + +static void pbL_FileDescriptorSet(pb_Loader *L, pbL_FileInfo **pinfo) { + uint32_t tag; + while (pb_readvarint32(&L->s, &tag)) { + switch (tag) { + case pb_pair(1, PB_TBYTES): /* FileDescriptorProto file */ + pbL_FileDescriptorProto(L, pbL_add(*pinfo)); break; + default: pb_skipvalue(&L->s, tag); + } + } +} + +/* loader */ + +static void pbL_delTypeInfo(pbL_TypeInfo *info) { + size_t i, count; + for (i = 0, count = pbL_count(info->nested_type); i < count; ++i) + pbL_delTypeInfo(&info->nested_type[i]); + for (i = 0, count = pbL_count(info->enum_type); i < count; ++i) + pbL_delete(info->enum_type[i].value); + pbL_delete(info->nested_type); + pbL_delete(info->enum_type); + pbL_delete(info->field); + pbL_delete(info->extension); +} + +static void pbL_delFileInfo(pbL_FileInfo *files) { + size_t i, count, j, jcount; + for (i = 0, count = pbL_count(files); i < count; ++i) { + for (j = 0, jcount = pbL_count(files[i].message_type); j < jcount; ++j) + pbL_delTypeInfo(&files[i].message_type[j]); + for (j = 0, jcount = pbL_count(files[i].enum_type); j < jcount; ++j) + pbL_delete(files[i].enum_type[j].value); + pbL_delete(files[i].message_type); + pbL_delete(files[i].enum_type); + pbL_delete(files[i].extension); + } + pbL_delete(files); +} + +static pb_Slice pbL_prefixname(pb_Buffer *b, pb_Slice s, size_t *ps) { + *ps = b->size; + pb_addchar(b, '.'); + pb_addslice(b, s); + return pb_result(b); +} + +static void pbL_loadEnum(pb_State *S, pbL_EnumInfo *info, pb_Loader *L) { + size_t i, count, curr; + pb_Type *t = pb_newtype(S, pb_newname(S, + pbL_prefixname(&L->b, info->name, &curr))); + t->is_enum = 1; + for (i = 0, count = pbL_count(info->value); i < count; ++i) { + pbL_EnumValueInfo *ev = &info->value[i]; + pb_newfield(S, t, pb_newname(S, ev->name), ev->number); + } + L->b.size = curr; +} + +static void pbL_loadField(pb_State *S, pbL_FieldInfo *info, pb_Loader *L, pb_Type *t) { + if (t != NULL || pb_len(info->extendee) != 0) { + pb_Type *ft = pb_newtype(S, pb_newname(S, info->type_name)); + pb_Field *f; + if (!ft && (info->type == PB_Tmessage || info->type == PB_Tenum)) + return; + if (t == NULL && !(t = pb_newtype(S, pb_newname(S, info->extendee)))) + return; + if (!(f = pb_newfield(S, t, pb_newname(S, info->name), info->number))) + return; + f->default_value = pb_newname(S, info->default_value); + f->type = ft; + f->type_id = info->type; + f->repeated = info->label == 3; /* repeated */ + f->packed = info->packed >= 0 ? info->packed : L->is_proto3; + if (f->type_id >= 9 && f->type_id <= 12) f->packed = 0; + f->scalar = f->type == NULL; + if (info->oneof_index != 0) { + pb_OneofEntry *e = (pb_OneofEntry*)pb_gettable(&t->oneof_index, + info->oneof_index), *fe; + if (e != NULL) { + fe = (pb_OneofEntry*)pb_settable(&t->oneof_index, (pb_Key)f); + fe->name = pb_usename(e->name); + fe->index = e->index; + } + } + } +} + +static void pbL_loadType(pb_State *S, pbL_TypeInfo *info, pb_Loader *L) { + size_t i, count, curr; + pb_Type *t = pb_newtype(S, pb_newname(S, + pbL_prefixname(&L->b, info->name, &curr))); + t->is_map = info->is_map; + for (i = 0, count = pbL_count(info->oneof_decl); i < count; ++i) { + pb_OneofEntry *e = (pb_OneofEntry*)pb_settable(&t->oneof_index, i+1); + e->name = pb_newname(S, info->oneof_decl[i]); + e->index = i+1; + } + for (i = 0, count = pbL_count(info->field); i < count; ++i) + pbL_loadField(S, &info->field[i], L, t); + for (i = 0, count = pbL_count(info->extension); i < count; ++i) + pbL_loadField(S, &info->extension[i], L, NULL); + for (i = 0, count = pbL_count(info->enum_type); i < count; ++i) + pbL_loadEnum(S, &info->enum_type[i], L); + for (i = 0, count = pbL_count(info->nested_type); i < count; ++i) + pbL_loadType(S, &info->nested_type[i], L); + L->b.size = curr; +} + +static void pbL_loadFile(pb_State *S, pbL_FileInfo *info, pb_Loader *L) { + size_t i, count, j, jcount, curr = 0; + for (i = 0, count = pbL_count(info); i < count; ++i) { + if (info[i].package.p) pbL_prefixname(&L->b, info[i].package, &curr); + if (pb_newname(S, info[i].syntax) == pb_newname(S, pb_slice("proto3"))) + L->is_proto3 = 1; + for (j = 0, jcount = pbL_count(info[i].enum_type); j < jcount; ++j) + pbL_loadEnum(S, &info[i].enum_type[j], L); + for (j = 0, jcount = pbL_count(info[i].message_type); j < jcount; ++j) + pbL_loadType(S, &info[i].message_type[j], L); + for (j = 0, jcount = pbL_count(info[i].extension); j < jcount; ++j) + pbL_loadField(S, &info[i].extension[j], L, NULL); + L->b.size = curr; + } +} + +PB_API int pb_load(pb_State *S, pb_Slice *s) { + pbL_FileInfo *files = NULL; + pb_Loader L; + int ret; + if ((ret = setjmp(L.jbuf)) < 0) + return PB_ERROR; + else if (ret == 0) { + L.s = *s; + L.is_proto3 = 0; + pb_initbuffer(&L.b); + pbL_FileDescriptorSet(&L, &files); + pbL_loadFile(S, files, &L); + } + pbL_delFileInfo(files); + pb_resetbuffer(&L.b); + s->p = L.s.p; + return ret; +} + + +PB_NS_END + +#endif /* PB_IMPLEMENTATION */ + +/* cc: flags+='-shared -DPB_IMPLEMENTATION -xc' output='pb.so' */ diff --git a/lualib/msgpack.lua b/lualib/msgpack.lua new file mode 100644 index 00000000..1cccfeb4 --- /dev/null +++ b/lualib/msgpack.lua @@ -0,0 +1,28 @@ +local lmsgpack = require "lmsgpack.safe" +local lmsgpack_encode = lmsgpack.pack +local lmsgpack_decode = lmsgpack.unpack + +local msgpack = {} + +-- 序列化 +function msgpack.encode (...) + return lmsgpack_encode(...) +end + +-- 反序列化 +function msgpack.decode (...) + return lmsgpack_decode(...) +end + +-- 序列化 +function msgpack.pack (...) + return lmsgpack_encode(...) +end + +-- 反序列化 +function msgpack.unpack (...) + return lmsgpack_decode(...) +end + + +return msgpack diff --git a/lualib/protobuf.lua b/lualib/protobuf.lua new file mode 100644 index 00000000..772a77f8 --- /dev/null +++ b/lualib/protobuf.lua @@ -0,0 +1,50 @@ +local lprotobuf = require "lprotobuf" + + +local lprotobuf_tohex = lprotobuf.tohex + +local lprotobuf_clear = lprotobuf.clear + +local lprotobuf_load = lprotobuf.load +local lprotobuf_loadfile = lprotobuf.loadfile + +local lprotobuf_encode = lprotobuf.encode +local lprotobuf_decode = lprotobuf.decode + + +local pb = {} + +-- 转化为16进制可读字符串 +function pb.tohex (pb_string) + return lprotobuf_tohex(pb_string) +end + +-- 从字符串中读取 +function pb.load (pb_cp_string) + return lprotobuf_load(pb_cp_string) +end + +-- 从文件中读取 +function pb.loadfile (filename) + return lprotobuf_loadfile(filename) +end + +-- 序列化 +function pb.encode (pb_registey, table) + return lprotobuf_encode(pb_registey, table) +end + +-- 反序列化 +function pb.decode (pb_registey, pb_string) + return lprotobuf_decode(pb_registey, pb_string) +end + +-- 清理 +-- When you passed A not exists message struct will get Segmentation fault. +function pb.clear (...) + return lprotobuf_clear(...) +end + +-- require ("logging"):new():DEBUG(lprotobuf) + +return pb diff --git a/script/test_msgpack.lua b/script/test_msgpack.lua new file mode 100644 index 00000000..1c47a0b0 --- /dev/null +++ b/script/test_msgpack.lua @@ -0,0 +1,8 @@ +local Log = require ("logging"):new() + +local msgpack = require "msgpack" + +local msg = msgpack.encode({1, 2, 3, 4, name = "CandyMi"}) +Log:DEBUG("序列化完成:"..msg) + +Log:DEBUG(msgpack.decode(msg)) diff --git a/script/test_protobuf.lua b/script/test_protobuf.lua new file mode 100644 index 00000000..23eb2c43 --- /dev/null +++ b/script/test_protobuf.lua @@ -0,0 +1,23 @@ +local Log = require ("logging"):new() + +local pb = require "protobuf" + +Log:DEBUG(pb.loadfile("Person.lua")) + +local pb_string = pb.encode("Person", { + name = "CandyMi", + age = 2^32 - 1, + hand = { + left = "左手", + right = "右手", + }, + foot = { + left = "左脚", + right = "右脚", + } +}) +Log:DEBUG(pb.tohex(pb_string)) + +Log:DEBUG(pb.decode("Person", pb_string)) + +Log:DEBUG(pb.clear("Person")) From b2c0f002e365574317f7db000234c4aa1ba01e4f Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 18 Jul 2019 23:17:35 +0800 Subject: [PATCH 228/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0build.sh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 22082c62..03054f81 100755 --- a/build.sh +++ b/build.sh @@ -11,7 +11,7 @@ cd ${current}/build/libev && make && make install cd ${current}/build/lua && - make all MYCFLAGS=-fPIC MYCFLAGS+=-DLUA_USE_DLOPEN SYSLIBS=-ldl && + make all MYCFLAGS=-fPIC MYCFLAGS+=-DLUA_USE_DLOPEN SYSLIBS="-ldl -lreadline" && cp lua.h luaconf.h lualib.h lauxlib.h /usr/local/include && cp liblua.* /usr/local/lib cd ${current} && rm -rf build From 63b48ec2b5446a353b1899f3a6fcd3fb84f59fb7 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 19 Jul 2019 13:14:33 +0800 Subject: [PATCH 229/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0build.sh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 03054f81..793e2a15 100755 --- a/build.sh +++ b/build.sh @@ -11,7 +11,7 @@ cd ${current}/build/libev && make && make install cd ${current}/build/lua && - make all MYCFLAGS=-fPIC MYCFLAGS+=-DLUA_USE_DLOPEN SYSLIBS="-ldl -lreadline" && + make all MYCFLAGS=-fPIC MYCFLAGS+=-DLUA_USE_POSIX MYCFLAGS+=-DLUA_USE_DLOPEN MYLIBS="-ldl -lreadline" && cp lua.h luaconf.h lualib.h lauxlib.h /usr/local/include && cp liblua.* /usr/local/lib cd ${current} && rm -rf build From c78e68dfda8dfcbf95eea1872d4f2e1820dfa1b8 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 19 Jul 2019 13:47:35 +0800 Subject: [PATCH 230/956] =?UTF-8?q?=E6=9B=B4=E6=94=B9len=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E4=B8=BAsocklen=5Ft?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 0cf81348..1764a4c8 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -199,7 +199,8 @@ IO_CONNECT(CORE_P_ core_io *io, int revents){ if (revents & EV_WRITE){ lua_State *co = (lua_State *)core_get_watcher_userdata(io); if (lua_status(co) == LUA_YIELD || lua_status(co) == LUA_OK){ - int CONNECTED = 0, err = 0, len = sizeof(socklen_t); + int CONNECTED = 0, err = 0; + socklen_t len = sizeof(socklen_t); if(getsockopt(io->fd, SOL_SOCKET, SO_ERROR, &err, (socklen_t*)&len) == 0 && err == 0) CONNECTED = 1; lua_pushboolean(co, CONNECTED); int status = lua_resume(co, NULL, 1); From e83bad755d523575704fb95d6a6b8464f2f10b0e Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 19 Jul 2019 16:04:40 +0800 Subject: [PATCH 231/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lpbc/lpb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/luaclib/src/lpbc/lpb.c b/luaclib/src/lpbc/lpb.c index 42c9fa1e..dc586c71 100644 --- a/luaclib/src/lpbc/lpb.c +++ b/luaclib/src/lpbc/lpb.c @@ -1359,7 +1359,7 @@ static void lpbD_field(lua_State *L, pb_SliceExt *s, pb_Field *f, uint32_t tag) static void lpbD_map(lua_State *L, pb_SliceExt *s, pb_Field *f) { pb_SliceExt p; int mask = 0, top = lua_gettop(L) + 1; - uint32_t tag; + uint32_t tag = 0; lpb_fetchtable(L, f); lpb_readbytes(L, s, &p); if (f->type == NULL) return; @@ -1397,7 +1397,7 @@ static void lpbD_repeated(lua_State *L, pb_SliceExt *s, pb_Field *f, uint32_t ta } static int lpb_decode(lua_State *L, pb_SliceExt *s, pb_Type *t) { - uint32_t tag; + uint32_t tag = 0; while (pb_readvarint32(&s->base, &tag)) { pb_Field *f = pb_field(t, pb_gettag(tag)); if (f == NULL) From a5141589bfd721cc891791ae51ef700f74117b43 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 19 Jul 2019 16:06:35 +0800 Subject: [PATCH 232/956] =?UTF-8?q?=E6=B8=85=E9=99=A4=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lpbc/lpb.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/luaclib/src/lpbc/lpb.h b/luaclib/src/lpbc/lpb.h index bd7ebbb0..4cf41007 100644 --- a/luaclib/src/lpbc/lpb.h +++ b/luaclib/src/lpbc/lpb.h @@ -502,7 +502,7 @@ PB_API size_t pb_readbytes(pb_Slice *s, pb_Slice *pv) { PB_API size_t pb_readgroup(pb_Slice *s, uint32_t tag, pb_Slice *pv) { const char *p = s->p; - uint32_t newtag; + uint32_t newtag = 0; size_t count; assert(pb_gettype(tag) == PB_TGSTART); while ((count = pb_readvarint32(s, &newtag)) != 0) { @@ -1338,7 +1338,7 @@ static void pbL_endmsg(pb_Loader *L, pb_Slice *pv) { static void pbL_FieldOptions(pb_Loader *L, pbL_FieldInfo *info) { pb_Slice s; - uint32_t tag; + uint32_t tag = 0; pbL_beginmsg(L, &s); while (pb_readvarint32(&L->s, &tag)) { switch (tag) { @@ -1352,7 +1352,7 @@ static void pbL_FieldOptions(pb_Loader *L, pbL_FieldInfo *info) { static void pbL_FieldDescriptorProto(pb_Loader *L, pbL_FieldInfo *info) { pb_Slice s; - uint32_t tag; + uint32_t tag = 0; pbL_beginmsg(L, &s); info->packed = -1; while (pb_readvarint32(&L->s, &tag)) { @@ -1384,7 +1384,7 @@ static void pbL_FieldDescriptorProto(pb_Loader *L, pbL_FieldInfo *info) { static void pbL_EnumValueDescriptorProto(pb_Loader *L, pbL_EnumValueInfo *info) { pb_Slice s; - uint32_t tag; + uint32_t tag = 0; pbL_beginmsg(L, &s); while (pb_readvarint32(&L->s, &tag)) { switch (tag) { @@ -1400,7 +1400,7 @@ static void pbL_EnumValueDescriptorProto(pb_Loader *L, pbL_EnumValueInfo *info) static void pbL_EnumDescriptorProto(pb_Loader *L, pbL_EnumInfo *info) { pb_Slice s; - uint32_t tag; + uint32_t tag = 0; pbL_beginmsg(L, &s); while (pb_readvarint32(&L->s, &tag)) { switch (tag) { @@ -1416,7 +1416,7 @@ static void pbL_EnumDescriptorProto(pb_Loader *L, pbL_EnumInfo *info) { static void pbL_MessageOptions(pb_Loader *L, pbL_TypeInfo *info) { pb_Slice s; - uint32_t tag; + uint32_t tag = 0; pbL_beginmsg(L, &s); while (pb_readvarint32(&L->s, &tag)) { switch (tag) { @@ -1430,7 +1430,7 @@ static void pbL_MessageOptions(pb_Loader *L, pbL_TypeInfo *info) { static void pbL_OneofDescriptorProto(pb_Loader *L, pbL_TypeInfo *info) { pb_Slice s; - uint32_t tag; + uint32_t tag = 0; pbL_beginmsg(L, &s); while (pb_readvarint32(&L->s, &tag)) { switch (tag) { @@ -1444,7 +1444,7 @@ static void pbL_OneofDescriptorProto(pb_Loader *L, pbL_TypeInfo *info) { static void pbL_DescriptorProto(pb_Loader *L, pbL_TypeInfo *info) { pb_Slice s; - uint32_t tag; + uint32_t tag = 0; pbL_beginmsg(L, &s); while (pb_readvarint32(&L->s, &tag)) { switch (tag) { @@ -1470,7 +1470,7 @@ static void pbL_DescriptorProto(pb_Loader *L, pbL_TypeInfo *info) { static void pbL_FileDescriptorProto(pb_Loader *L, pbL_FileInfo *info) { pb_Slice s; - uint32_t tag; + uint32_t tag = 0; pbL_beginmsg(L, &s); while (pb_readvarint32(&L->s, &tag)) { switch (tag) { @@ -1491,7 +1491,7 @@ static void pbL_FileDescriptorProto(pb_Loader *L, pbL_FileInfo *info) { } static void pbL_FileDescriptorSet(pb_Loader *L, pbL_FileInfo **pinfo) { - uint32_t tag; + uint32_t tag = 0; while (pb_readvarint32(&L->s, &tag)) { switch (tag) { case pb_pair(1, PB_TBYTES): /* FileDescriptorProto file */ From e821d562ab7f8752ee44ed67babbedcd5f47c5c8 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 20 Jul 2019 11:43:57 +0800 Subject: [PATCH 233/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=94=AF=E6=8C=815.4?= =?UTF-8?q?=E7=9A=84API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltask.c | 2 +- luaclib/src/ltcp.c | 6 +++--- luaclib/src/ltimer.c | 2 +- luaclib/src/ludp.c | 2 +- src/core.c | 14 +++----------- src/core_sys.h | 8 ++++++++ 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/luaclib/src/ltask.c b/luaclib/src/ltask.c index a5e84bce..114f423c 100644 --- a/luaclib/src/ltask.c +++ b/luaclib/src/ltask.c @@ -6,7 +6,7 @@ static void TASK_CB(CORE_P_ core_task *task, int revents){ lua_State *co = (lua_State *) core_get_watcher_userdata(task); if (co && (lua_status(co) == LUA_YIELD || lua_status(co) == LUA_OK)){ - int status = lua_resume(co, NULL, lua_status(co) == LUA_YIELD ? lua_gettop(co) : lua_gettop(co) - 1); + int status = CO_RESUME(co, NULL, lua_status(co) == LUA_YIELD ? lua_gettop(co) : lua_gettop(co) - 1); if (status != LUA_YIELD && status != LUA_OK){ LOG("ERROR", lua_tostring(co, -1)); } diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 1764a4c8..d1a00e79 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -180,7 +180,7 @@ TCP_IO_CB(CORE_P_ core_io *io, int revents) { lua_State *co = (lua_State *)core_get_watcher_userdata(io); if (lua_status(co) == LUA_YIELD || lua_status(co) == LUA_OK){ - int status = lua_resume(co, NULL, 0); + int status = CO_RESUME(co, NULL, 0); if (status != LUA_YIELD && status != LUA_OK){ LOG("ERROR", lua_tostring(co, -1)); core_io_stop(CORE_LOOP_ io); @@ -203,7 +203,7 @@ IO_CONNECT(CORE_P_ core_io *io, int revents){ socklen_t len = sizeof(socklen_t); if(getsockopt(io->fd, SOL_SOCKET, SO_ERROR, &err, (socklen_t*)&len) == 0 && err == 0) CONNECTED = 1; lua_pushboolean(co, CONNECTED); - int status = lua_resume(co, NULL, 1); + int status = CO_RESUME(co, NULL, 1); if (status != LUA_YIELD && status != LUA_OK){ LOG("ERROR", lua_tostring(co, -1)); core_io_stop(CORE_LOOP_ io); @@ -235,7 +235,7 @@ IO_ACCEPT(CORE_P_ core_io *io, int revents){ inet_ntop(AF_INET6, &SA.sin6_addr, buf, INET6_ADDRSTRLEN); lua_pushinteger(co, client); lua_pushlstring(co, buf, strlen(buf)); - int status = lua_resume(co, NULL, lua_status(co) == LUA_YIELD ? lua_gettop(co) : lua_gettop(co) - 1); + int status = CO_RESUME(co, NULL, lua_status(co) == LUA_YIELD ? lua_gettop(co) : lua_gettop(co) - 1); if (status != LUA_YIELD && status != LUA_OK) { LOG("ERROR", lua_tostring(co, -1)); LOG("ERROR", "Error Lua Accept Method"); diff --git a/luaclib/src/ltimer.c b/luaclib/src/ltimer.c index 6d2018fe..5e828c45 100644 --- a/luaclib/src/ltimer.c +++ b/luaclib/src/ltimer.c @@ -10,7 +10,7 @@ TIMEOUT_CB(CORE_P_ core_timer *timer, int revents){ lua_State *co = (lua_State *) core_get_watcher_userdata(timer); - int status = lua_resume(co, NULL, lua_gettop(co) > 0 ? lua_gettop(co) - 1 : 0); + int status = CO_RESUME(co, NULL, lua_gettop(co) > 0 ? lua_gettop(co) - 1 : 0); if (status != LUA_OK && status != LUA_YIELD){ diff --git a/luaclib/src/ludp.c b/luaclib/src/ludp.c index fb818a3e..7740338f 100644 --- a/luaclib/src/ludp.c +++ b/luaclib/src/ludp.c @@ -84,7 +84,7 @@ UDP_IO_CB(CORE_P_ core_io *io, int revents){ if (revents & EV_READ){ lua_State *co = (lua_State *)core_get_watcher_userdata(io); if (lua_status(co) == LUA_YIELD || lua_status(co) == LUA_OK){ - status = lua_resume(co, NULL, lua_gettop(co) > 0 ? lua_gettop(co) - 1 : 0); + status = CO_RESUME(co, NULL, lua_gettop(co) > 0 ? lua_gettop(co) - 1 : 0); if (status != LUA_YIELD && status != LUA_OK){ LOG("ERROR", lua_tostring(co, -1)); } diff --git a/src/core.c b/src/core.c index 11973772..f8089780 100644 --- a/src/core.c +++ b/src/core.c @@ -164,14 +164,7 @@ init_main(){ init_lua_libs(L); - // 停止GC - lua_gc(L, LUA_GCSTOP, 0); - - // 设置 GC间歇率 = 每次开启一次新的GC所需的等待时间与条件; 默认为:200 - // lua_gc(L, LUA_GCSETPAUSE, 200); - - // 设置 GC步进率倍率 = 控制垃圾收集器相对于内存分配速度的倍数; 默认为:200 - // lua_gc(L, LUA_GCSETSTEPMUL, 200); + CO_GCRESET(L); status = luaL_loadfile(L, "script/main.lua"); if (status > 1){ @@ -179,7 +172,7 @@ init_main(){ return lua_close(L), exit(-1); } - status = lua_resume(L, NULL, 0); + status = CO_RESUME(L, NULL, 0); if (status > 1){ LOG("ERROR", lua_tostring(L, -1)); return lua_close(L), exit(-1); @@ -187,8 +180,7 @@ init_main(){ if (status == LUA_YIELD) { signal_init(); } - /* 重启GC */ - lua_gc(L, LUA_GCRESTART, 0); + } void diff --git a/src/core_sys.h b/src/core_sys.h index c6cbfd0a..101b78f1 100644 --- a/src/core_sys.h +++ b/src/core_sys.h @@ -26,6 +26,14 @@ #include #include +#if LUA_VERSION_NUM >= 504 + #define CO_GCRESET(L) lua_gc(L, LUA_GCGEN, NULL, NULL); + #define CO_RESUME(L, from, nargs) ({int nout; lua_resume(L, from, nargs, &nout);}) +#else + #define CO_GCRESET(L) + #define CO_RESUME(L, from, nargs) lua_resume(L, from, nargs) +#endif + #ifndef EWOULDBLOCK #define EWOULDBLOCK EAGAIN #endif From cdae6c6666ae97fd3364114652a4050cbf2c5b0a Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 20 Jul 2019 16:11:28 +0800 Subject: [PATCH 234/956] =?UTF-8?q?=E9=BB=98=E8=AE=A4=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_sys.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core_sys.h b/src/core_sys.h index 101b78f1..dd6fb81b 100644 --- a/src/core_sys.h +++ b/src/core_sys.h @@ -28,7 +28,7 @@ #if LUA_VERSION_NUM >= 504 #define CO_GCRESET(L) lua_gc(L, LUA_GCGEN, NULL, NULL); - #define CO_RESUME(L, from, nargs) ({int nout; lua_resume(L, from, nargs, &nout);}) + #define CO_RESUME(L, from, nargs) ({int nout = 0; lua_resume(L, from, nargs, &nout);}) #else #define CO_GCRESET(L) #define CO_RESUME(L, from, nargs) lua_resume(L, from, nargs) From 70973270bb758dd90fef933fc574e54cf263e8cc Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 21 Jul 2019 18:32:31 +0800 Subject: [PATCH 235/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lpbc/Makefile | 4 ++-- lualib/httpc/protocol.lua | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/luaclib/src/lpbc/Makefile b/luaclib/src/lpbc/Makefile index e1e578a5..a9f352e8 100644 --- a/luaclib/src/lpbc/Makefile +++ b/luaclib/src/lpbc/Makefile @@ -17,11 +17,11 @@ DLL = -lcore build: $(CC) -o lprotobuf.so lpb.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) - mv lprotobuf.so ../../ + mv *.so ../../ rebuild: $(CC) -o lprotobuf.so lpb.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) - mv lprotobuf.so ../../ + mv *.so ../../ clean: rm -rf *.o *.so diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua index 47c2251e..d5180171 100644 --- a/lualib/httpc/protocol.lua +++ b/lualib/httpc/protocol.lua @@ -179,7 +179,7 @@ local function httpc_response(sock, SSL) local buf = split(DATA, posB + 1, #DATA) data, len = RESPONSE_CHUNKED_PARSER(buf) if len == -1 then - return nil, SSL.." 错误的http trunked" + return nil, SSL.." 错误的http trunked. 1" end if data then local Pos = find(data, CRLF..(0)..CRLF2) @@ -195,7 +195,7 @@ local function httpc_response(sock, SSL) insert(content, data) local data, len = RESPONSE_CHUNKED_PARSER(concat(content)) if len == -1 then - return nil, SSL.." 错误的http trunked. 1" + return nil, SSL.." 错误的http trunked. 2" end if data then local Pos = find(data, CRLF..(0)..CRLF2) From 2af6537b2d4b9460119b766b36dee0df06fbb0b0 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 21 Jul 2019 19:24:17 +0800 Subject: [PATCH 236/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lhttpparser/lhttpparser.c | 10 ++-------- luaclib/src/lpeg/makefile | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/luaclib/src/lhttpparser/lhttpparser.c b/luaclib/src/lhttpparser/lhttpparser.c index 2a4290fd..ca7fd50d 100644 --- a/luaclib/src/lhttpparser/lhttpparser.c +++ b/luaclib/src/lhttpparser/lhttpparser.c @@ -7,15 +7,11 @@ int lparser_response_chunked(lua_State *L){ size_t buf_len; const char* data = luaL_checklstring(L, 1, &buf_len); - if (!data) return luaL_error(L, "parser_response_trunck_decode need a string buf."); char *buf = (char *)xcalloc(1, buf_len); - if (!buf) return luaL_error(L, "parser_response_trunck_decode create a string buf error."); - strncpy(buf, data, buf_len); - struct phr_chunked_decoder decoder = {}; - decoder.consume_trailer = 1; + struct phr_chunked_decoder decoder = { .consume_trailer = 1 }; int last = phr_decode_chunked(&decoder, buf, &buf_len); if (0 > last) { @@ -24,7 +20,7 @@ lparser_response_chunked(lua_State *L){ xfree(buf); return 2; } - lua_pushlstring(L, buf, strlen(buf)); + lua_pushlstring(L, buf, buf_len); xfree(buf); return 1; } @@ -34,7 +30,6 @@ static int lparser_http_request(lua_State *L){ size_t buf_len; const char* buf = luaL_checklstring(L, 1, &buf_len); - if (!buf) return luaL_error(L, "lparser_request_header need a str buf."); int minor_version, ret, i; const char *method; @@ -67,7 +62,6 @@ static int lparser_http_response(lua_State *L){ size_t buf_len; const char* buf = luaL_checklstring(L, 1, &buf_len); - if (!buf) return luaL_error(L, "parser_response_protocol need a str buf."); int status, minor_version, ret, i; size_t msg_len, num_headers; diff --git a/luaclib/src/lpeg/makefile b/luaclib/src/lpeg/makefile index d6d5e930..852473c1 100644 --- a/luaclib/src/lpeg/makefile +++ b/luaclib/src/lpeg/makefile @@ -9,7 +9,7 @@ default : CC = gcc LIBS = -L/usr/local/lib -L../ -L../../../ -CFLAGS = -O2 -shared -fPIC +CFLAGS = -O3 -shared -fPIC DLL = -lcore build: From c911ca74e643efc006716dae99296c6d6c868aa2 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 21 Jul 2019 19:32:48 +0800 Subject: [PATCH 237/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E4=B8=8E=E7=BC=96=E8=AF=91=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcjson/Makefile | 5 +++-- luaclib/src/lcjson/fpconv.c | 4 ++-- luaclib/src/lcjson/strbuf.c | 12 ++++++------ luaclib/src/lpeg/makefile | 2 +- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/luaclib/src/lcjson/Makefile b/luaclib/src/lcjson/Makefile index 29d2a038..387cbbee 100644 --- a/luaclib/src/lcjson/Makefile +++ b/luaclib/src/lcjson/Makefile @@ -14,13 +14,14 @@ CC = cc INCLUDE = -I/usr/local/include LIB = -L/usr/local/lib -L../ -L../../../ +DLL = -lcore build: - $(CC) -o cjson.so lua_cjson.c fpconv.c strbuf.c $(INCLUDE) $(LIB) $(CFLAGS) -lcore + $(CC) -o cjson.so lua_cjson.c fpconv.c strbuf.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) mv *.so ../../ rebuild: $(OBJS) - $(CC) -o cjson.so lua_cjson.c fpconv.c strbuf.c $(INCLUDE) $(LIB) $(CFLAGS) -lcore + $(CC) -o cjson.so lua_cjson.c fpconv.c strbuf.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) mv *.so ../../ clean: diff --git a/luaclib/src/lcjson/fpconv.c b/luaclib/src/lcjson/fpconv.c index 63ea938d..0e058873 100644 --- a/luaclib/src/lcjson/fpconv.c +++ b/luaclib/src/lcjson/fpconv.c @@ -127,7 +127,7 @@ double fpconv_strtod(const char *nptr, char **endptr) /* Duplicate number into buffer */ if (buflen >= FPCONV_G_FMT_BUFSIZE) { /* Handle unusually large numbers */ - buf = malloc(buflen + 1); + buf = xmalloc(buflen + 1); if (!buf) { fprintf(stderr, "Out of memory"); abort(); @@ -147,7 +147,7 @@ double fpconv_strtod(const char *nptr, char **endptr) value = strtod(buf, &endbuf); *endptr = (char *)&nptr[endbuf - buf]; if (buflen >= FPCONV_G_FMT_BUFSIZE) - free(buf); + xfree(buf); return value; } diff --git a/luaclib/src/lcjson/strbuf.c b/luaclib/src/lcjson/strbuf.c index 764eeef5..8b4dc00a 100644 --- a/luaclib/src/lcjson/strbuf.c +++ b/luaclib/src/lcjson/strbuf.c @@ -55,7 +55,7 @@ void strbuf_init(strbuf_t *s, int len) s->reallocs = 0; s->debug = 0; - s->buf = malloc(size); + s->buf = xmalloc(size); if (!s->buf) die("Out of memory"); @@ -66,7 +66,7 @@ strbuf_t *strbuf_new(int len) { strbuf_t *s; - s = malloc(sizeof(strbuf_t)); + s = xmalloc(sizeof(strbuf_t)); if (!s) die("Out of memory"); @@ -103,11 +103,11 @@ void strbuf_free(strbuf_t *s) debug_stats(s); if (s->buf) { - free(s->buf); + xfree(s->buf); s->buf = NULL; } if (s->dynamic) - free(s); + xfree(s); } char *strbuf_free_to_string(strbuf_t *s, int *len) @@ -123,7 +123,7 @@ char *strbuf_free_to_string(strbuf_t *s, int *len) *len = s->length; if (s->dynamic) - free(s); + xfree(s); return buf; } @@ -170,7 +170,7 @@ void strbuf_resize(strbuf_t *s, int len) } s->size = newsize; - s->buf = realloc(s->buf, s->size); + s->buf = xrealloc(s->buf, s->size); if (!s->buf) die("Out of memory"); s->reallocs++; diff --git a/luaclib/src/lpeg/makefile b/luaclib/src/lpeg/makefile index 852473c1..39bfc285 100644 --- a/luaclib/src/lpeg/makefile +++ b/luaclib/src/lpeg/makefile @@ -7,7 +7,7 @@ default : @echo "Please use 'make clean' command to clean all." @echo "=======================================" -CC = gcc +CC = cc LIBS = -L/usr/local/lib -L../ -L../../../ CFLAGS = -O3 -shared -fPIC DLL = -lcore From 5c1a08a2984ac1e6d0ad7a357d4d83733a9d35f2 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 22 Jul 2019 02:26:59 +0800 Subject: [PATCH 238/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Makefile | 2 +- src/core.c | 9 +++------ src/core_ev.c | 2 -- src/core_sys.c | 6 +++--- src/core_sys.h | 17 +++++++++-------- 5 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/Makefile b/src/Makefile index b76205fb..59e2dbf5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -25,7 +25,7 @@ INCLUDES += -I/usr/local/include # 默认情况下使用系统内存分配器 CFLAGS += -Wall -O3 -fPIC --shared -fno-strict-aliasing -Wl,-rpath,/usr/local/lib DLL += -lev -llua -ldl -MACRO += -w -O3 -Wl,-rpath,./ +MACRO += -Wall -O3 -Wl,-rpath,./ build : diff --git a/src/core.c b/src/core.c index f8089780..2a1e9d04 100644 --- a/src/core.c +++ b/src/core.c @@ -1,5 +1,5 @@ #include "core.h" - +/* const char *signame[]= { "INVALID", "SIGHUP", @@ -37,6 +37,7 @@ const char *signame[]= { }; #define signum_to_string(number) (signame[number]) +*/ static void // 忽略信号 SIG_IGNORE(core_loop *loop, core_signal *signal, int revents){ @@ -59,10 +60,6 @@ ERROR_CB(const char *msg){ static void * EV_ALLOC(void *ptr, long nsize){ // 为libev内存hook注入日志; - if (ptr && 0 > nsize){ - LOG("ERROR", "attemp to pass a negative number to malloc or free") - return NULL; - } if (nsize == 0) return xfree(ptr), NULL; for (;;) { void *newptr = xrealloc(ptr, nsize); @@ -109,7 +106,7 @@ init_lua_libs(lua_State *L){ lua_setfield(L, 1, "path"); /* 注入luaclib搜索路径 */ - lua_pushliteral(L, "luaclib/?.so;./?.so;"); + lua_pushliteral(L, "luaclib/?.so;./?.so;luaclib/lib?.so;./lib?.so;luaclib/?.dylib;./?.dylib;luaclib/lib?.dylib;./lib?.dylib;"); lua_setfield(L, 1, "cpath"); lua_settop(L, 0); diff --git a/src/core_ev.c b/src/core_ev.c index 4973bd5d..52c16748 100644 --- a/src/core_ev.c +++ b/src/core_ev.c @@ -30,8 +30,6 @@ core_timer_stop(core_loop *loop, core_timer *timer){ /* =========== Timer =========== */ - - /* =========== IO =========== */ void core_io_init(core_io *io, _IO_CB cb, int fd, int events){ diff --git a/src/core_sys.c b/src/core_sys.c index dfb6f2a7..e3c40de8 100644 --- a/src/core_sys.c +++ b/src/core_sys.c @@ -2,7 +2,7 @@ double /* 此方法提供一个精确到微秒级的时间戳 */ now(void){ - struct timespec now; + struct timespec now = {}; clock_gettime(CLOCK_REALTIME, &now); return now.tv_sec + now.tv_nsec * 1e-9; } @@ -10,7 +10,7 @@ now(void){ int /* 此方法可用于检查是否为有效ipv4地址*/ ipv4(const char *IP){ if (!IP) return 0; - struct in_addr addr; + struct in_addr addr = {}; if (inet_pton(AF_INET, IP, &addr) == 1) return 1; return 0; } @@ -18,7 +18,7 @@ ipv4(const char *IP){ int /* 此方法可用于检查是否为有效ipv6地址*/ ipv6(const char *IP){ if (!IP) return 0; - struct in6_addr addr; + struct in6_addr addr = {}; if (inet_pton(AF_INET6, IP, &addr) == 1) return 1; return 0; } diff --git a/src/core_sys.h b/src/core_sys.h index dd6fb81b..1a4d2361 100644 --- a/src/core_sys.h +++ b/src/core_sys.h @@ -42,14 +42,15 @@ /* [datetime][level][file][function][line][具体打印内容] */ #define LOG(log_level, content) { \ - time_t t; struct tm* lt; \ - /*获取Unix时间戳、转为时间结构。*/ \ - time(&t); lt = localtime(&t); \ - fprintf(stdout, "[%04d/%02d/%02d][%02d:%02d:%02d][%s][%s][%s:%d] : %s\n", \ - lt->tm_year+1900, 1+lt->tm_mon, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec, \ - log_level, \ - __FILE__, __FUNCTION__, __LINE__, \ - content);} + time_t t; struct tm* lt; \ + /*获取Unix时间戳、转为时间结构。*/ \ + time(&t); lt = localtime(&t); \ + fprintf(stdout, "[%04d/%02d/%02d][%02d:%02d:%02d][%s][%s][%s:%d] : %s\n", \ + lt->tm_year + 1900, 1 + lt->tm_mon, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec, \ + log_level, \ + __FILE__, __FUNCTION__, __LINE__, \ + content); \ +} /* 微秒级时间戳函数 */ double now(void); From d5cd1501cac4773fe71690bae8f82c051f02897a Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 22 Jul 2019 02:43:11 +0800 Subject: [PATCH 239/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/Makefile | 6 ++-- luaclib/src/lcrypt/lcrypt.c | 6 ++++ luaclib/src/lcrypt/url.c | 55 +++++++++++++++++++++++++++++++++++++ luaclib/src/lsys.c | 54 ------------------------------------ lualib/crypt/init.lua | 11 ++++++++ lualib/url/init.lua | 4 +-- 6 files changed, 78 insertions(+), 58 deletions(-) create mode 100644 luaclib/src/lcrypt/url.c diff --git a/luaclib/src/lcrypt/Makefile b/luaclib/src/lcrypt/Makefile index e67ced2e..de6c63f6 100644 --- a/luaclib/src/lcrypt/Makefile +++ b/luaclib/src/lcrypt/Makefile @@ -16,12 +16,14 @@ DLL = -lcore INCLUDE = -I/usr/local/include LIB = -L/usr/local/lib -L../ -L../../../ +FILES = lcrypt.c crc.c md5.c url.c sha1.c sha2.c + build: - $(CC) -o lcrypt.so lcrypt.c crc.c md5.c sha1.c sha2.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) + $(CC) -o lcrypt.so $(FILES) $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) mv *.so ../../ rebuild: - $(CC) -o lcrypt.so lcrypt.c crc.c md5.c sha1.c sha2.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) + $(CC) -o lcrypt.so $(FILES) $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) mv *.so ../../ clean: diff --git a/luaclib/src/lcrypt/lcrypt.c b/luaclib/src/lcrypt/lcrypt.c index e2c5b74c..3271f22e 100644 --- a/luaclib/src/lcrypt/lcrypt.c +++ b/luaclib/src/lcrypt/lcrypt.c @@ -962,6 +962,10 @@ int lhmac_sha256(lua_State *L); int lsha512(lua_State *L); int lhmac_sha512(lua_State *L); +// url.c +int lurlencode(lua_State *L); +int lurldecode(lua_State *L); + LUAMOD_API int luaopen_lcrypt(lua_State *L) { luaL_checkversion(L); @@ -978,6 +982,8 @@ luaopen_lcrypt(lua_State *L) { { "dhsecret", ldhsecret }, { "base64encode", lb64encode }, { "base64decode", lb64decode }, + { "urlencode", lurlencode }, + { "urldecode", lurldecode }, { "md5", lmd5 }, { "hmac_md5", lhmac_md5 }, { "crc32", lcrc32 }, diff --git a/luaclib/src/lcrypt/url.c b/luaclib/src/lcrypt/url.c new file mode 100644 index 00000000..c0512d1b --- /dev/null +++ b/luaclib/src/lcrypt/url.c @@ -0,0 +1,55 @@ +#define LUA_LIB + +#include "../../../src/core.h" + +#define hex_char(ch) ({(uint8_t)((ch) > 9 ? (ch) + 55: (ch) + 48);}) + +#define is_normal_char(ch) ({((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || ((ch) >= '0' && (ch) <= '9') ? 1 : 0;}) + +/* url编码 */ +int +lurlencode(lua_State *L){ + size_t url_len; + const char* url = luaL_checklstring(L, 1, &url_len); + if (!url) return luaL_error(L, "urlencode error: 需要传递一个有效的url字符串!"); + luaL_Buffer convert_url; + luaL_buffinit(L, &convert_url); + while (*url) { + uint8_t ch = (uint8_t)*url++; + if (ch == ' ') { + luaL_addlstring(&convert_url, "%20", 3); + continue; + } + if (is_normal_char(ch) || strchr("-_.!~*'()", ch)){ + luaL_addchar(&convert_url, ch); + continue; + } + char ver[3] = {'%', hex_char(((uint8_t)ch) >> 4), hex_char(((uint8_t)ch) & 15)}; + luaL_addlstring(&convert_url, ver, 3); + } + luaL_pushresult(&convert_url); + return 1; +} + +/* url解码 */ +int +lurldecode(lua_State *L){ + size_t url_len; + const char* url = luaL_checklstring(L, 1, &url_len); + if (!url) return luaL_error(L, "urldecode error: 需要传递一个有效的url字符串!"); + luaL_Buffer convert_url; + luaL_buffinit(L, &convert_url); + while (*url) { + uint8_t ch = (uint8_t)*url++; + if (ch != '%') { + luaL_addchar(&convert_url, ch); + continue; + } + char vert[2]; + vert[0] = (uint8_t)*url++; + vert[1] = (uint8_t)*url++; + luaL_addchar(&convert_url, (uint8_t)((vert[0] - 48 - ((vert[0] >= 'A') ? 7 : 0) - ((vert[0] >= 'a') ? 32 : 0)) * 16 + (vert[1] - 48 - ((vert[1] >= 'A') ? 7 : 0) - ((vert[1] >= 'a') ? 32 : 0)))); + } + luaL_pushresult(&convert_url); + return 1; +} diff --git a/luaclib/src/lsys.c b/luaclib/src/lsys.c index f468a67b..d5b8dfed 100644 --- a/luaclib/src/lsys.c +++ b/luaclib/src/lsys.c @@ -2,10 +2,6 @@ #include "../../src/core.h" -#define hex_char(ch) ({(uint8_t)((ch) > 9 ? (ch) + 55: (ch) + 48);}) - -#define is_normal_char(ch) ({((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || ((ch) >= '0' && (ch) <= '9') ? 1 : 0;}) - // 提供一个精确到微秒的时间戳 static int lnow(lua_State *L){ @@ -43,54 +39,6 @@ ldate(lua_State *L){ return 1; } -/* url编码 */ -static int -lurlencode(lua_State *L){ - size_t url_len; - const char* url = luaL_checklstring(L, 1, &url_len); - if (!url) return luaL_error(L, "urlencode error: 需要传递一个有效的url字符串!"); - luaL_Buffer convert_url; - luaL_buffinit(L, &convert_url); - while (*url) { - uint8_t ch = (uint8_t)*url++; - if (ch == ' ') { - luaL_addlstring(&convert_url, "%20", 3); - continue; - } - if (is_normal_char(ch) || strchr("-_.!~*'()", ch)){ - luaL_addchar(&convert_url, ch); - continue; - } - char ver[3] = {'%', hex_char(((uint8_t)ch) >> 4), hex_char(((uint8_t)ch) & 15)}; - luaL_addlstring(&convert_url, ver, 3); - } - luaL_pushresult(&convert_url); - return 1; -} - -/* url解码 */ -static int -lurldecode(lua_State *L){ - size_t url_len; - const char* url = luaL_checklstring(L, 1, &url_len); - if (!url) return luaL_error(L, "urldecode error: 需要传递一个有效的url字符串!"); - luaL_Buffer convert_url; - luaL_buffinit(L, &convert_url); - while (*url) { - uint8_t ch = (uint8_t)*url++; - if (ch != '%') { - luaL_addchar(&convert_url, ch); - continue; - } - char vert[2]; - vert[0] = (uint8_t)*url++; - vert[1] = (uint8_t)*url++; - luaL_addchar(&convert_url, (uint8_t)((vert[0] - 48 - ((vert[0] >= 'A') ? 7 : 0) - ((vert[0] >= 'a') ? 32 : 0)) * 16 + (vert[1] - 48 - ((vert[1] >= 'A') ? 7 : 0) - ((vert[1] >= 'a') ? 32 : 0)))); - } - luaL_pushresult(&convert_url); - return 1; -} - static int lnew_tab(lua_State *L){ lua_Integer array_size = luaL_checkinteger(L, 1); @@ -108,8 +56,6 @@ luaopen_sys(lua_State *L){ {"ipv6", lipv6}, {"date", ldate}, {"new_tab", lnew_tab}, - {"urlencode", lurlencode}, - {"urldecode", lurldecode}, {NULL, NULL} }; luaL_newlib(L, sys_libs); diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index cb99adc7..ef1ab9cb 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -40,6 +40,9 @@ local desdecode = CRYPT.desdecode local dhsecret = CRYPT.dhsecret local dhexchange = CRYPT.dhexchange +local urlencode = CRYPT.urlencode +local urldecode = CRYPT.urldecode + local crypt = {} @@ -252,4 +255,12 @@ function crypt.crc64 (...) return crc64(...) end +function crypt.urldecode (...) + return urldecode(...) +end + +function crypt.urlencode (...) + return urlencode(...) +end + return crypt diff --git a/lualib/url/init.lua b/lualib/url/init.lua index 6074eb36..5b2ca645 100644 --- a/lualib/url/init.lua +++ b/lualib/url/init.lua @@ -6,8 +6,8 @@ -- local spliter = string.gsub -- C版实现 -local encode = require("sys").urlencode -local decode = require("sys").urldecode +local encode = require("crypt").urlencode +local decode = require("crypt").urldecode --[[ 经过测试: 100万此编码/解码两者性能相差30倍, 正好是lua与C的性能差距. From d093395acc9458ca9bfe438834345b69351c781f Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 22 Jul 2019 04:06:43 +0800 Subject: [PATCH 240/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dtest=5Fcsv=E8=AF=BB?= =?UTF-8?q?=E5=8F=96=E6=96=87=E4=BB=B6=E8=B7=AF=E5=BE=84=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_csv.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/test_csv.lua b/script/test_csv.lua index 42aa5a8b..a7f16e14 100644 --- a/script/test_csv.lua +++ b/script/test_csv.lua @@ -2,4 +2,4 @@ local csv = require "csv" local Log = require("logging"):new() -Log:DEBUG(csv.loadfile("../Excel.csv")) +Log:DEBUG(csv.loadfile("./Excel.csv")) From 4be9ab73a21443a65ff39305cc31257f397a1a2d Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 22 Jul 2019 04:16:43 +0800 Subject: [PATCH 241/956] =?UTF-8?q?=E5=AE=8C=E5=96=84test=5Fhttpc=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_httpc.lua | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/script/test_httpc.lua b/script/test_httpc.lua index 98e40ce9..32cd6b1d 100644 --- a/script/test_httpc.lua +++ b/script/test_httpc.lua @@ -116,3 +116,12 @@ cf.timeout(5, function () hc:close() end) end) + +-- -- 如果有需要可以开启这段注释 +-- local httpd = require "httpd" +-- local app = httpd:new() +-- app:api('/api', function (content) +-- return "{}" +-- end) +-- app:listen("", 8080) +-- app:run() From 9509b9b3687f5cca092e05c34132eafc3735ac75 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 22 Jul 2019 07:22:14 +0800 Subject: [PATCH 242/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=A7=A3=E9=87=8A?= =?UTF-8?q?=E5=99=A8=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.sh b/build.sh index 793e2a15..980abe5c 100755 --- a/build.sh +++ b/build.sh @@ -1,4 +1,6 @@ +#!/bin/bash # 运行这个文件可以安装libev与lua + current=`pwd` rm -rf build && mkdir build && cd build From 7d0bdf34e1823d45f932a2536321e12bf9f08a59 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 22 Jul 2019 17:11:09 +0800 Subject: [PATCH 243/956] =?UTF-8?q?=E4=BC=98=E5=8C=96TCP.lua=E4=BB=A3?= =?UTF-8?q?=E7=A0=81,=20=E6=8F=90=E5=8D=87=E5=8F=AF=E8=AF=BB=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/TCP.lua | 128 ++++++++++++++++++++-------------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index f2ab99bc..f35880f5 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -84,80 +84,79 @@ function TCP:set_backlog(backlog) end function TCP:send(buf) - if self.ssl then - return Log:ERROR("Please use ssl_send method :)") - end + if self.ssl then + Log:ERROR("Please use ssl_send method :)") + return + end + if type(buf) ~= 'string' or buf == '' then + return + end + local wlen = tcp_write(self.fd, buf, #buf) + if not wlen or wlen == #buf then + return wlen == #buf + end + buf = split(buf, wlen + 1, -1) + local co = co_self() + self.SEND_IO = tcp_pop() + self.send_current_co = co_self() + self.send_co = co_new(function ( ... ) while 1 do - local len = tcp_write(self.fd, buf, #buf) - if not len or len == #buf then - return len == #buf - end - if len == 0 then - self.SEND_IO = tcp_pop() - local co = co_self() - self.send_current_co = co_self() - self.send_co = co_new(function ( ... ) - while 1 do - local len = tcp_write(self.fd, buf, #buf) - if not len or len == #buf then - tcp_push(self.SEND_IO) - tcp_stop(self.SEND_IO) - self.SEND_IO = nil - self.send_co = nil - self.send_current_co = nil - return co_wakeup(co, len == #buf) - end - buf = split(buf, len + 1, -1) - co_wait() - end - end) - tcp_start(self.SEND_IO, self.fd, EVENT_WRITE, self.send_co) - return co_wait() - end - buf = split(buf, len + 1, -1) + local len = tcp_write(self.fd, buf, #buf) + if not len or len == #buf then + tcp_stop(self.SEND_IO) + tcp_push(self.SEND_IO) + self.SEND_IO = nil + self.send_co = nil + self.send_current_co = nil + return co_wakeup(co, len == #buf) + end + buf = split(buf, len + 1, -1) + co_wait() end + end) + tcp_start(self.SEND_IO, self.fd, EVENT_WRITE, self.send_co) + return co_wait() end function TCP:ssl_send(buf) - if not self.ssl then - return Log:ERROR("Please use send method :)") - end + if not self.ssl then + Log:ERROR("Please use send method :)") + return + end + if type(buf) ~= 'string' or buf == '' then + return + end + local wlen = tcp_ssl_write(self.ssl, buf, #buf) + if not wlen or wlen == #buf then + return wlen == #buf + end + buf = split(buf, wlen + 1, -1) + self.SEND_IO = tcp_pop() + local co = co_self() + self.send_current_co = co_self() + self.send_co = co_new(function ( ... ) while 1 do - local len = tcp_ssl_write(self.ssl, buf, #buf) - if not len or len == #buf then - return len == #buf - end - if len == 0 then - self.SEND_IO = tcp_pop() - local co = co_self() - self.send_current_co = co_self() - self.send_co = co_new(function ( ... ) - while 1 do - local len = tcp_ssl_write(self.ssl, buf, #buf) - if not len or len == #buf then - tcp_push(self.SEND_IO) - tcp_stop(self.SEND_IO) - self.SEND_IO = nil - self.send_co = nil - self.send_current_co = nil - -- 这里在发送数据的时候, 客户端可能已经关闭了链接 - -- if not len then log.error("write error.") - return co_wakeup(co, len == #buf) - end - buf = split(buf, len + 1, -1) - co_wait() - end - end) - tcp_start(self.SEND_IO, self.fd, EVENT_WRITE, self.send_co) - return co_wait() - end - buf = split(buf, len + 1, -1) + local len = tcp_ssl_write(self.ssl, buf, #buf) + if not len or len == #buf then + tcp_stop(self.SEND_IO) + tcp_push(self.SEND_IO) + self.SEND_IO = nil + self.send_co = nil + self.send_current_co = nil + return co_wakeup(co, len == #buf) + end + buf = split(buf, len + 1, -1) + co_wait() end + end) + tcp_start(self.SEND_IO, self.fd, EVENT_WRITE, self.send_co) + return co_wait() end function TCP:recv(bytes) if self.ssl then - return Log:ERROR("Please use ssl_recv method :)") + Log:ERROR("Please use ssl_recv method :)") + return end local data, len = tcp_read(self.fd, bytes) if not len or len > 0 then @@ -197,7 +196,8 @@ end function TCP:ssl_recv(bytes) if not self.ssl then - return Log:ERROR("Please use recv method :)") + Log:ERROR("Please use recv method :)") + return end local buf, len = tcp_sslread(self.ssl, bytes) if not buf then From 33901bce5ba20700741de348c046b1c08a87216c Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 24 Jul 2019 07:17:39 +0800 Subject: [PATCH 244/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E5=AE=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Makefile | 2 +- src/core_sys.h | 26 ++++++++++++++++---------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/Makefile b/src/Makefile index 59e2dbf5..b76205fb 100644 --- a/src/Makefile +++ b/src/Makefile @@ -25,7 +25,7 @@ INCLUDES += -I/usr/local/include # 默认情况下使用系统内存分配器 CFLAGS += -Wall -O3 -fPIC --shared -fno-strict-aliasing -Wl,-rpath,/usr/local/lib DLL += -lev -llua -ldl -MACRO += -Wall -O3 -Wl,-rpath,./ +MACRO += -w -O3 -Wl,-rpath,./ build : diff --git a/src/core_sys.h b/src/core_sys.h index 1a4d2361..b063142a 100644 --- a/src/core_sys.h +++ b/src/core_sys.h @@ -27,11 +27,19 @@ #include #if LUA_VERSION_NUM >= 504 - #define CO_GCRESET(L) lua_gc(L, LUA_GCGEN, NULL, NULL); - #define CO_RESUME(L, from, nargs) ({int nout = 0; lua_resume(L, from, nargs, &nout);}) + #ifndef CO_GCRESET + #define CO_GCRESET(L) lua_gc(L, LUA_GCGEN, NULL, NULL); + #endif + #ifndef CO_RESUME + #define CO_RESUME(L, from, nargs) ({int nout = 0; lua_resume(L, from, nargs, &nout);}) + #endif #else - #define CO_GCRESET(L) - #define CO_RESUME(L, from, nargs) lua_resume(L, from, nargs) + #ifndef CO_GCRESET + #define CO_GCRESET(L) + #endif + #ifndef CO_RESUME + #define CO_RESUME(L, from, nargs) lua_resume(L, from, nargs) + #endif #endif #ifndef EWOULDBLOCK @@ -41,15 +49,13 @@ #define non_blocking(socket) (fcntl(socket, F_SETFL, fcntl(socket, F_GETFL, 0) | O_NONBLOCK)); /* [datetime][level][file][function][line][具体打印内容] */ -#define LOG(log_level, content) { \ - time_t t; struct tm* lt; \ - /*获取Unix时间戳、转为时间结构。*/ \ - time(&t); lt = localtime(&t); \ +#define LOG(LEVEL, CONTENT) { \ + time_t t = time(NULL); struct tm* lt = localtime(&t); \ fprintf(stdout, "[%04d/%02d/%02d][%02d:%02d:%02d][%s][%s][%s:%d] : %s\n", \ lt->tm_year + 1900, 1 + lt->tm_mon, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec, \ - log_level, \ + LEVEL, \ __FILE__, __FUNCTION__, __LINE__, \ - content); \ + CONTENT); \ } /* 微秒级时间戳函数 */ From 98fff0d9001a40d6c0717c438720fc76b7f3484e Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 24 Jul 2019 07:18:44 +0800 Subject: [PATCH 245/956] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/TCP.lua | 150 ++++++++++++++++++++-------------------- 1 file changed, 74 insertions(+), 76 deletions(-) diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index f35880f5..2c332a4a 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -267,93 +267,91 @@ function TCP:listen(ip, port, cb) end function TCP:connect(domain, port) - local ok, IP = dns_resolve(domain) - if not ok then - return nil, "Can't resolve this domain or ip:"..(domain or IP or "") + local ok, IP = dns_resolve(domain) + if not ok then + return nil, "Can't resolve this domain or ip:"..(domain or IP or "") + end + self.fd = tcp_new_client_fd(IP, port) + if not self.fd then + return nil, "Connect This host fault! "..(domain or "no domain")..":"..(port or "no port") + end + local co = co_self() + self.CONNECT_IO = tcp_pop() + self.connect_current_co = co_self() + self.connect_co = co_new(function (connected) + if self.timer then + self.timer:stop() + self.timer = nil end - self.fd = tcp_new_client_fd(IP, port) - if not self.fd then - return nil, "Connect This host fault! "..(domain or "no domain")..":"..(port or "no port") + tcp_push(self.CONNECT_IO) + tcp_stop(self.CONNECT_IO) + self.connect_current_co = nil + self.CONNECT_IO = nil + self.connect_co = nil + if connected then + return co_wakeup(co, true) end - local co = co_self() - self.CONNECT_IO = tcp_pop() - self.connect_current_co = co_self() - self.connect_co = co_new(function (connected) + return co_wakeup(co, false, '连接失败') + end) + self.timer = ti_timeout(self._timeout, function ( ... ) + tcp_push(self.CONNECT_IO) + tcp_stop(self.CONNECT_IO) + self.timer = nil + self.CONNECT_IO = nil + self.connect_co = nil + self.connect_current_co = nil + return co_wakeup(co, nil, 'connect timeot.') + end) + tcp_connect(self.CONNECT_IO, self.fd, self.connect_co) + return co_wait() +end + +function TCP:ssl_connect(domain, port) + local ok, err = self:connect(domain, port) + if not ok then + return nil, "domain connect error." + end + self.ssl_ctx, self.ssl = tcp.new_ssl(self.fd) + if not self.ssl_ctx or not self.ssl then + return nil, "create a ssl ctx error." + end + self.CONNECT_IO = tcp_pop() + self.connect_current_co = co_self() + self.connect_co = co_new(function () + local EVENTS = EVENT_WRITE + while 1 do + local ok, EVENT = tcp_ssl_do_handshak(self.ssl) + if ok or not EVENT then if self.timer then - self.timer:stop() - self.timer = nil + self.timer:stop() + self.timer = nil end tcp_push(self.CONNECT_IO) tcp_stop(self.CONNECT_IO) - self.connect_current_co = nil - self.CONNECT_IO = nil - self.connect_co = nil - if connected then - return co_wakeup(co, true) - end - return co_wakeup(co, false, '连接失败') - end) - self.timer = ti_timeout(self._timeout, function ( ... ) - tcp_push(self.CONNECT_IO) - tcp_stop(self.CONNECT_IO) - self.timer = nil self.CONNECT_IO = nil self.connect_co = nil self.connect_current_co = nil - return co_wakeup(co, nil, 'connect timeot.') - end) - tcp_connect(self.CONNECT_IO, self.fd, self.connect_co) - return co_wait() -end - -function TCP:ssl_connect(domain, port) - local ok, err = self:connect(domain, port) - if not ok then - return nil, "SSL connect error." - end - self.ssl_ctx, self.ssl = tcp.new_ssl(self.fd) - if not self.ssl_ctx or not self.ssl then - return Log:ERROR("Create a SSL Error! :) ") - end - - local co = co_self() - self.CONNECT_IO = tcp_pop() - self.connect_current_co = co - self.connect_co = co_new(function () - local EVENTS = EVENT_WRITE - while 1 do - local ok, EVENT = tcp_ssl_do_handshak(self.ssl) - if ok or not EVENT then - if self.timer then - self.timer:stop() - self.timer = nil - end - tcp_push(self.CONNECT_IO) - tcp_stop(self.CONNECT_IO) - self.CONNECT_IO = nil - self.connect_co = nil - self.connect_current_co = nil - return co_wakeup(co, ok) - end - if EVENTS ~= EVENT then - EVENTS = EVENT - tcp_stop(self.CONNECT_IO) - tcp_start(self.CONNECT_IO, self.fd, EVENTS, self.connect_co) - end - co_wait() + return co_wakeup(co, ok) end - end) - self.timer = ti_timeout(self._timeout, function ( ... ) - tcp_push(self.CONNECT_IO) + if EVENTS ~= EVENT then + EVENTS = EVENT tcp_stop(self.CONNECT_IO) - self.timer = nil - self.CONNECT_IO = nil - self.connect_co = nil - self.connect_current_co = nil - return co_wakeup(co, nil, 'ssl_connect timeot.') - end) - tcp_start(self.CONNECT_IO, self.fd, EVENT_WRITE, self.connect_co) - return co_wait() + tcp_start(self.CONNECT_IO, self.fd, EVENTS, self.connect_co) + end + co_wait() + end + end) + self.timer = ti_timeout(self._timeout, function ( ... ) + tcp_push(self.CONNECT_IO) + tcp_stop(self.CONNECT_IO) + self.timer = nil + self.CONNECT_IO = nil + self.connect_co = nil + self.connect_current_co = nil + return co_wakeup(co, nil, 'ssl_connect timeot.') + end) + tcp_start(self.CONNECT_IO, self.fd, EVENT_WRITE, self.connect_co) + return co_wait() end function TCP:count() From 7f1b51e4c75d4fd6dac6b41edb09731bcc9ec188 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 24 Jul 2019 11:41:56 +0800 Subject: [PATCH 246/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0lfs=E5=BA=93=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/Makefile | 2 + luaclib/src/lfs/Makefile | 27 ++ luaclib/src/lfs/lfs.c | 947 +++++++++++++++++++++++++++++++++++++++ luaclib/src/lfs/lfs.h | 34 ++ 4 files changed, 1010 insertions(+) create mode 100644 luaclib/src/lfs/Makefile create mode 100644 luaclib/src/lfs/lfs.c create mode 100644 luaclib/src/lfs/lfs.h diff --git a/luaclib/Makefile b/luaclib/Makefile index b1b38b5f..5fca2178 100644 --- a/luaclib/Makefile +++ b/luaclib/Makefile @@ -20,6 +20,7 @@ build : $(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore $(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore ### 以下为内置第三方库编译位置 ### + cd src/lfs && rm -rf *.o *.so && make build # 增加lfs库 cd src/lpbc && rm -rf *.o *.so && make build # 增加lpbc库 cd src/lpeg && rm -rf *.o *.so && make build # 增加lpeg库 cd src/lcrypt && rm -rf *.o *.so && make build # 增加lcrypt库 @@ -34,6 +35,7 @@ rebuild : $(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore $(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore ### 以下为内置第三方库编译位置 ### + cd src/lfs && rm -rf *.o *.so && make build # 增加lfs库 cd src/lpbc && rm -rf *.o *.so && make build # 增加lpbc库 cd src/lpeg && rm -rf *.o *.so && make build # 增加lpeg库 cd src/lcrypt && rm -rf *.o *.so && make build # 增加lcrypt库 diff --git a/luaclib/src/lfs/Makefile b/luaclib/src/lfs/Makefile new file mode 100644 index 00000000..03af3eae --- /dev/null +++ b/luaclib/src/lfs/Makefile @@ -0,0 +1,27 @@ +.PHONY : build rebuild clean + +default : + @echo "=======================================" + @echo "Please use 'make build' command to build it.." + @echo "Please use 'make rebuild' command to build it.." + @echo "Please use 'make clean' command to clean all." + @echo "=======================================" + +CC = cc + +INCLUDE = -I/usr/local/include +LIB = -L/usr/local/lib -L../ -L../../../ + +CFLAGS = -O3 -Wall -shared -fPIC +DLL = -lcore + +build: + $(CC) -o lfs.so lfs.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) + mv *.so ../../ + +rebuild: + $(CC) -o lfs.so lfs.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) + mv *.so ../../ + +clean: + rm -rf *.o *.so diff --git a/luaclib/src/lfs/lfs.c b/luaclib/src/lfs/lfs.c new file mode 100644 index 00000000..f510ab5a --- /dev/null +++ b/luaclib/src/lfs/lfs.c @@ -0,0 +1,947 @@ +/* +** LuaFileSystem +** Copyright Kepler Project 2003 - 2017 (http://keplerproject.github.io/luafilesystem) +** +** File system manipulation library. +** This library offers these functions: +** lfs.attributes (filepath [, attributename | attributetable]) +** lfs.chdir (path) +** lfs.currentdir () +** lfs.dir (path) +** lfs.link (old, new[, symlink]) +** lfs.lock (fh, mode) +** lfs.lock_dir (path) +** lfs.mkdir (path) +** lfs.rmdir (path) +** lfs.setmode (filepath, mode) +** lfs.symlinkattributes (filepath [, attributename]) +** lfs.touch (filepath [, atime [, mtime]]) +** lfs.unlock (fh) +*/ + +#ifndef LFS_DO_NOT_USE_LARGE_FILE +#ifndef _WIN32 +#ifndef _AIX +#define _FILE_OFFSET_BITS 64 /* Linux, Solaris and HP-UX */ +#else +#define _LARGE_FILES 1 /* AIX */ +#endif +#endif +#endif + +#ifndef LFS_DO_NOT_USE_LARGE_FILE +#define _LARGEFILE64_SOURCE +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 + #include + #include + #include + #include + #ifdef __BORLANDC__ + #include + #else + #include + #endif + #include + /* MAX_PATH seems to be 260. Seems kind of small. Is there a better one? */ + #define LFS_MAXPATHLEN MAX_PATH +#else + #include + #include + #include + #include + #include + #include /* for MAXPATHLEN */ + #define LFS_MAXPATHLEN MAXPATHLEN +#endif + +#include +#include +#include + +#include "lfs.h" + +#define LFS_VERSION "1.7.0" +#define LFS_LIBNAME "lfs" + +#if LUA_VERSION_NUM >= 503 /* Lua 5.3 */ + +#ifndef luaL_optlong +#define luaL_optlong luaL_optinteger +#endif + +#endif + +#if LUA_VERSION_NUM >= 502 +# define new_lib(L, l) (luaL_newlib(L, l)) +#else +# define new_lib(L, l) (lua_newtable(L), luaL_register(L, NULL, l)) +#endif + +/* Define 'strerror' for systems that do not implement it */ +#ifdef NO_STRERROR +#define strerror(_) "System unable to describe the error" +#endif + +#define DIR_METATABLE "directory metatable" +typedef struct dir_data { + int closed; +#ifdef _WIN32 + intptr_t hFile; + char pattern[MAX_PATH+1]; +#else + DIR *dir; +#endif +} dir_data; + +#define LOCK_METATABLE "lock metatable" + +#ifdef _WIN32 + #ifdef __BORLANDC__ + #define lfs_setmode(file, m) (setmode(_fileno(file), m)) + #define STAT_STRUCT struct stati64 + #else + #define lfs_setmode(file, m) (_setmode(_fileno(file), m)) + #define STAT_STRUCT struct _stati64 + #endif +#define STAT_FUNC _stati64 +#define LSTAT_FUNC STAT_FUNC +#else +#define _O_TEXT 0 +#define _O_BINARY 0 +#define lfs_setmode(file, m) ((void)file, (void)m, 0) +#define STAT_STRUCT struct stat +#define STAT_FUNC stat +#define LSTAT_FUNC lstat +#endif + +#ifdef _WIN32 + #define lfs_mkdir _mkdir +#else + #define lfs_mkdir(path) (mkdir((path), \ + S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH)) +#endif + +/* +** Utility functions +*/ +static int pusherror(lua_State *L, const char *info) +{ + lua_pushnil(L); + if (info==NULL) + lua_pushstring(L, strerror(errno)); + else + lua_pushfstring(L, "%s: %s", info, strerror(errno)); + lua_pushinteger(L, errno); + return 3; +} + +static int pushresult(lua_State *L, int res, const char *info) { + if (res == -1) { + return pusherror(L, info); + } else { + lua_pushboolean(L, 1); + return 1; + } +} + + +/* +** This function changes the working (current) directory +*/ +static int change_dir (lua_State *L) { + const char *path = luaL_checkstring(L, 1); + if (chdir(path)) { + lua_pushnil (L); + lua_pushfstring (L,"Unable to change working directory to '%s'\n%s\n", + path, chdir_error); + return 2; + } else { + lua_pushboolean (L, 1); + return 1; + } +} + +/* +** This function returns the current directory +** If unable to get the current directory, it returns nil +** and a string describing the error +*/ +static int get_dir (lua_State *L) { +#ifdef NO_GETCWD + lua_pushnil(L); + lua_pushstring(L, "Function 'getcwd' not provided by system"); + return 2; +#else + char *path = NULL; + /* Passing (NULL, 0) is not guaranteed to work. Use a temp buffer and size instead. */ + size_t size = LFS_MAXPATHLEN; /* initial buffer size */ + int result; + while (1) { + char* path2 = realloc(path, size); + if (!path2) /* failed to allocate */ { + result = pusherror(L, "get_dir realloc() failed"); + break; + } + path = path2; + if (getcwd(path, size) != NULL) { + /* success, push the path to the Lua stack */ + lua_pushstring(L, path); + result = 1; + break; + } + if (errno != ERANGE) { /* unexpected error */ + result = pusherror(L, "get_dir getcwd() failed"); + break; + } + /* ERANGE = insufficient buffer capacity, double size and retry */ + size *= 2; + } + free(path); + return result; +#endif +} + +/* +** Check if the given element on the stack is a file and returns it. +*/ +static FILE *check_file (lua_State *L, int idx, const char *funcname) { +#if LUA_VERSION_NUM == 501 + FILE **fh = (FILE **)luaL_checkudata (L, idx, "FILE*"); + if (*fh == NULL) { + luaL_error (L, "%s: closed file", funcname); + return 0; + } else + return *fh; +#elif LUA_VERSION_NUM >= 502 && LUA_VERSION_NUM <= 503 + luaL_Stream *fh = (luaL_Stream *)luaL_checkudata (L, idx, "FILE*"); + if (fh->closef == 0 || fh->f == NULL) { + luaL_error (L, "%s: closed file", funcname); + return 0; + } else + return fh->f; +#else +#error unsupported Lua version +#endif +} + + +/* +** +*/ +static int _file_lock (lua_State *L, FILE *fh, const char *mode, const long start, long len, const char *funcname) { + int code; +#ifdef _WIN32 + /* lkmode valid values are: + LK_LOCK Locks the specified bytes. If the bytes cannot be locked, the program immediately tries again after 1 second. If, after 10 attempts, the bytes cannot be locked, the constant returns an error. + LK_NBLCK Locks the specified bytes. If the bytes cannot be locked, the constant returns an error. + LK_NBRLCK Same as _LK_NBLCK. + LK_RLCK Same as _LK_LOCK. + LK_UNLCK Unlocks the specified bytes, which must have been previously locked. + + Regions should be locked only briefly and should be unlocked before closing a file or exiting the program. + + http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt__locking.asp + */ + int lkmode; + switch (*mode) { + case 'r': lkmode = LK_NBLCK; break; + case 'w': lkmode = LK_NBLCK; break; + case 'u': lkmode = LK_UNLCK; break; + default : return luaL_error (L, "%s: invalid mode", funcname); + } + if (!len) { + fseek (fh, 0L, SEEK_END); + len = ftell (fh); + } + fseek (fh, start, SEEK_SET); +#ifdef __BORLANDC__ + code = locking (fileno(fh), lkmode, len); +#else + code = _locking (fileno(fh), lkmode, len); +#endif +#else + struct flock f; + switch (*mode) { + case 'w': f.l_type = F_WRLCK; break; + case 'r': f.l_type = F_RDLCK; break; + case 'u': f.l_type = F_UNLCK; break; + default : return luaL_error (L, "%s: invalid mode", funcname); + } + f.l_whence = SEEK_SET; + f.l_start = (off_t)start; + f.l_len = (off_t)len; + code = fcntl (fileno(fh), F_SETLK, &f); +#endif + return (code != -1); +} + +#ifdef _WIN32 +typedef struct lfs_Lock { + HANDLE fd; +} lfs_Lock; +static int lfs_lock_dir(lua_State *L) { + size_t pathl; HANDLE fd; + lfs_Lock *lock; + char *ln; + const char *lockfile = "/lockfile.lfs"; + const char *path = luaL_checklstring(L, 1, &pathl); + ln = (char*)malloc(pathl + strlen(lockfile) + 1); + if(!ln) { + lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2; + } + strcpy(ln, path); strcat(ln, lockfile); + if((fd = CreateFile(ln, GENERIC_WRITE, 0, NULL, CREATE_NEW, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL)) == INVALID_HANDLE_VALUE) { + int en = GetLastError(); + free(ln); lua_pushnil(L); + if(en == ERROR_FILE_EXISTS || en == ERROR_SHARING_VIOLATION) + lua_pushstring(L, "File exists"); + else + lua_pushstring(L, strerror(en)); + return 2; + } + free(ln); + lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock)); + lock->fd = fd; + luaL_getmetatable (L, LOCK_METATABLE); + lua_setmetatable (L, -2); + return 1; +} +static int lfs_unlock_dir(lua_State *L) { + lfs_Lock *lock = (lfs_Lock *)luaL_checkudata(L, 1, LOCK_METATABLE); + if(lock->fd != INVALID_HANDLE_VALUE) { + CloseHandle(lock->fd); + lock->fd=INVALID_HANDLE_VALUE; + } + return 0; +} +#else +typedef struct lfs_Lock { + char *ln; +} lfs_Lock; +static int lfs_lock_dir(lua_State *L) { + lfs_Lock *lock; + size_t pathl; + char *ln; + const char *lockfile = "/lockfile.lfs"; + const char *path = luaL_checklstring(L, 1, &pathl); + lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock)); + ln = (char*)malloc(pathl + strlen(lockfile) + 1); + if(!ln) { + lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2; + } + strcpy(ln, path); strcat(ln, lockfile); + if(symlink("lock", ln) == -1) { + free(ln); lua_pushnil(L); + lua_pushstring(L, strerror(errno)); return 2; + } + lock->ln = ln; + luaL_getmetatable (L, LOCK_METATABLE); + lua_setmetatable (L, -2); + return 1; +} +static int lfs_unlock_dir(lua_State *L) { + lfs_Lock *lock = (lfs_Lock *)luaL_checkudata(L, 1, LOCK_METATABLE); + if(lock->ln) { + unlink(lock->ln); + free(lock->ln); + lock->ln = NULL; + } + return 0; +} +#endif + +static int lfs_g_setmode (lua_State *L, FILE *f, int arg) { + static const int mode[] = {_O_BINARY, _O_TEXT}; + static const char *const modenames[] = {"binary", "text", NULL}; + int op = luaL_checkoption(L, arg, NULL, modenames); + int res = lfs_setmode(f, mode[op]); + if (res != -1) { + int i; + lua_pushboolean(L, 1); + for (i = 0; modenames[i] != NULL; i++) { + if (mode[i] == res) { + lua_pushstring(L, modenames[i]); + return 2; + } + } + lua_pushnil(L); + return 2; + } else { + return pusherror(L, NULL); + } +} + +static int lfs_f_setmode(lua_State *L) { + return lfs_g_setmode(L, check_file(L, 1, "setmode"), 2); +} + +/* +** Locks a file. +** @param #1 File handle. +** @param #2 String with lock mode ('w'rite, 'r'ead). +** @param #3 Number with start position (optional). +** @param #4 Number with length (optional). +*/ +static int file_lock (lua_State *L) { + FILE *fh = check_file (L, 1, "lock"); + const char *mode = luaL_checkstring (L, 2); + const long start = (long) luaL_optinteger (L, 3, 0); + long len = (long) luaL_optinteger (L, 4, 0); + if (_file_lock (L, fh, mode, start, len, "lock")) { + lua_pushboolean (L, 1); + return 1; + } else { + lua_pushnil (L); + lua_pushfstring (L, "%s", strerror(errno)); + return 2; + } +} + + +/* +** Unlocks a file. +** @param #1 File handle. +** @param #2 Number with start position (optional). +** @param #3 Number with length (optional). +*/ +static int file_unlock (lua_State *L) { + FILE *fh = check_file (L, 1, "unlock"); + const long start = (long) luaL_optinteger (L, 2, 0); + long len = (long) luaL_optinteger (L, 3, 0); + if (_file_lock (L, fh, "u", start, len, "unlock")) { + lua_pushboolean (L, 1); + return 1; + } else { + lua_pushnil (L); + lua_pushfstring (L, "%s", strerror(errno)); + return 2; + } +} + + +/* +** Creates a link. +** @param #1 Object to link to. +** @param #2 Name of link. +** @param #3 True if link is symbolic (optional). +*/ +static int make_link (lua_State *L) { +#ifndef _WIN32 + const char *oldpath = luaL_checkstring(L, 1); + const char *newpath = luaL_checkstring(L, 2); + int res = (lua_toboolean(L,3) ? symlink : link)(oldpath, newpath); + if (res == -1) { + return pusherror(L, NULL); + } else { + lua_pushinteger(L, 0); + return 1; + } +#else + errno = ENOSYS; /* = "Function not implemented" */ + return pushresult(L, -1, "make_link is not supported on Windows"); +#endif +} + + +/* +** Creates a directory. +** @param #1 Directory path. +*/ +static int make_dir (lua_State *L) { + const char *path = luaL_checkstring(L, 1); + return pushresult(L, lfs_mkdir(path), NULL); +} + + +/* +** Removes a directory. +** @param #1 Directory path. +*/ +static int remove_dir (lua_State *L) { + const char *path = luaL_checkstring(L, 1); + return pushresult(L, rmdir(path), NULL); +} + + +/* +** Directory iterator +*/ +static int dir_iter (lua_State *L) { +#ifdef _WIN32 + struct _finddata_t c_file; +#else + struct dirent *entry; +#endif + dir_data *d = (dir_data *)luaL_checkudata (L, 1, DIR_METATABLE); + luaL_argcheck (L, d->closed == 0, 1, "closed directory"); +#ifdef _WIN32 + if (d->hFile == 0L) { /* first entry */ + if ((d->hFile = _findfirst (d->pattern, &c_file)) == -1L) { + lua_pushnil (L); + lua_pushstring (L, strerror (errno)); + d->closed = 1; + return 2; + } else { + lua_pushstring (L, c_file.name); + return 1; + } + } else { /* next entry */ + if (_findnext (d->hFile, &c_file) == -1L) { + /* no more entries => close directory */ + _findclose (d->hFile); + d->closed = 1; + return 0; + } else { + lua_pushstring (L, c_file.name); + return 1; + } + } +#else + if ((entry = readdir (d->dir)) != NULL) { + lua_pushstring (L, entry->d_name); + return 1; + } else { + /* no more entries => close directory */ + closedir (d->dir); + d->closed = 1; + return 0; + } +#endif +} + + +/* +** Closes directory iterators +*/ +static int dir_close (lua_State *L) { + dir_data *d = (dir_data *)lua_touserdata (L, 1); +#ifdef _WIN32 + if (!d->closed && d->hFile) { + _findclose (d->hFile); + } +#else + if (!d->closed && d->dir) { + closedir (d->dir); + } +#endif + d->closed = 1; + return 0; +} + + +/* +** Factory of directory iterators +*/ +static int dir_iter_factory (lua_State *L) { + const char *path = luaL_checkstring (L, 1); + dir_data *d; + lua_pushcfunction (L, dir_iter); + d = (dir_data *) lua_newuserdata (L, sizeof(dir_data)); + luaL_getmetatable (L, DIR_METATABLE); + lua_setmetatable (L, -2); + d->closed = 0; +#ifdef _WIN32 + d->hFile = 0L; + if (strlen(path) > MAX_PATH-2) + luaL_error (L, "path too long: %s", path); + else + sprintf (d->pattern, "%s/*", path); +#else + d->dir = opendir (path); + if (d->dir == NULL) + luaL_error (L, "cannot open %s: %s", path, strerror (errno)); +#endif + return 2; +} + + +/* +** Creates directory metatable. +*/ +static int dir_create_meta (lua_State *L) { + luaL_newmetatable (L, DIR_METATABLE); + + /* Method table */ + lua_newtable(L); + lua_pushcfunction (L, dir_iter); + lua_setfield(L, -2, "next"); + lua_pushcfunction (L, dir_close); + lua_setfield(L, -2, "close"); + + /* Metamethods */ + lua_setfield(L, -2, "__index"); + lua_pushcfunction (L, dir_close); + lua_setfield (L, -2, "__gc"); + return 1; +} + + +/* +** Creates lock metatable. +*/ +static int lock_create_meta (lua_State *L) { + luaL_newmetatable (L, LOCK_METATABLE); + + /* Method table */ + lua_newtable(L); + lua_pushcfunction(L, lfs_unlock_dir); + lua_setfield(L, -2, "free"); + + /* Metamethods */ + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, lfs_unlock_dir); + lua_setfield(L, -2, "__gc"); + return 1; +} + + +#ifdef _WIN32 + #ifndef S_ISDIR + #define S_ISDIR(mode) (mode&_S_IFDIR) + #endif + #ifndef S_ISREG + #define S_ISREG(mode) (mode&_S_IFREG) + #endif + #ifndef S_ISLNK + #define S_ISLNK(mode) (0) + #endif + #ifndef S_ISSOCK + #define S_ISSOCK(mode) (0) + #endif + #ifndef S_ISFIFO + #define S_ISFIFO(mode) (0) + #endif + #ifndef S_ISCHR + #define S_ISCHR(mode) (mode&_S_IFCHR) + #endif + #ifndef S_ISBLK + #define S_ISBLK(mode) (0) + #endif +#endif +/* +** Convert the inode protection mode to a string. +*/ +#ifdef _WIN32 +static const char *mode2string (unsigned short mode) { +#else +static const char *mode2string (mode_t mode) { +#endif + if ( S_ISREG(mode) ) + return "file"; + else if ( S_ISDIR(mode) ) + return "directory"; + else if ( S_ISLNK(mode) ) + return "link"; + else if ( S_ISSOCK(mode) ) + return "socket"; + else if ( S_ISFIFO(mode) ) + return "named pipe"; + else if ( S_ISCHR(mode) ) + return "char device"; + else if ( S_ISBLK(mode) ) + return "block device"; + else + return "other"; +} + + +/* +** Set access time and modification values for a file. +** @param #1 File path. +** @param #2 Access time in seconds, current time is used if missing. +** @param #3 Modification time in seconds, access time is used if missing. +*/ +static int file_utime (lua_State *L) { + const char *file = luaL_checkstring(L, 1); + struct utimbuf utb, *buf; + + if (lua_gettop (L) == 1) /* set to current date/time */ + buf = NULL; + else { + utb.actime = (time_t) luaL_optnumber(L, 2, 0); + utb.modtime = (time_t) luaL_optinteger(L, 3, utb.actime); + buf = &utb; + } + + return pushresult(L, utime(file, buf), NULL); +} + + +/* inode protection mode */ +static void push_st_mode (lua_State *L, STAT_STRUCT *info) { + lua_pushstring (L, mode2string (info->st_mode)); +} +/* device inode resides on */ +static void push_st_dev (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer) info->st_dev); +} +/* inode's number */ +static void push_st_ino (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer) info->st_ino); +} +/* number of hard links to the file */ +static void push_st_nlink (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer)info->st_nlink); +} +/* user-id of owner */ +static void push_st_uid (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer)info->st_uid); +} +/* group-id of owner */ +static void push_st_gid (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer)info->st_gid); +} +/* device type, for special file inode */ +static void push_st_rdev (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer) info->st_rdev); +} +/* time of last access */ +static void push_st_atime (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer) info->st_atime); +} +/* time of last data modification */ +static void push_st_mtime (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer) info->st_mtime); +} +/* time of last file status change */ +static void push_st_ctime (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer) info->st_ctime); +} +/* file size, in bytes */ +static void push_st_size (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer)info->st_size); +} +#ifndef _WIN32 +/* blocks allocated for file */ +static void push_st_blocks (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer)info->st_blocks); +} +/* optimal file system I/O blocksize */ +static void push_st_blksize (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer)info->st_blksize); +} +#endif + + /* +** Convert the inode protection mode to a permission list. +*/ + +#ifdef _WIN32 +static const char *perm2string (unsigned short mode) { + static char perms[10] = "---------"; + int i; + for (i=0;i<9;i++) perms[i]='-'; + if (mode & _S_IREAD) + { perms[0] = 'r'; perms[3] = 'r'; perms[6] = 'r'; } + if (mode & _S_IWRITE) + { perms[1] = 'w'; perms[4] = 'w'; perms[7] = 'w'; } + if (mode & _S_IEXEC) + { perms[2] = 'x'; perms[5] = 'x'; perms[8] = 'x'; } + return perms; +} +#else +static const char *perm2string (mode_t mode) { + static char perms[10] = "---------"; + int i; + for (i=0;i<9;i++) perms[i]='-'; + if (mode & S_IRUSR) perms[0] = 'r'; + if (mode & S_IWUSR) perms[1] = 'w'; + if (mode & S_IXUSR) perms[2] = 'x'; + if (mode & S_IRGRP) perms[3] = 'r'; + if (mode & S_IWGRP) perms[4] = 'w'; + if (mode & S_IXGRP) perms[5] = 'x'; + if (mode & S_IROTH) perms[6] = 'r'; + if (mode & S_IWOTH) perms[7] = 'w'; + if (mode & S_IXOTH) perms[8] = 'x'; + return perms; +} +#endif + +/* permssions string */ +static void push_st_perm (lua_State *L, STAT_STRUCT *info) { + lua_pushstring (L, perm2string (info->st_mode)); +} + +typedef void (*_push_function) (lua_State *L, STAT_STRUCT *info); + +struct _stat_members { + const char *name; + _push_function push; +}; + +struct _stat_members members[] = { + { "mode", push_st_mode }, + { "dev", push_st_dev }, + { "ino", push_st_ino }, + { "nlink", push_st_nlink }, + { "uid", push_st_uid }, + { "gid", push_st_gid }, + { "rdev", push_st_rdev }, + { "access", push_st_atime }, + { "modification", push_st_mtime }, + { "change", push_st_ctime }, + { "size", push_st_size }, + { "permissions", push_st_perm }, +#ifndef _WIN32 + { "blocks", push_st_blocks }, + { "blksize", push_st_blksize }, +#endif + { NULL, NULL } +}; + +/* +** Get file or symbolic link information +*/ +static int _file_info_ (lua_State *L, int (*st)(const char*, STAT_STRUCT*)) { + STAT_STRUCT info; + const char *file = luaL_checkstring (L, 1); + int i; + + if (st(file, &info)) { + lua_pushnil(L); + lua_pushfstring(L, "cannot obtain information from file '%s': %s", file, strerror(errno)); + lua_pushinteger(L, errno); + return 3; + } + if (lua_isstring (L, 2)) { + const char *member = lua_tostring (L, 2); + for (i = 0; members[i].name; i++) { + if (strcmp(members[i].name, member) == 0) { + /* push member value and return */ + members[i].push (L, &info); + return 1; + } + } + /* member not found */ + return luaL_error(L, "invalid attribute name '%s'", member); + } + /* creates a table if none is given, removes extra arguments */ + lua_settop(L, 2); + if (!lua_istable (L, 2)) { + lua_newtable (L); + } + /* stores all members in table on top of the stack */ + for (i = 0; members[i].name; i++) { + lua_pushstring (L, members[i].name); + members[i].push (L, &info); + lua_rawset (L, -3); + } + return 1; +} + + +/* +** Get file information using stat. +*/ +static int file_info (lua_State *L) { + return _file_info_ (L, STAT_FUNC); +} + + +/* +** Push the symlink target to the top of the stack. +** Assumes the file name is at position 1 of the stack. +** Returns 1 if successful (with the target on top of the stack), +** 0 on failure (with stack unchanged, and errno set). +*/ +static int push_link_target(lua_State *L) { +#ifdef _WIN32 + errno = ENOSYS; + return 0; +#else + const char *file = luaL_checkstring(L, 1); + char *target = NULL; + int tsize, size = 256; /* size = initial buffer capacity */ + while (1) { + char* target2 = realloc(target, size); + if (!target2) { /* failed to allocate */ + free(target); + return 0; + } + target = target2; + tsize = readlink(file, target, size); + if (tsize < 0) { /* a readlink() error occurred */ + free(target); + return 0; + } + if (tsize < size) + break; + /* possibly truncated readlink() result, double size and retry */ + size *= 2; + } + target[tsize] = '\0'; + lua_pushlstring(L, target, tsize); + free(target); + return 1; +#endif +} + +/* +** Get symbolic link information using lstat. +*/ +static int link_info (lua_State *L) { + int ret; + if (lua_isstring (L, 2) && (strcmp(lua_tostring(L, 2), "target") == 0)) { + int ok = push_link_target(L); + return ok ? 1 : pusherror(L, "could not obtain link target"); + } + ret = _file_info_ (L, LSTAT_FUNC); + if (ret == 1 && lua_type(L, -1) == LUA_TTABLE) { + int ok = push_link_target(L); + if (ok) { + lua_setfield(L, -2, "target"); + } + } + return ret; +} + + +/* +** Assumes the table is on top of the stack. +*/ +static void set_info (lua_State *L) { + lua_pushliteral(L, "Copyright (C) 2003-2017 Kepler Project"); + lua_setfield(L, -2, "_COPYRIGHT"); + lua_pushliteral(L, "LuaFileSystem is a Lua library developed to complement the set of functions related to file systems offered by the standard Lua distribution"); + lua_setfield(L, -2, "_DESCRIPTION"); + lua_pushliteral(L, "LuaFileSystem " LFS_VERSION); + lua_setfield(L, -2, "_VERSION"); +} + + +static const struct luaL_Reg fslib[] = { + {"attributes", file_info}, + {"chdir", change_dir}, + {"currentdir", get_dir}, + {"dir", dir_iter_factory}, + {"link", make_link}, + {"lock", file_lock}, + {"mkdir", make_dir}, + {"rmdir", remove_dir}, + {"symlinkattributes", link_info}, + {"setmode", lfs_f_setmode}, + {"touch", file_utime}, + {"unlock", file_unlock}, + {"lock_dir", lfs_lock_dir}, + {NULL, NULL}, +}; + +LFS_EXPORT int luaopen_lfs (lua_State *L) { + dir_create_meta (L); + lock_create_meta (L); + new_lib (L, fslib); + lua_pushvalue(L, -1); + lua_setglobal(L, LFS_LIBNAME); + set_info (L); + return 1; +} diff --git a/luaclib/src/lfs/lfs.h b/luaclib/src/lfs/lfs.h new file mode 100644 index 00000000..45875640 --- /dev/null +++ b/luaclib/src/lfs/lfs.h @@ -0,0 +1,34 @@ +/* +** LuaFileSystem +** Copyright Kepler Project 2003 - 2017 (http://keplerproject.github.io/luafilesystem) +*/ + +/* Define 'chdir' for systems that do not implement it */ +#ifdef NO_CHDIR + #define chdir(p) (-1) + #define chdir_error "Function 'chdir' not provided by system" +#else + #define chdir_error strerror(errno) +#endif + +#ifdef _WIN32 + #define chdir(p) (_chdir(p)) + #define getcwd(d, s) (_getcwd(d, s)) + #define rmdir(p) (_rmdir(p)) + #define LFS_EXPORT __declspec (dllexport) + #ifndef fileno + #define fileno(f) (_fileno(f)) + #endif +#else + #define LFS_EXPORT +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +LFS_EXPORT int luaopen_lfs (lua_State *L); + +#ifdef __cplusplus +} +#endif From 0099a707c984655652a7f34ebf295d85d67fa125 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 24 Jul 2019 11:52:24 +0800 Subject: [PATCH 247/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0lfs=E5=BA=93=E6=94=AF?= =?UTF-8?q?=E6=8C=81,=20=E5=A2=9E=E5=8A=A0test=5Flfs=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_lfs.lua | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 script/test_lfs.lua diff --git a/script/test_lfs.lua b/script/test_lfs.lua new file mode 100644 index 00000000..0afc2716 --- /dev/null +++ b/script/test_lfs.lua @@ -0,0 +1,37 @@ +local Log = require "logging":new() + +local lfs = require "lfs" + +-- Log:DEBUG(lfs) + +local function list_logs_files () + local logs = {} + for filename in lfs.dir("logs") do + -- mode为"directory"表示为目录, mode为"file"表示文件 + if lfs.attributes(filename).mode == "file" then + logs[#logs+1] = filename + end + end + return logs +end + +local function change_dir (dir) + local old = "将当前目录路径["..lfs.currentdir().."]修改为" + lfs.chdir(lfs.currentdir()..dir) + local new = "["..lfs.currentdir().."]" + return old..new +end + +Log:DEBUG("lfs版本为:"..lfs._VERSION) + +Log:DEBUG(change_dir("/src")) + +Log:DEBUG(change_dir("/../")) + +Log:DEBUG("查看LICENSE文件属性:", lfs.attributes("LICENSE")) + +Log:DEBUG("创建test文件夹:", lfs.mkdir("test")) + +Log:DEBUG("删除test文件夹:", lfs.rmdir("test")) + +Log:DEBUG("列出logs文件夹目录", list_logs_files()) From b5492a449a3cf979d7db8a8a68b2e909101e47ce Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 24 Jul 2019 12:23:06 +0800 Subject: [PATCH 248/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_lfs.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/test_lfs.lua b/script/test_lfs.lua index 0afc2716..12920693 100644 --- a/script/test_lfs.lua +++ b/script/test_lfs.lua @@ -8,7 +8,7 @@ local function list_logs_files () local logs = {} for filename in lfs.dir("logs") do -- mode为"directory"表示为目录, mode为"file"表示文件 - if lfs.attributes(filename).mode == "file" then + if lfs.attributes("logs".."/"..filename).mode == "file" then logs[#logs+1] = filename end end From 691d68af15e56cfe45d8cd4658a4ab9f7391d577 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 24 Jul 2019 12:35:39 +0800 Subject: [PATCH 249/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0lfs=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_lfs.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/test_lfs.lua b/script/test_lfs.lua index 12920693..e4a4e050 100644 --- a/script/test_lfs.lua +++ b/script/test_lfs.lua @@ -24,7 +24,7 @@ end Log:DEBUG("lfs版本为:"..lfs._VERSION) -Log:DEBUG(change_dir("/src")) +Log:DEBUG(change_dir("/script")) Log:DEBUG(change_dir("/../")) From 160e1092d428dd20222adf87604b091867f28857 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 26 Jul 2019 10:52:26 +0800 Subject: [PATCH 250/956] =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E5=AE=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lfs/lfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/luaclib/src/lfs/lfs.c b/luaclib/src/lfs/lfs.c index f510ab5a..13a18967 100644 --- a/luaclib/src/lfs/lfs.c +++ b/luaclib/src/lfs/lfs.c @@ -221,7 +221,7 @@ static FILE *check_file (lua_State *L, int idx, const char *funcname) { return 0; } else return *fh; -#elif LUA_VERSION_NUM >= 502 && LUA_VERSION_NUM <= 503 +#elif LUA_VERSION_NUM >= 502 luaL_Stream *fh = (luaL_Stream *)luaL_checkudata (L, idx, "FILE*"); if (fh->closef == 0 || fh->f == NULL) { luaL_error (L, "%s: closed file", funcname); @@ -318,7 +318,7 @@ static int lfs_lock_dir(lua_State *L) { } static int lfs_unlock_dir(lua_State *L) { lfs_Lock *lock = (lfs_Lock *)luaL_checkudata(L, 1, LOCK_METATABLE); - if(lock->fd != INVALID_HANDLE_VALUE) { + if(lock->fd != INVALID_HANDLE_VALUE) { CloseHandle(lock->fd); lock->fd=INVALID_HANDLE_VALUE; } From 67a77d562a00ac53105a253ab023fd33158d4888 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 29 Jul 2019 16:43:10 +0800 Subject: [PATCH 251/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0prepare=E8=AF=AD?= =?UTF-8?q?=E5=8F=A5=E4=B8=8Eexecute=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/DB/init.lua | 64 +++++++++++++++++++++++++++++++++++++-- lualib/protocol/mysql.lua | 10 ++++-- 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index e162c582..ac68e2c9 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -3,6 +3,7 @@ local timer = require "internal.Timer" local co = require "internal.Co" local class = require "class" local log = require "logging" +local crypt = require "crypt" local Log = log:new({ dump = true, path = 'DB'}) local co_self = co.self @@ -10,10 +11,12 @@ local co_wait = co.wait local co_wakeup = co.wakeup local type = type +local pairs = pairs +local ipairs = ipairs +local assert = assert +local select = select local tostring = tostring local tonumber = tonumber -local assert = assert -local ipairs = ipairs local rep = string.rep local find = string.find @@ -27,6 +30,8 @@ local remove = table.remove local concat = table.concat local unpack = table.unpack +local randomkey = crypt.randomkey + local SELECT = "SELECT" local INSERT = "INSERT INTO" @@ -86,6 +91,9 @@ local function DB_CREATE (opt) db:query(fmt('SET GLOBAL wait_timeout=%s', WAIT_TIMEOUT)) db:query(fmt('SET GLOBAL interactive_timeout=%s', WAIT_TIMEOUT)) end + if opt.precomp then + return opt:reprepare() + end return db end @@ -444,6 +452,58 @@ function DB:delete(table_name) } end +-- 重新执行编译 +function DB:reprepare () + for rkey, stmt in pairs(self.prepare) do + assert(self:query(stmt), "["..stmt.."] 预编译失败.") + end + return true +end + +-- PREPARE +function DB:prepare (sql) + if type(sql) ~= 'string' or sql == '' then + return nil, "试图传递一个无效的SQL语句" + end + if not self.precomp then + self.precomp = {} + end + local rkey = randomkey(true) + local stmt = fmt([[PREPARE %s FROM "%s"]], rkey, sql) + assert(self:query(stmt), "["..sql.."] 预编译失败.") + self.precomp[rkey] = stmt + return rkey +end + +-- EXECUTE +function DB:execute (rkey, ...) + if not self.precomp then + return nil, "尚未有任何预编译语句" + end + local stmt = self.precomp[rkey] + if not stmt then + return nil, "找不到这个预编译语句." + end + local qua = select("#", ...) + if qua <= 0 then + return self:query([[ EXECUTE ]]..rkey) + end + local arg_keys = {} + local arg_key = "@cf_args" + local arg_values = {...} + local req1 = {} + for q = 1, qua do + local key = arg_key..q + local value = arg_values[q]:gsub("'", "\\'") + arg_keys[#arg_keys+1] = key + req1[#req1+1] = concat({key, "=", "'", value, "'"}) + -- Log:DEBUG(key, value) + end + -- Log:DEBUG(stmt) + -- Log:DEBUG(concat({"SET ", concat(req1, ", "), ";", " EXECUTE ", rkey, " USING ", concat(arg_keys, ", "), ";"})) + return self:query(concat({"SET ", concat(req1, ", "), ";", " EXECUTE ", rkey, " USING ", concat(arg_keys, ", "), ";"})) +end + -- 原始查询语句 function DB:query(query) if not self.INITIALIZATION then diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua index d43fba58..ba091bfb 100644 --- a/lualib/protocol/mysql.lua +++ b/lualib/protocol/mysql.lua @@ -652,9 +652,15 @@ function MySQL.query(self, query, est_nrows) local ok = self:send_query(query) if not ok then self.state = nil - return nil, 'connection already close' + return nil, 'connection already close. 1' end - return self:read_result(est_nrows) + while 1 do + local ok, ret = self:read_result(est_nrows) + if not ok or ret ~= 'again' then + return ok, ret + end + end + -- return self:read_result(est_nrows) end function MySQL.set_compact_arrays(self, value) From eaa1ac9c77b5a7d45288b20238ec5828007db3d6 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 30 Jul 2019 10:52:48 +0800 Subject: [PATCH 252/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=B2=BE=E7=AE=80?= =?UTF-8?q?=E7=89=88=E7=9A=84ffi=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/Makefile | 2 + luaclib/src/lffi/ctype.c | 261 ++++ luaclib/src/lffi/ffi.c | 2933 +++++++++++++++++++++++++++++++++++++ luaclib/src/lffi/ffi.h | 435 ++++++ luaclib/src/lffi/makefile | 27 + luaclib/src/lffi/parser.c | 2614 +++++++++++++++++++++++++++++++++ 6 files changed, 6272 insertions(+) create mode 100644 luaclib/src/lffi/ctype.c create mode 100644 luaclib/src/lffi/ffi.c create mode 100644 luaclib/src/lffi/ffi.h create mode 100644 luaclib/src/lffi/makefile create mode 100644 luaclib/src/lffi/parser.c diff --git a/luaclib/Makefile b/luaclib/Makefile index 5fca2178..43cc5c0f 100644 --- a/luaclib/Makefile +++ b/luaclib/Makefile @@ -21,6 +21,7 @@ build : $(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore ### 以下为内置第三方库编译位置 ### cd src/lfs && rm -rf *.o *.so && make build # 增加lfs库 + cd src/lffi && rm -rf *.o *.so && make build # 增加lffi库 cd src/lpbc && rm -rf *.o *.so && make build # 增加lpbc库 cd src/lpeg && rm -rf *.o *.so && make build # 增加lpeg库 cd src/lcrypt && rm -rf *.o *.so && make build # 增加lcrypt库 @@ -36,6 +37,7 @@ rebuild : $(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore ### 以下为内置第三方库编译位置 ### cd src/lfs && rm -rf *.o *.so && make build # 增加lfs库 + cd src/lffi && rm -rf *.o *.so && make build # 增加lffi库 cd src/lpbc && rm -rf *.o *.so && make build # 增加lpbc库 cd src/lpeg && rm -rf *.o *.so && make build # 增加lpeg库 cd src/lcrypt && rm -rf *.o *.so && make build # 增加lcrypt库 diff --git a/luaclib/src/lffi/ctype.c b/luaclib/src/lffi/ctype.c new file mode 100644 index 00000000..27186f57 --- /dev/null +++ b/luaclib/src/lffi/ctype.c @@ -0,0 +1,261 @@ +/* vim: ts=4 sw=4 sts=4 et tw=78 + * Portions copyright (c) 2015-present, Facebook, Inc. All rights reserved. + * Portions copyright (c) 2011 James R. McKaskill. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "ffi.h" + +static int to_define_key; + +static void update_on_definition(lua_State* L, int ct_usr, int ct_idx) +{ + ct_usr = lua_absindex(L, ct_usr); + ct_idx = lua_absindex(L, ct_idx); + + lua_pushlightuserdata(L, &to_define_key); + lua_rawget(L, ct_usr); + + if (lua_isnil(L, -1)) { + lua_pop(L, 1); /* pop the nil */ + + /* {} */ + lua_newtable(L); + + /* {__mode='k'} */ + lua_newtable(L); + lua_pushliteral(L, "k"); + lua_setfield(L, -2, "__mode"); + + /* setmetatable({}, {__mode='k'}) */ + lua_setmetatable(L, -2); + + /* usr[TO_UPDATE_KEY] = setmetatable({}, {__mode='k'}) */ + lua_pushlightuserdata(L, &to_define_key); + lua_pushvalue(L, -2); + lua_rawset(L, ct_usr); + + /* leave the table on the stack */ + } + + /* to_update[ctype or cdata] = true */ + lua_pushvalue(L, ct_idx); + lua_pushboolean(L, 1); + lua_rawset(L, -3); + + /* pop the to_update table */ + lua_pop(L, 1); +} + +void set_defined(lua_State* L, int ct_usr, struct ctype* ct) +{ + ct_usr = lua_absindex(L, ct_usr); + + ct->is_defined = 1; + + /* update ctypes and cdatas that were created before the definition came in */ + lua_pushlightuserdata(L, &to_define_key); + lua_rawget(L, ct_usr); + + if (!lua_isnil(L, -1)) { + lua_pushnil(L); + + while (lua_next(L, -2)) { + struct ctype* upd = (struct ctype*) lua_touserdata(L, -2); + upd->base_size = ct->base_size; + upd->align_mask = ct->align_mask; + upd->is_defined = 1; + upd->is_variable_struct = ct->is_variable_struct; + upd->variable_increment = ct->variable_increment; + assert(!upd->variable_size_known); + lua_pop(L, 1); + } + + lua_pop(L, 1); + /* usr[TO_UPDATE_KEY] = nil */ + lua_pushlightuserdata(L, &to_define_key); + lua_pushnil(L); + lua_rawset(L, ct_usr); + } else { + lua_pop(L, 1); + } +} + +struct ctype* push_ctype(lua_State* L, int ct_usr, const struct ctype* ct) +{ + struct ctype* ret; + ct_usr = lua_absindex(L, ct_usr); + + ret = (struct ctype*) lua_newuserdata(L, sizeof(struct ctype)); + *ret = *ct; + + push_upval(L, &ctype_mt_key); + lua_setmetatable(L, -2); + +#if LUA_VERSION_NUM == 501 + if (!ct_usr || lua_isnil(L, ct_usr)) { + push_upval(L, &niluv_key); + lua_setfenv(L, -2); + } +#endif + + if (ct_usr && !lua_isnil(L, ct_usr)) { + lua_pushvalue(L, ct_usr); + lua_setuservalue(L, -2); + } + + if (!ct->is_defined && ct_usr && !lua_isnil(L, ct_usr)) { + update_on_definition(L, ct_usr, -1); + } + + return ret; +} + +size_t ctype_size(lua_State* L, const struct ctype* ct) +{ + if (ct->pointers - ct->is_array) { + return sizeof(void*) * (ct->is_array ? ct->array_size : 1); + + } else if (!ct->is_defined || ct->type == VOID_TYPE) { + return luaL_error(L, "can't calculate size of an undefined type"); + + } else if (ct->variable_size_known) { + assert(ct->is_variable_struct && !ct->is_array); + return ct->base_size + ct->variable_increment; + + } else if (ct->is_variable_array || ct->is_variable_struct) { + return luaL_error(L, "internal error: calc size of variable type with unknown size"); + + } else { + return ct->base_size * (ct->is_array ? ct->array_size : 1); + } +} + +void* push_cdata(lua_State* L, int ct_usr, const struct ctype* ct) +{ + struct cdata* cd; + size_t sz = ct->is_reference ? sizeof(void*) : ctype_size(L, ct); + ct_usr = lua_absindex(L, ct_usr); + + /* This is to stop valgrind from complaining. Bitfields are accessed in 8 + * byte chunks so that the code doesn't have to deal with different access + * patterns, but this means that occasionally it will read past the end of + * the struct. As its not setting the bits past the end (only reading and + * then writing the bits back) and the read is aligned its a non-issue, + * but valgrind complains nonetheless. + */ + if (ct->has_bitfield) { + sz = ALIGN_UP(sz, 7); + } + + cd = (struct cdata*) lua_newuserdata(L, sizeof(struct cdata) + sz); + *(struct ctype*) &cd->type = *ct; + memset(cd+1, 0, sz); + + /* TODO: handle cases where lua_newuserdata returns a pointer that is not + * aligned */ +#if 0 + assert((uintptr_t) (cd + 1) % 8 == 0); +#endif + +#if LUA_VERSION_NUM == 501 + if (!ct_usr || lua_isnil(L, ct_usr)) { + push_upval(L, &niluv_key); + lua_setfenv(L, -2); + } +#endif + + if (ct_usr && !lua_isnil(L, ct_usr)) { + lua_pushvalue(L, ct_usr); + lua_setuservalue(L, -2); + } + + push_upval(L, &cdata_mt_key); + lua_setmetatable(L, -2); + + if (!ct->is_defined && ct_usr && !lua_isnil(L, ct_usr)) { + update_on_definition(L, ct_usr, -1); + } + + return cd+1; +} + +/* returns the value as a ctype, pushes the user value onto the stack */ +void check_ctype(lua_State* L, int idx, struct ctype* ct) +{ + if (lua_isstring(L, idx)) { + struct parser P; + P.line = 1; + P.prev = P.next = lua_tostring(L, idx); + P.align_mask = DEFAULT_ALIGN_MASK; + parse_type(L, &P, ct); + parse_argument(L, &P, -1, ct, NULL, NULL); + lua_remove(L, -2); /* remove the user value from parse_type */ + + } else if (lua_getmetatable(L, idx)) { + if (!equals_upval(L, -1, &ctype_mt_key) + && !equals_upval(L, -1, &cdata_mt_key)) { + goto err; + } + + lua_pop(L, 1); /* pop the metatable */ + *ct = *(struct ctype*) lua_touserdata(L, idx); + lua_getuservalue(L, idx); + + } else { + goto err; + } + + return; + +err: + luaL_error(L, "expected cdata, ctype or string for arg #%d", idx); +} + +/* to_cdata returns the struct cdata* and pushes the user value onto the + * stack. If the index is not a ctype then ct is set to the zero value such + * that ct->type is INVALID_TYPE, a nil is pushed, and NULL is returned. */ +void* to_cdata(lua_State* L, int idx, struct ctype* ct) +{ + struct cdata* cd; + + memset(ct, 0, sizeof(struct ctype)); + if (!lua_isuserdata(L, idx) || !lua_getmetatable(L, idx)) { + lua_pushnil(L); + return NULL; + } + + if (!equals_upval(L, -1, &cdata_mt_key)) { + lua_pop(L, 1); /* mt */ + lua_pushnil(L); + return NULL; + } + + lua_pop(L, 1); /* mt */ + cd = (struct cdata*) lua_touserdata(L, idx); + *ct = cd->type; + lua_getuservalue(L, idx); + + if (ct->is_reference) { + return *(void**) (cd+1); + + } else if (ct->pointers && !ct->is_array) { + return *(void**) (cd+1); + + } else { + return cd + 1; + } +} + +/* check_cdata returns the struct cdata* and pushes the user value onto the + * stack. Also dereferences references. */ +void* check_cdata(lua_State* L, int idx, struct ctype* ct) +{ + void* p = to_cdata(L, idx, ct); + if (ct->type == INVALID_TYPE) { + luaL_error(L, "expected cdata for arg #%d", idx); + } + return p; +} diff --git a/luaclib/src/lffi/ffi.c b/luaclib/src/lffi/ffi.c new file mode 100644 index 00000000..4400d3bf --- /dev/null +++ b/luaclib/src/lffi/ffi.c @@ -0,0 +1,2933 @@ +/* vim: ts=4 sw=4 sts=4 et tw=78 + * Portions copyright (c) 2015-present, Facebook, Inc. All rights reserved. + * Portions copyright (c) 2011 James R. McKaskill. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "ffi.h" +#include +#include + +/* Set to 1 to get extra debugging on print */ +#define DEBUG_TOSTRING 0 + +int ctype_mt_key; +int cdata_mt_key; +int constants_key; +int types_key; +int gc_key; +int callbacks_key; +int functions_key; +int abi_key; +int next_unnamed_key; +int niluv_key; +int asmname_key; + +void push_upval(lua_State* L, int* key) +{ + lua_pushlightuserdata(L, key); + lua_rawget(L, LUA_REGISTRYINDEX); +} + +void set_upval(lua_State* L, int* key) +{ + lua_pushlightuserdata(L, key); + lua_insert(L, -2); + lua_rawset(L, LUA_REGISTRYINDEX); +} + +int equals_upval(lua_State* L, int idx, int* key) +{ + int ret; + lua_pushvalue(L, idx); + push_upval(L, key); + ret = lua_rawequal(L, -2, -1); + lua_pop(L, 2); + return ret; +} + +static int type_error(lua_State* L, int idx, const char* to_type, int to_usr, const struct ctype* to_ct) +{ + luaL_Buffer B; + struct ctype ft; + + assert(to_type || (to_usr && to_ct)); + if (to_usr) { + to_usr = lua_absindex(L, to_usr); + } + + idx = lua_absindex(L, idx); + + luaL_buffinit(L, &B); + to_cdata(L, idx, &ft); + + if (ft.type != INVALID_TYPE) { + push_type_name(L, -1, &ft); + lua_pushfstring(L, "unable to convert argument %d from cdata<%s> to cdata<", idx, lua_tostring(L, -1)); + lua_remove(L, -2); + luaL_addvalue(&B); + } else { + lua_pushfstring(L, "unable to convert argument %d from lua<%s> to cdata<", idx, luaL_typename(L, idx)); + luaL_addvalue(&B); + } + + if (to_ct) { + push_type_name(L, to_usr, to_ct); + luaL_addvalue(&B); + } else { + luaL_addstring(&B, to_type); + } + + luaL_addchar(&B, '>'); + + luaL_pushresult(&B); + return lua_error(L); +} + +static void* userdata_toptr(lua_State* L, int idx) +{ + void* ptr = lua_touserdata(L, idx); + + // check for FILE* + lua_getmetatable(L, idx); + luaL_getmetatable(L, LUA_FILEHANDLE); + int isfile = lua_rawequal(L, -1, -2); + lua_pop(L, 2); + + if (isfile) { +#if LUA_VERSION_NUM == 501 + FILE** stream = (FILE**) ptr; + return *stream; +#else + luaL_Stream* stream = (luaL_Stream*) ptr; + return stream->f; +#endif + } + + return ptr; +} + +static int cdata_tointeger(lua_State* L, int idx, ptrdiff_t* val) +{ + struct ctype ct; + void* addr = to_cdata(L, idx, &ct); + lua_pop(L, 1); + + if (ct.pointers) { + return 0; + } + + switch (ct.type) { + case INT8_TYPE: + *val = *(int8_t*)addr; + return 1; + case INT16_TYPE: + *val = *(int16_t*)addr; + return 1; + case INT32_TYPE: + *val = *(int32_t*)addr; + return 1; + case INT64_TYPE: + *val = *(int64_t*)addr; + return 1; + default: + return 0; + } +} + +static int64_t check_intptr(lua_State* L, int idx, void* p, struct ctype* ct) +{ + if (ct->type == INVALID_TYPE) { + int64_t ret; + memset(ct, 0, sizeof(*ct)); + ct->base_size = 8; + ct->type = INT64_TYPE; + ct->is_defined = 1; + ret = luaL_checknumber(L, idx); + return ret; + + } else if (ct->pointers) { + return (intptr_t) p; + } + + switch (ct->type) { + case INTPTR_TYPE: + case FUNCTION_PTR_TYPE: + return *(intptr_t*) p; + + case INT64_TYPE: + return *(int64_t*) p; + + case INT32_TYPE: + return ct->is_unsigned ? (int64_t) *(uint32_t*) p : (int64_t) *(int32_t*) p; + + case INT16_TYPE: + return ct->is_unsigned ? (int64_t) *(uint16_t*) p : (int64_t) *(int16_t*) p; + + case INT8_TYPE: + return ct->is_unsigned ? (int64_t) *(uint8_t*) p : (int64_t) *(int8_t*) p; + + default: + type_error(L, idx, "intptr_t", 0, NULL); + return 0; + } +} + +#define TO_NUMBER(TYPE, ALLOW_POINTERS, LUA_TONUMBER) \ + TYPE ret = 0; \ + void* p; \ + struct ctype ct; \ + \ + switch (lua_type(L, idx)) { \ + case LUA_TBOOLEAN: \ + ret = (TYPE) lua_toboolean(L, idx); \ + break; \ + \ + case LUA_TNUMBER: \ + ret = (TYPE) LUA_TONUMBER(L, idx); \ + break; \ + \ + case LUA_TSTRING: \ + if (!ALLOW_POINTERS) { \ + type_error(L, idx, #TYPE, 0, NULL); \ + } \ + ret = (TYPE) (intptr_t) lua_tostring(L, idx); \ + break; \ + \ + case LUA_TLIGHTUSERDATA: \ + if (!ALLOW_POINTERS) { \ + type_error(L, idx, #TYPE, 0, NULL); \ + } \ + ret = (TYPE) (intptr_t) lua_topointer(L, idx); \ + break; \ + \ + case LUA_TUSERDATA: \ + p = to_cdata(L, idx, &ct); \ + \ + if (ct.type == INVALID_TYPE) { \ + if (!ALLOW_POINTERS) { \ + type_error(L, idx, #TYPE, 0, NULL); \ + } \ + ret = (TYPE) (intptr_t) userdata_toptr(L, idx); \ + } else if (ct.pointers || ct.type == STRUCT_TYPE || ct.type == UNION_TYPE) {\ + if (!ALLOW_POINTERS) { \ + type_error(L, idx, #TYPE, 0, NULL); \ + } \ + ret = (TYPE) (intptr_t) p; \ + } else if (ct.type == COMPLEX_DOUBLE_TYPE) { \ + ret = (TYPE) creal(*(complex_double*) p); \ + } else if (ct.type == COMPLEX_FLOAT_TYPE) { \ + ret = (TYPE) crealf(*(complex_float*) p); \ + } else if (ct.type == DOUBLE_TYPE) { \ + ret = (TYPE) *(double*) p; \ + } else if (ct.type == FLOAT_TYPE) { \ + ret = (TYPE) *(float*) p; \ + } else { \ + ret = check_intptr(L, idx, p, &ct); \ + } \ + lua_pop(L, 1); \ + break; \ + \ + case LUA_TNIL: \ + ret = (TYPE) 0; \ + break; \ + \ + default: \ + type_error(L, idx, #TYPE, 0, NULL); \ + } \ + +static int64_t cast_int64(lua_State* L, int idx, int is_cast) +{ TO_NUMBER(int64_t, is_cast, lua_tointeger); return ret; } + +static uint64_t cast_uint64(lua_State* L, int idx, int is_cast) +{ TO_NUMBER(uint64_t, is_cast, lua_tointeger); return ret; } + +int32_t check_int32(lua_State* L, int idx) +{ return (int32_t) cast_int64(L, idx, 0); } + +uint32_t check_uint32(lua_State* L, int idx) +{ return (uint32_t) cast_uint64(L, idx, 0); } + +int64_t check_int64(lua_State* L, int idx) +{ return cast_int64(L, idx, 0); } + +uint64_t check_uint64(lua_State* L, int idx) +{ return cast_uint64(L, idx, 0); } + +double check_double(lua_State* L, int idx) +{ TO_NUMBER(double, 0, lua_tonumber); return ret; } + +float check_float(lua_State* L, int idx) +{ TO_NUMBER(double, 0, lua_tonumber); return ret; } + +uintptr_t check_uintptr(lua_State* L, int idx) +{ TO_NUMBER(uintptr_t, 1, lua_tointeger); return ret; } + +complex_double check_complex_double(lua_State* L, int idx) +{ + double real = 0, imag = 0; + void* p; + struct ctype ct; + + switch (lua_type(L, idx)) { + case LUA_TNUMBER: + real = (double) lua_tonumber(L, idx); + break; + case LUA_TTABLE: + lua_rawgeti(L, idx, 1); + real = check_double(L, -1); + lua_pop(L, 1); + + lua_rawgeti(L, idx, 2); + if (lua_isnil(L, -1)) { + imag = real; + } else { + imag = check_double(L, -1); + } + lua_pop(L, 1); + break; + case LUA_TUSERDATA: + p = to_cdata(L, idx, &ct); + if (ct.type == COMPLEX_DOUBLE_TYPE) { + real = creal(*(complex_double*) p); + imag = cimag(*(complex_double*) p); + } else if (ct.type == COMPLEX_FLOAT_TYPE) { + real = crealf(*(complex_float*) p); + imag = cimagf(*(complex_float*) p); + } else if (ct.type == DOUBLE_TYPE) { + real = *(double*) p; + } else if (ct.type == FLOAT_TYPE) { + real = *(float*) p; + } else { + real = check_intptr(L, idx, p, &ct); + } + lua_pop(L, 1); + break; + + default: + type_error(L, idx, "complex", 0, NULL); + } + + return mk_complex_double(real, imag); +} + +complex_float check_complex_float(lua_State* L, int idx) +{ + complex_double d = check_complex_double(L, idx); + return mk_complex_float(creal(d), cimag(d)); +} + +static size_t unpack_vararg(lua_State* L, int i, char* to) +{ + void* p; + struct ctype ct; + + switch (lua_type(L, i)) { + case LUA_TBOOLEAN: + *(int*) to = lua_toboolean(L, i); + return sizeof(int); + + case LUA_TNUMBER: + *(double*) to = lua_tonumber(L, i); // TODO in Lua 5.3: lua_tointeger sometimes should be here + return sizeof(double); + + case LUA_TSTRING: + *(const char**) to = lua_tostring(L, i); + return sizeof(const char*); + + case LUA_TLIGHTUSERDATA: + *(void**) to = lua_touserdata(L, i); + return sizeof(void*); + + case LUA_TUSERDATA: + p = to_cdata(L, i, &ct); + lua_pop(L, 1); + + if (ct.type == INVALID_TYPE) { + *(void**) to = userdata_toptr(L, i); + return sizeof(void*); + + } else if (ct.pointers || ct.type == INTPTR_TYPE) { + *(void**) to = p; + return sizeof(void*); + + } else if (ct.type == INT32_TYPE) { + *(int32_t*) to = *(int32_t*) p; + return sizeof(int32_t); + + } else if (ct.type == INT64_TYPE) { + *(int64_t*) to = *(int64_t*) p; + return sizeof(int64_t); + } + goto err; + + case LUA_TNIL: + *(void**) to = NULL; + return sizeof(void*); + + default: + goto err; + } + +err: + return type_error(L, i, "vararg", 0, NULL); +} + +void unpack_varargs_stack(lua_State* L, int first, int last, char* to) +{ + int i; + + for (i = first; i <= last; i++) { + to += unpack_vararg(L, i, to); + } +} + +void unpack_varargs_stack_skip(lua_State* L, int first, int last, int ints_to_skip, int floats_to_skip, char* to) +{ + int i; + + for (i = first; i <= last; i++) { + int type = lua_type(L, i); + + if (type == LUA_TNUMBER && --floats_to_skip >= 0) { + continue; + } else if (type != LUA_TNUMBER && --ints_to_skip >= 0) { + continue; + } + + to += unpack_vararg(L, i, to); + } +} + +void unpack_varargs_float(lua_State* L, int first, int last, int max, char* to) +{ + int i; + + for (i = first; i <= last && max > 0; i++) { + if (lua_type(L, i) == LUA_TNUMBER) { + unpack_vararg(L, i, to); + to += sizeof(double); + max--; + } + } +} + +void unpack_varargs_int(lua_State* L, int first, int last, int max, char* to) +{ + int i; + + for (i = first; i <= last && max > 0; i++) { + if (lua_type(L, i) != LUA_TNUMBER) { + unpack_vararg(L, i, to); + to += sizeof(void*); + max--; + } + } +} + +void unpack_varargs_reg(lua_State* L, int first, int last, char* to) +{ + int i; + + for (i = first; i <= last; i++) { + unpack_vararg(L, i, to); + to += sizeof(double); + } +} + +/* to_enum tries to convert a value at idx to the enum type indicated by to_ct + * and uv to_usr. For strings this means it will do a string lookup for the + * enum type. It leaves the stack unchanged. Will throw an error if the type + * at idx can't be conerted. + */ +int32_t check_enum(lua_State* L, int idx, int to_usr, const struct ctype* to_ct) +{ + int32_t ret; + + switch (lua_type(L, idx)) { + case LUA_TSTRING: + /* lookup string in to_usr to find value */ + to_usr = lua_absindex(L, to_usr); + lua_pushvalue(L, idx); + lua_rawget(L, to_usr); + + if (lua_isnil(L, -1)) { + goto err; + } + + ret = (int32_t) lua_tointeger(L, -1); + lua_pop(L, 1); + return ret; + + case LUA_TUSERDATA: + return check_int32(L, idx); + + case LUA_TNIL: + return (int32_t) 0; + + case LUA_TNUMBER: + return (int32_t) lua_tointeger(L, idx); + + default: + goto err; + } + +err: + return type_error(L, idx, NULL, to_usr, to_ct); +} + +/* to_pointer tries converts a value at idx to a pointer. It fills out ct and + * pushes the uv of the found type. It will throw a lua error if it can not + * convert the value to a pointer. */ +static void* check_pointer(lua_State* L, int idx, struct ctype* ct) +{ + void* p; + memset(ct, 0, sizeof(*ct)); + idx = lua_absindex(L, idx); + + switch (lua_type(L, idx)) { + case LUA_TNIL: + ct->type = VOID_TYPE; + ct->pointers = 1; + ct->is_null = 1; + lua_pushnil(L); + return NULL; + + case LUA_TNUMBER: + ct->type = INTPTR_TYPE; + ct->is_unsigned = 1; + ct->pointers = 0; + lua_pushnil(L); + return (void*) (uintptr_t) lua_tonumber(L, idx); // TODO in Lua 5.3: maybe change to lua_tointeger + + case LUA_TLIGHTUSERDATA: + ct->type = VOID_TYPE; + ct->pointers = 1; + lua_pushnil(L); + return lua_touserdata(L, idx); + + case LUA_TSTRING: + ct->type = INT8_TYPE; + ct->pointers = 1; + ct->is_unsigned = IS_CHAR_UNSIGNED; + ct->is_array = 1; + ct->base_size = 1; + ct->const_mask = 2; + lua_pushnil(L); + return (void*) lua_tolstring(L, idx, &ct->array_size); + + case LUA_TUSERDATA: + p = to_cdata(L, idx, ct); + + if (ct->type == INVALID_TYPE) { + /* some other type of user data */ + ct->type = VOID_TYPE; + ct->pointers = 1; + return userdata_toptr(L, idx); + } else if (ct->type == STRUCT_TYPE || ct->type == UNION_TYPE) { + return p; + } else { + return (void*) (intptr_t) check_intptr(L, idx, p, ct); + } + break; + } + + type_error(L, idx, "pointer", 0, NULL); + return NULL; +} + +static int is_void_ptr(const struct ctype* ct) +{ + return ct->type == VOID_TYPE + && ct->pointers == 1; +} + +static int is_same_type(lua_State* L, int usr1, int usr2, const struct ctype* t1, const struct ctype* t2) +{ + if (t1->type != t2->type) { + return 0; + } + +#if LUA_VERSION_NUM == 501 + if (lua_isnil(L, usr1) != lua_isnil(L, usr2)) { + int ret; + usr1 = lua_absindex(L, usr1); + usr2 = lua_absindex(L, usr2); + push_upval(L, &niluv_key); + + ret = lua_rawequal(L, usr1, -1) + || lua_rawequal(L, usr2, -1); + + lua_pop(L, 1); + + if (ret) { + return 1; + } + } +#endif + + return lua_rawequal(L, usr1, usr2); +} + +static void set_struct(lua_State* L, int idx, void* to, int to_usr, const struct ctype* tt, int check_pointers); + +/* to_typed_pointer converts a value at idx to a type tt with target uv to_usr + * checking all types. May push a temporary value so that it can create + * structs on the fly. */ +void* check_typed_pointer(lua_State* L, int idx, int to_usr, const struct ctype* tt) +{ + struct ctype ft; + void* p; + + to_usr = lua_absindex(L, to_usr); + idx = lua_absindex(L, idx); + + if (tt->pointers == 1 && (tt->type == STRUCT_TYPE || tt->type == UNION_TYPE) && lua_type(L, idx) == LUA_TTABLE) { + /* need to construct a struct of the target type */ + struct ctype ct = *tt; + ct.pointers = ct.is_array = 0; + p = push_cdata(L, to_usr, &ct); + set_struct(L, idx, p, to_usr, &ct, 1); + return p; + } + + p = check_pointer(L, idx, &ft); + + if (tt->pointers == 1 && ft.pointers == 0 && (ft.type == STRUCT_TYPE || ft.type == UNION_TYPE)) { + /* auto dereference structs */ + ft.pointers = 1; + ft.const_mask <<= 1; + } + + if (is_void_ptr(tt)) { + /* any pointer can convert to void* */ + goto suc; + + } else if (is_void_ptr(&ft) && (ft.pointers || ft.is_reference)) { + /* void* can convert to any pointer */ + goto suc; + + } else if (ft.is_null) { + /* NULL can convert to any pointer */ + goto suc; + + } else if (!is_same_type(L, to_usr, -1, tt, &ft)) { + /* the base type is different */ + goto err; + + } else if (tt->pointers != ft.pointers) { + goto err; + + } else if (ft.const_mask & ~tt->const_mask) { + /* for every const in from it must be in to, there are further rules + * for const casting (see the c++ spec), but they are hard to test + * quickly */ + goto err; + } + +suc: + return p; + +err: + type_error(L, idx, NULL, to_usr, tt); + return NULL; +} + +static void set_value(lua_State* L, int idx, void* to, int to_usr, const struct ctype* tt, int check_pointers); + +static void set_array(lua_State* L, int idx, void* to, int to_usr, const struct ctype* tt, int check_pointers) +{ + size_t i, sz, esz; + struct ctype et; + + idx = lua_absindex(L, idx); + to_usr = lua_absindex(L, to_usr); + + switch (lua_type(L, idx)) { + case LUA_TSTRING: + if (tt->pointers == 1 && tt->type == INT8_TYPE) { + const char* str = lua_tolstring(L, idx, &sz); + + if (!tt->is_variable_array && sz >= tt->array_size) { + memcpy(to, str, tt->array_size); + } else { + /* include nul terminator */ + memcpy(to, str, sz+1); + } + } else { + goto err; + } + break; + + case LUA_TTABLE: + et = *tt; + et.pointers--; + et.const_mask >>= 1; + et.is_array = 0; + esz = et.pointers ? sizeof(void*) : et.base_size; + + lua_rawgeti(L, idx, 2); + + if (tt->is_variable_array) { + /* we have no idea how big the array is, so set values based off + * how many items were given to us */ + lua_pop(L, 1); + for (i = 0; i < lua_rawlen(L, idx); i++) { + lua_rawgeti(L, idx, (int) i + 1); + set_value(L, -1, (char*) to + esz * i, to_usr, &et, check_pointers); + lua_pop(L, 1); + } + + } else if (lua_isnil(L, -1)) { + /* there is no second element, so we set the whole array to the + * first element (or nil - ie 0) if there is no first element) */ + lua_pop(L, 1); + lua_rawgeti(L, idx, 1); + + if (lua_isnil(L, -1)) { + memset(to, 0, ctype_size(L, tt)); + } else { + /* if its still variable we have no idea how many values to set */ + for (i = 0; i < tt->array_size; i++) { + set_value(L, -1, (char*) to + esz * i, to_usr, &et, check_pointers); + } + } + + lua_pop(L, 1); + + } else { + /* there is a second element, so we set each element using the + * equiv index in the table initializer */ + lua_pop(L, 1); + for (i = 0; i < tt->array_size; i++) { + lua_rawgeti(L, idx, (int) (i+1)); + + if (lua_isnil(L, -1)) { + /* we've hit the end of the values provided in the + * initializer, so memset the rest to zero */ + lua_pop(L, 1); + memset((char*) to + esz * i, 0, (tt->array_size - i) * esz); + break; + + } else { + set_value(L, -1, (char*) to + esz * i, to_usr, &et, check_pointers); + lua_pop(L, 1); + } + } + } + break; + + default: + goto err; + } + + return; + +err: + type_error(L, idx, NULL, to_usr, tt); +} + +/* pops the member key from the stack, leaves the member user value on the + * stack. Returns the member offset. Returns -ve if the member can not be + * found. */ +static ptrdiff_t get_member(lua_State* L, int usr, const struct ctype* ct, struct ctype* mt) +{ + ptrdiff_t off; + lua_rawget(L, usr); + + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + return -1; + } + + *mt = *(const struct ctype*) lua_touserdata(L, -1); + lua_getuservalue(L, -1); + lua_replace(L, -2); + + if (mt->is_variable_array && ct->variable_size_known) { + /* eg char mbr[?] */ + size_t sz = (mt->pointers > 1) ? sizeof(void*) : mt->base_size; + assert(ct->is_variable_struct && mt->is_array); + mt->array_size = ct->variable_increment / sz; + mt->is_variable_array = 0; + + } else if (mt->is_variable_struct && ct->variable_size_known) { + /* eg struct {char a; char b[?]} mbr; */ + assert(ct->is_variable_struct); + mt->variable_size_known = 1; + mt->variable_increment = ct->variable_increment; + } + + off = mt->offset; + mt->offset = 0; + return off; +} + +static void set_struct(lua_State* L, int idx, void* to, int to_usr, const struct ctype* tt, int check_pointers) +{ + int have_first = 0; + int have_other = 0; + struct ctype mt; + void* p; + + to_usr = lua_absindex(L, to_usr); + idx = lua_absindex(L, idx); + + switch (lua_type(L, idx)) { + case LUA_TTABLE: + /* match up to the members based off the table initializers key - this + * will match both numbered and named members in the user table + * we need a special case for when no entries in the initializer - + * zero initialize the c struct, and only one entry in the initializer + * - set all members to this value */ + memset(to, 0, ctype_size(L, tt)); + lua_pushnil(L); + while (lua_next(L, idx)) { + ptrdiff_t off; + + if (!have_first && lua_tonumber(L, -2) == 1 && lua_tonumber(L, -1) != 0) { + have_first = 1; + } else if (!have_other && (lua_type(L, -2) != LUA_TNUMBER || lua_tonumber(L, -2) != 1)) { + have_other = 1; + } + + lua_pushvalue(L, -2); + off = get_member(L, to_usr, tt, &mt); + assert(off >= 0); + set_value(L, -2, (char*) to + off, -1, &mt, check_pointers); + + /* initializer value, mt usr */ + lua_pop(L, 2); + } + + /* if we only had a single non zero value then initialize all members to that value */ + if (!have_other && have_first && tt->type != UNION_TYPE) { + size_t i, sz; + ptrdiff_t off; + lua_rawgeti(L, idx, 1); + sz = lua_rawlen(L, to_usr); + + for (i = 2; i < sz; i++) { + lua_pushinteger(L, i); + off = get_member(L, to_usr, tt, &mt); + assert(off >= 0); + set_value(L, -2, (char*) to + off, -1, &mt, check_pointers); + lua_pop(L, 1); /* mt usr */ + } + + lua_pop(L, 1); /* initializer table */ + } + break; + + case LUA_TUSERDATA: + if (check_pointers) { + p = check_typed_pointer(L, idx, to_usr, tt); + } else { + struct ctype ct; + p = check_pointer(L, idx, &ct); + } + memcpy(to, p, tt->base_size); + lua_pop(L, 1); + break; + + default: + goto err; + } + + return; + +err: + type_error(L, idx, NULL, to_usr, tt); +} + +static void set_value(lua_State* L, int idx, void* to, int to_usr, const struct ctype* tt, int check_pointers) +{ + int top = lua_gettop(L); + + if (tt->is_array) { + set_array(L, idx, to, to_usr, tt, check_pointers); + + } else if (tt->pointers || tt->is_reference) { + union { + uint8_t c[sizeof(void*)]; + void* p; + } u; + + if (lua_istable(L, idx)) { + luaL_error(L, "Can't set a pointer member to a struct that's about to be freed"); + } + + if (check_pointers) { + u.p = check_typed_pointer(L, idx, to_usr, tt); + } else { + struct ctype ct; + u.p = check_pointer(L, idx, &ct); + } + +#ifndef ALLOW_MISALIGNED_ACCESS + if ((uintptr_t) to & PTR_ALIGN_MASK) { + memcpy(to, u.c, sizeof(void*)); + } else +#endif + { + *(void**) to = u.p; + } + + lua_pop(L, 1); + + } else if (tt->is_bitfield) { + + uint64_t hi_mask = UINT64_C(0) - (UINT64_C(1) << (tt->bit_offset + tt->bit_size)); + uint64_t low_mask = (UINT64_C(1) << tt->bit_offset) - UINT64_C(1); + uint64_t val = check_uint64(L, idx); + val &= (UINT64_C(1) << tt->bit_size) - 1; + val <<= tt->bit_offset; + *(uint64_t*) to = val | (*(uint64_t*) to & (hi_mask | low_mask)); + + } else if (tt->type == STRUCT_TYPE || tt->type == UNION_TYPE) { + set_struct(L, idx, to, to_usr, tt, check_pointers); + + } else { + +#ifndef ALLOW_MISALIGNED_ACCESS + union { + uint8_t c[8]; + _Bool b; + uint64_t u64; + float f; + double d; + } misalign; + + void* origto = to; + + if ((uintptr_t) origto & (tt->base_size - 1)) { + to = misalign.c; + } +#endif + + switch (tt->type) { + case BOOL_TYPE: + *(_Bool*) to = (cast_int64(L, idx, !check_pointers) != 0); + break; + case INT8_TYPE: + if (tt->is_unsigned) { + *(uint8_t*) to = (uint8_t) cast_uint64(L, idx, !check_pointers); + } else { + *(int8_t*) to = (int8_t) cast_int64(L, idx, !check_pointers); + } + break; + case INT16_TYPE: + if (tt->is_unsigned) { + *(uint16_t*) to = (uint16_t) cast_uint64(L, idx, !check_pointers); + } else { + *(int16_t*) to = (int16_t) cast_int64(L, idx, !check_pointers); + } + break; + case INT32_TYPE: + if (tt->is_unsigned) { + *(uint32_t*) to = (uint32_t) cast_uint64(L, idx, !check_pointers); + } else { + *(int32_t*) to = (int32_t) cast_int64(L, idx, !check_pointers); + } + break; + case INT64_TYPE: + if (tt->is_unsigned) { + *(uint64_t*) to = cast_uint64(L, idx, !check_pointers); + } else { + *(int64_t*) to = cast_int64(L, idx, !check_pointers); + } + break; + case FLOAT_TYPE: + *(float*) to = (float) check_double(L, idx); + break; + case DOUBLE_TYPE: + *(double*) to = check_double(L, idx); + break; + case COMPLEX_FLOAT_TYPE: + *(complex_float*) to = check_complex_float(L, idx); + break; + case COMPLEX_DOUBLE_TYPE: + *(complex_double*) to = check_complex_double(L, idx); + break; + case INTPTR_TYPE: + *(uintptr_t*) to = check_uintptr(L, idx); + break; + case ENUM_TYPE: + *(int32_t*) to = check_enum(L, idx, to_usr, tt); + break; + default: + goto err; + } + +#ifndef ALLOW_MISALIGNED_ACCESS + if ((uintptr_t) origto & (tt->base_size - 1)) { + memcpy(origto, misalign.c, tt->base_size); + } +#endif + } + + assert(lua_gettop(L) == top); + return; +err: + type_error(L, idx, NULL, to_usr, tt); +} + +static int ffi_typeof(lua_State* L) +{ + struct ctype ct; + check_ctype(L, 1, &ct); + push_ctype(L, -1, &ct); + return 1; +} + +static void setmintop(lua_State* L, int idx) +{ + if (lua_gettop(L) < idx) { + lua_settop(L, idx); + } +} + +/* warning: in the case that it finds an array size, it removes that index */ +static void get_variable_array_size(lua_State* L, int idx, struct ctype* ct) +{ + /* we only care about the variable buisness for the variable array + * directly ie ffi.new('char[?]') or the struct that contains the variable + * array ffi.new('struct {char v[?]}'). A pointer to the struct doesn't + * care about the variable size (it treats it as a zero sized array). */ + + if (ct->is_variable_array) { + assert(ct->is_array); + ct->array_size = (size_t) luaL_checknumber(L, idx); + ct->is_variable_array = 0; + lua_remove(L, idx); + + } else if (ct->is_variable_struct && !ct->variable_size_known) { + assert(ct->type == STRUCT_TYPE && !ct->is_array); + ct->variable_increment *= (size_t) luaL_checknumber(L, idx); + ct->variable_size_known = 1; + lua_remove(L, idx); + } +} + +static int is_scalar(struct ctype* ct) +{ + int type = ct->type; + if (ct->pointers || ct->is_reference) { + return !ct->is_array; + } + return type != STRUCT_TYPE && type != UNION_TYPE && !IS_COMPLEX(type); +} + +static int should_pack(lua_State *L, int ct_usr, struct ctype* ct, int idx) +{ + struct ctype argt; + ct_usr = lua_absindex(L, ct_usr); + + if (IS_COMPLEX(ct->type)) { + return 0; + } + + switch (lua_type(L, idx)) { + case LUA_TTABLE: + return 0; + case LUA_TSTRING: + return ct->type == STRUCT_TYPE; + case LUA_TUSERDATA: + // don't pack if the argument is a cdata with the same type + to_cdata(L, idx, &argt); + int same = is_same_type(L, ct_usr, -1, ct, &argt); + lua_pop(L, 1); + return !same; + default: + return 1; + } +} + +static int do_new(lua_State* L, int is_cast) +{ + int cargs, i; + void* p; + struct ctype ct; + int check_ptrs = !is_cast; + + check_ctype(L, 1, &ct); + + /* this removes the vararg argument if its needed, and errors if its invalid */ + if (!is_cast) { + get_variable_array_size(L, 2, &ct); + } + + p = push_cdata(L, -1, &ct); + + /* if the user mt has a __gc function then call ffi.gc on this value */ + if (push_user_mt(L, -2, &ct)) { + push_upval(L, &gc_key); + lua_pushvalue(L, -3); + + /* user_mt.__gc */ + lua_pushliteral(L, "__gc"); + lua_rawget(L, -4); + + lua_rawset(L, -3); /* gc_upval[cdata] = user_mt.__gc */ + lua_pop(L, 2); /* user_mt and gc_upval */ + } + + /* stack is: + * ctype arg + * ctor args ... 0+ + * ctype usr + * cdata + */ + + cargs = lua_gettop(L) - 3; + + if (cargs == 0) { + return 1; + } + + int scalar = is_scalar(&ct); + if (scalar && cargs > 1) { + return luaL_error(L, "too many initializers"); + } + + if (cargs > 1 || (!scalar && should_pack(L, -2, &ct, 2))) { + lua_createtable(L, cargs, 0); + lua_replace(L, 1); + for (i = 1; i <= cargs; i++) { + lua_pushvalue(L, i + 1); + lua_rawseti(L, 1, i); + } + assert(lua_gettop(L) == cargs + 3); + set_value(L, 1, p, -2, &ct, check_ptrs); + return 1; + } + + set_value(L, 2, p, -2, &ct, check_ptrs); + return 1; +} + +static int ffi_new(lua_State* L) +{ return do_new(L, 0); } + +static int ffi_cast(lua_State* L) +{ return do_new(L, 1); } + +static int ctype_new(lua_State* L) +{ return do_new(L, 0); } + +static int ctype_call(lua_State* L) +{ + struct ctype ct; + int top = lua_gettop(L); + + check_ctype(L, 1, &ct); + + if (push_user_mt(L, -1, &ct)) { + lua_pushstring(L, "__new"); + lua_rawget(L, -2); + if (!lua_isnil(L, -1)) { + lua_insert(L, 1); // function at bottom of stack under args + lua_pop(L, 2); + lua_call(L, top, 1); + return 1; + } + lua_pop(L, 2); + } + lua_pop(L, 1); + + assert(lua_gettop(L) == top); + return do_new(L, 0); +} + +static int ffi_sizeof(lua_State* L) +{ + struct ctype ct; + check_ctype(L, 1, &ct); + get_variable_array_size(L, 2, &ct); + lua_pushinteger(L, ctype_size(L, &ct)); + return 1; +} + +static int ffi_alignof(lua_State* L) +{ + struct ctype ct, mt; + lua_settop(L, 2); + check_ctype(L, 1, &ct); + + /* if no member is specified then we return the alignment of the type */ + if (lua_isnil(L, 2)) { + lua_pushinteger(L, ct.align_mask + 1); + return 1; + } + + /* get the alignment of the member */ + lua_pushvalue(L, 2); + if (get_member(L, -2, &ct, &mt) < 0) { + push_type_name(L, 3, &ct); + return luaL_error(L, "type %s has no member %s", lua_tostring(L, -1), lua_tostring(L, 2)); + } + + lua_pushinteger(L, mt.align_mask + 1); + return 1; +} + +static int ffi_offsetof(lua_State* L) +{ + ptrdiff_t off; + struct ctype ct, mt; + lua_settop(L, 2); + check_ctype(L, 1, &ct); + + lua_pushvalue(L, 2); + off = get_member(L, -2, &ct, &mt); /* this replaces the member key at -1 with the mbr usr value */ + if (off < 0) { + push_type_name(L, 3, &ct); + return luaL_error(L, "type %s has no member %s", lua_tostring(L, -1), lua_tostring(L, 2)); + } + + lua_pushinteger(L, off); + + if (!mt.is_bitfield) { + return 1; + } + + lua_pushinteger(L, mt.bit_offset); + lua_pushinteger(L, mt.bit_size); + return 3; +} + +static int ffi_istype(lua_State* L) +{ + struct ctype tt, ft; + check_ctype(L, 1, &tt); + to_cdata(L, 2, &ft); + + if (ft.type == INVALID_TYPE) { + goto fail; + } + + if (!is_same_type(L, 3, 4, &tt, &ft)) { + goto fail; + } + + if (tt.pointers != ft.pointers) { + goto fail; + } + + if (tt.is_array != ft.is_array) { + goto fail; + } + + if (tt.is_array && tt.array_size != ft.array_size) { + goto fail; + } + + if (tt.calling_convention != ft.calling_convention) { + goto fail; + } + + lua_pushboolean(L, 1); + return 1; + +fail: + lua_pushboolean(L, 0); + return 1; +} + +static int cdata_gc(lua_State* L) +{ + struct ctype ct; + check_cdata(L, 1, &ct); + lua_settop(L, 1); + + /* call the gc func if there is any registered */ + lua_pushvalue(L, 1); + lua_rawget(L, lua_upvalueindex(2)); + if (!lua_isnil(L, -1)) { + lua_pushvalue(L, 1); + lua_pcall(L, 1, 0, 0); + } + + /* unset the closure */ + lua_pushvalue(L, 1); + lua_pushnil(L); + lua_rawset(L, lua_upvalueindex(1)); + + return 0; +} + +static int user_mt_key; + +static int ffi_metatype(lua_State* L) +{ + struct ctype ct; + lua_settop(L, 2); + + check_ctype(L, 1, &ct); + if (lua_type(L, 2) != LUA_TTABLE && lua_type(L, 2) != LUA_TNIL) { + return luaL_argerror(L, 2, "metatable must be a table or nil"); + } + + lua_pushlightuserdata(L, &user_mt_key); + lua_pushvalue(L, 2); + lua_rawset(L, 3); /* user[user_mt_key] = mt */ + + /* return the passed in ctype */ + push_ctype(L, 3, &ct); + return 1; +} + +/* push_user_mt returns 1 if the type has a user metatable and pushes it onto + * the stack, otherwise it returns 0 and pushes nothing */ +int push_user_mt(lua_State* L, int ct_usr, const struct ctype* ct) +{ + if (ct->type != STRUCT_TYPE && ct->type != UNION_TYPE && !IS_COMPLEX(ct->type)) { + return 0; + } + if (!lua_istable(L, ct_usr)) { + return 0; + } + + ct_usr = lua_absindex(L, ct_usr); + lua_pushlightuserdata(L, &user_mt_key); + lua_rawget(L, ct_usr); + + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + return 0; + } + return 1; +} + +static int ffi_gc(lua_State* L) +{ + struct ctype ct; + lua_settop(L, 2); + check_cdata(L, 1, &ct); + + push_upval(L, &gc_key); + lua_pushvalue(L, 1); + lua_pushvalue(L, 2); + lua_rawset(L, -3); + + /* return the cdata back */ + lua_settop(L, 1); + return 1; +} + +/* lookup_cdata_index returns the offset of the found type and user value on + * the stack if valid. Otherwise returns -ve and doesn't touch the stack. + */ +static ptrdiff_t lookup_cdata_index(lua_State* L, int idx, int ct_usr, struct ctype* ct) +{ + struct ctype mt; + ptrdiff_t off; + + ct_usr = lua_absindex(L, ct_usr); + int type = lua_type(L, idx); + + switch (type) { + case LUA_TNUMBER: + case LUA_TUSERDATA: + /* possibilities are array, pointer */ + + if (!ct->pointers || is_void_ptr(ct)) { + return -1; + } + + // unbox cdata + if (type == LUA_TUSERDATA) { + if (!cdata_tointeger(L, idx, &off)) { + return -1; + } + } else { + off = lua_tointeger(L, idx); + } + + ct->is_array = 0; + ct->pointers--; + ct->const_mask >>= 1; + ct->is_reference = 0; + + lua_pushvalue(L, ct_usr); + + return (ct->pointers ? sizeof(void*) : ct->base_size) * off; + + case LUA_TSTRING: + /* possibilities are struct/union, pointer to struct/union */ + + if ((ct->type != STRUCT_TYPE && ct->type != UNION_TYPE) || ct->is_array || ct->pointers > 1) { + return -1; + } + + lua_pushvalue(L, idx); + off = get_member(L, ct_usr, ct, &mt); + if (off < 0) { + return -1; + } + + *ct = mt; + return off; + + default: + return -1; + } +} + +static int cdata_newindex(lua_State* L) +{ + struct ctype tt; + char* to; + ptrdiff_t off; + + lua_settop(L, 3); + + to = (char*) check_cdata(L, 1, &tt); + off = lookup_cdata_index(L, 2, -1, &tt); + + if (off < 0) { + if (!push_user_mt(L, -1, &tt)) { + goto err; + } + + lua_pushliteral(L, "__newindex"); + lua_rawget(L, -2); + + if (lua_isnil(L, -1)) { + goto err; + } + + lua_insert(L, 1); + lua_settop(L, 4); + lua_call(L, 3, LUA_MULTRET); + return lua_gettop(L); + } + + if (tt.const_mask & 1) { + return luaL_error(L, "can't set const data"); + } + + set_value(L, 3, to + off, -1, &tt, 1); + return 0; + +err: + push_type_name(L, 4, &tt); + return luaL_error(L, "type %s has no member %s", lua_tostring(L, -1), lua_tostring(L, 2)); +} + +static int cdata_index(lua_State* L) +{ + void* to; + struct ctype ct; + char* data; + ptrdiff_t off; + + lua_settop(L, 2); + data = (char*) check_cdata(L, 1, &ct); + assert(lua_gettop(L) == 3); + + if (!ct.pointers) { + switch (ct.type) { + case FUNCTION_PTR_TYPE: + /* Callbacks use the same metatable as standard cdata values, but have set + * and free members. So instead of mt.__index = mt, we do the equiv here. */ + lua_getmetatable(L, 1); + lua_pushvalue(L, 2); + lua_rawget(L, -2); + return 1; + + /* This provides the .re and .im virtual members */ + case COMPLEX_DOUBLE_TYPE: + case COMPLEX_FLOAT_TYPE: + if (!lua_isstring(L, 2)) { + luaL_error(L, "invalid member for complex number"); + + } else if (strcmp(lua_tostring(L, 2), "re") == 0) { + lua_pushnumber(L, ct.type == COMPLEX_DOUBLE_TYPE ? creal(*(complex_double*) data) : crealf(*(complex_float*) data)); + + } else if (strcmp(lua_tostring(L, 2), "im") == 0) { + lua_pushnumber(L, ct.type == COMPLEX_DOUBLE_TYPE ? cimag(*(complex_double*) data) : cimagf(*(complex_float*) data)); + + } else { + luaL_error(L, "invalid member for complex number"); + } + return 1; + } + } + + off = lookup_cdata_index(L, 2, -1, &ct); + + if (off < 0) { + assert(lua_gettop(L) == 3); + if (!push_user_mt(L, -1, &ct)) { + goto err; + } + + lua_pushliteral(L, "__index"); + lua_rawget(L, -2); + + if (lua_isnil(L, -1)) { + goto err; + } + + if (lua_istable(L, -1)) { + lua_pushvalue(L, 2); + lua_gettable(L, -2); + return 1; + } + + lua_insert(L, 1); + lua_settop(L, 3); + lua_call(L, 2, LUA_MULTRET); + return lua_gettop(L); + +err: + push_type_name(L, 3, &ct); + return luaL_error(L, "type %s has no member %s", lua_tostring(L, -1), lua_tostring(L, 2)); + } + + assert(lua_gettop(L) == 4); /* ct, key, ct_usr, mbr_usr */ + data += off; + + if (ct.is_array) { + /* push a reference to the array */ + ct.is_reference = 1; + to = push_cdata(L, -1, &ct); + *(void**) to = data; + return 1; + + } else if (ct.is_bitfield) { + + if (ct.type == INT64_TYPE) { + struct ctype rt; + uint64_t val = *(uint64_t*) data; + val >>= ct.bit_offset; + val &= (UINT64_C(1) << ct.bit_size) - 1; + + memset(&rt, 0, sizeof(rt)); + rt.base_size = 8; + rt.type = INT64_TYPE; + rt.is_unsigned = 1; + rt.is_defined = 1; + + to = push_cdata(L, 0, &rt); + *(uint64_t*) to = val; + + return 1; + + } else if (ct.type == BOOL_TYPE) { + uint64_t val = *(uint64_t*) data; + lua_pushboolean(L, (int) (val & (UINT64_C(1) << ct.bit_offset))); + return 1; + + } else { + uint64_t val = *(uint64_t*) data; + val >>= ct.bit_offset; + val &= (UINT64_C(1) << ct.bit_size) - 1; + lua_pushinteger(L, val); + return 1; + } + + } else if (ct.pointers) { +#ifndef ALLOW_MISALIGNED_ACCESS + union { + uint8_t c[8]; + void* p; + } misalignbuf; + + if ((uintptr_t) data & PTR_ALIGN_MASK) { + memcpy(misalignbuf.c, data, sizeof(void*)); + data = misalignbuf.c; + } +#endif + to = push_cdata(L, -1, &ct); + *(void**) to = *(void**) data; + return 1; + + } else if (ct.type == STRUCT_TYPE || ct.type == UNION_TYPE) { + /* push a reference to the member */ + ct.is_reference = 1; + to = push_cdata(L, -1, &ct); + *(void**) to = data; + return 1; + + } else { +#ifndef ALLOW_MISALIGNED_ACCESS + union { + uint8_t c[8]; + double d; + float f; + uint64_t u64; + } misalignbuf; + + assert(ct.base_size <= 8); + + if ((uintptr_t) data & (ct.base_size - 1)) { + memcpy(misalignbuf.c, data, ct.base_size); + data = misalignbuf.c; + } +#endif + + switch (ct.type) { + case BOOL_TYPE: + lua_pushboolean(L, *(_Bool*) data); + break; + case INT8_TYPE: + lua_pushinteger(L, ct.is_unsigned ? (lua_Integer) *(uint8_t*) data : (lua_Integer) *(int8_t*) data); + break; + case INT16_TYPE: + lua_pushinteger(L, ct.is_unsigned ? (lua_Integer) *(uint16_t*) data : (lua_Integer) *(int16_t*) data); + break; + case ENUM_TYPE: + case INT32_TYPE: + lua_pushinteger(L, ct.is_unsigned ? (lua_Integer) *(uint32_t*) data : (lua_Integer) *(int32_t*) data); + break; + case INT64_TYPE: + to = push_cdata(L, -1, &ct); + *(int64_t*) to = *(int64_t*) data; + break; + case INTPTR_TYPE: + to = push_cdata(L, -1, &ct); + *(intptr_t*) to = *(intptr_t*) data; + break; + case FLOAT_TYPE: + lua_pushnumber(L, *(float*) data); + break; + case DOUBLE_TYPE: + lua_pushnumber(L, *(double*) data); + break; + case COMPLEX_DOUBLE_TYPE: + to = push_cdata(L, -1, &ct); + *(complex_double*) to = *(complex_double*) data; + break; + case COMPLEX_FLOAT_TYPE: + to = push_cdata(L, -1, &ct); + *(complex_float*) to = *(complex_float*) data; + break; + default: + luaL_error(L, "internal error: invalid member type"); + } + + return 1; + } +} + +static complex_double check_complex(lua_State* L, int idx, void* p, struct ctype* ct) +{ + if (ct->type == INVALID_TYPE) { + double d = luaL_checknumber(L, idx); +#ifdef HAVE_COMPLEX + return d; +#else + complex_double c; + c.real = d; + c.imag = 0; + return c; +#endif + } else if (ct->type == COMPLEX_DOUBLE_TYPE) { + return *(complex_double*) p; + } else if (ct->type == COMPLEX_FLOAT_TYPE) { + complex_float* f = (complex_float*) p; +#ifdef HAVE_COMPLEX + return *f; +#else + complex_double d; + d.real = f->real; + d.imag = f->imag; + return d; +#endif + } else { + complex_double dummy; + type_error(L, idx, "complex", 0, NULL); + memset(&dummy, 0, sizeof(dummy)); + return dummy; + } +} + +static int rank(const struct ctype* ct) +{ + if (ct->pointers) { + return 5; + } + + switch (ct->type) { + case COMPLEX_DOUBLE_TYPE: + return 7; + case COMPLEX_FLOAT_TYPE: + return 6; + case INTPTR_TYPE: + return sizeof(intptr_t) >= sizeof(int64_t) ? 4 : 1; + case INT64_TYPE: + return ct->is_unsigned ? 3 : 2; + case INT32_TYPE: + case INT16_TYPE: + case INT8_TYPE: + return 2; + default: + return 0; + } +} + +static void push_complex(lua_State* L, complex_double res, int ct_usr, const struct ctype* ct) +{ + if (ct->type == COMPLEX_DOUBLE_TYPE) { + complex_double* p = (complex_double*) push_cdata(L, ct_usr, ct); + *p = res; + } else { + complex_float* p = (complex_float*) push_cdata(L, ct_usr, ct); +#ifdef HAVE_COMPLEX + *p = (complex float) res; +#else + p->real = (float) res.real; + p->imag = (float) res.imag; +#endif + } +} + +static void push_number(lua_State* L, int64_t val, int ct_usr, const struct ctype* ct) +{ + if ((ct->pointers || ct->type == INTPTR_TYPE) && sizeof(intptr_t) != sizeof(int64_t)) { + intptr_t* p = (intptr_t*) push_cdata(L, ct_usr, ct); + *p = val; + } else { + int64_t* p = (int64_t*) push_cdata(L, ct_usr, ct); + *p = val; + } +} + +static int call_user_op(lua_State* L, const char* opfield, int idx, int ct_usr, const struct ctype* ct) +{ + idx = lua_absindex(L, idx); + + if (push_user_mt(L, ct_usr, ct)) { + lua_pushstring(L, opfield); + lua_rawget(L, -2); + if (!lua_isnil(L, -1)) { + int top = lua_gettop(L); + lua_pushvalue(L, idx); + lua_call(L, 1, LUA_MULTRET); + return lua_gettop(L) - top + 1; + } + lua_pop(L, 2); + } + return -1; +} + +static int cdata_unm(lua_State* L) +{ + struct ctype ct; + void* p; + int64_t val; + int ret; + + lua_settop(L, 1); + p = to_cdata(L, 1, &ct); + + ret = call_user_op(L, "__unm", 1, 2, &ct); + if (ret >= 0) { + return ret; + } + + val = check_intptr(L, 1, p, &ct); + + if (ct.pointers) { + luaL_error(L, "can't negate a pointer value"); + } else { + memset(&ct, 0, sizeof(ct)); + ct.type = INT64_TYPE; + ct.base_size = 8; + ct.is_defined = 1; + push_number(L, -val, 0, &ct); + } + + return 1; +} + +/* returns -ve if no binop was called otherwise returns the number of return + * arguments */ +static int call_user_binop(lua_State* L, const char* opfield, int lidx, int lusr, const struct ctype* lt, int ridx, int rusr, const struct ctype* rt) +{ + lidx = lua_absindex(L, lidx); + ridx = lua_absindex(L, ridx); + + if (push_user_mt(L, lusr, lt)) { + lua_pushstring(L, opfield); + lua_rawget(L, -2); + + if (!lua_isnil(L, -1)) { + int top = lua_gettop(L); + lua_pushvalue(L, lidx); + lua_pushvalue(L, ridx); + lua_call(L, 2, LUA_MULTRET); + return lua_gettop(L) - top + 1; + } + + lua_pop(L, 2); /* user_mt and user_mt.op */ + } + + if (push_user_mt(L, rusr, rt)) { + lua_pushstring(L, opfield); + lua_rawget(L, -2); + + if (!lua_isnil(L, -1)) { + int top = lua_gettop(L); + lua_pushvalue(L, lidx); + lua_pushvalue(L, ridx); + lua_call(L, 2, LUA_MULTRET); + return lua_gettop(L) - top + 1; + } + + lua_pop(L, 2); /* user_mt and user_mt.op */ + } + + return -1; +} + +static int cdata_concat(lua_State* L) +{ + struct ctype lt, rt; + int ret; + + lua_settop(L, 2); + to_cdata(L, 1, <); + to_cdata(L, 2, &rt); + + ret = call_user_binop(L, "__concat", 1, 3, <, 2, 4, &rt); + if (ret >= 0) { + return ret; + } + + return luaL_error(L, "NYI"); +} + +static int cdata_len(lua_State* L) +{ + struct ctype ct; + int ret; + + lua_settop(L, 1); + to_cdata(L, 1, &ct); + + ret = call_user_op(L, "__len", 1, 2, &ct); + if (ret >= 0) { + return ret; + } + + push_type_name(L, 2, &ct); + return luaL_error(L, "type %s does not implement the __len metamethod", lua_tostring(L, -1)); +} + +static int cdata_pairs(lua_State* L) +{ + struct ctype ct; + int ret; + + lua_settop(L, 1); + to_cdata(L, 1, &ct); + + ret = call_user_op(L, "__pairs", 1, 2, &ct); + if (ret >= 0) { + return ret; + } + + push_type_name(L, 2, &ct); + return luaL_error(L, "type %s does not implement the __pairs metamethod", lua_tostring(L, -1)); +} + +static int cdata_ipairs(lua_State* L) +{ + struct ctype ct; + int ret; + + lua_settop(L, 1); + to_cdata(L, 1, &ct); + + ret = call_user_op(L, "__ipairs", 1, 2, &ct); + if (ret >= 0) { + return ret; + } + + push_type_name(L, 2, &ct); + return luaL_error(L, "type %s does not implement the __ipairs metamethod", lua_tostring(L, -1)); +} + +static int cdata_add(lua_State* L) +{ + struct ctype lt, rt, ct; + void *lp, *rp; + int ct_usr; + int ret; + + lua_settop(L, 2); + + lp = to_cdata(L, 1, <); + rp = to_cdata(L, 2, &rt); + assert(lua_gettop(L) == 4); + + ret = call_user_binop(L, "__add", 1, 3, <, 2, 4, &rt); + if (ret >= 0) { + return ret; + } + assert(lua_gettop(L) == 4); + + ct_usr = rank(<) > rank(&rt) ? 3 : 4; + ct = rank(<) > rank(&rt) ? lt : rt; + + if (IS_COMPLEX(ct.type)) { + complex_double left, right, res; + + left = check_complex(L, 1, lp, <); + right = check_complex(L, 2, rp, &rt); + assert(lua_gettop(L) == 4); + +#ifdef HAVE_COMPLEX + res = left + right; +#else + res.real = left.real + right.real; + res.imag = left.imag + right.imag; +#endif + + push_complex(L, res, ct_usr, &ct); + return 1; + + } else { + int64_t left = check_intptr(L, 1, lp, <); + int64_t right = check_intptr(L, 2, rp, &rt); + assert(lua_gettop(L) == 4); + + /* note due to 2s complement it doesn't matter if we do the addition as int or uint, + * but the result needs to be uint64_t if either of the sources are */ + + if (lt.pointers && rt.pointers) { + luaL_error(L, "can't add two pointers"); + + } else if (lt.pointers) { + int64_t res = left + (lt.pointers > 1 ? sizeof(void*) : lt.base_size) * right; + lt.is_array = 0; + push_number(L, res, 3, <); + + } else if (rt.pointers) { + int64_t res = right + (rt.pointers > 1 ? sizeof(void*) : rt.base_size) * left; + rt.is_array = 0; + push_number(L, res, 4, &rt); + + } else { + push_number(L, left + right, ct_usr, &ct); + } + + return 1; + } +} + +static int cdata_sub(lua_State* L) +{ + struct ctype lt, rt, ct; + void *lp, *rp; + int ct_usr; + int ret; + + lua_settop(L, 2); + + lp = to_cdata(L, 1, <); + rp = to_cdata(L, 2, &rt); + + ret = call_user_binop(L, "__sub", 1, 3, <, 2, 4, &rt); + if (ret >= 0) { + return ret; + } + + ct_usr = rank(<) > rank(&rt) ? 3 : 4; + ct = rank(<) > rank(&rt) ? lt : rt; + + if (IS_COMPLEX(ct.type)) { + complex_double left, right, res; + + left = check_complex(L, 1, lp, <); + right = check_complex(L, 2, rp, &rt); + +#ifdef HAVE_COMPLEX + res = left - right; +#else + res.real = left.real - right.real; + res.imag = left.imag - right.imag; +#endif + + push_complex(L, res, ct_usr, &ct); + return 1; + + } else { + int64_t left = check_intptr(L, 1, lp, <); + int64_t right = check_intptr(L, 2, rp, &rt); + + if (rt.pointers) { + luaL_error(L, "NYI: can't subtract a pointer value"); + + } else if (lt.pointers) { + int64_t res = left - (lt.pointers > 1 ? sizeof(void*) : lt.base_size) * right; + lt.is_array = 0; + push_number(L, res, 3, <); + + } else { + int64_t res = left - right; + push_number(L, res, ct_usr, &ct); + } + + return 1; + } +} + +/* TODO fix for unsigned */ +#define NUMBER_ONLY_BINOP(OPSTR, DO_NORMAL, DO_COMPLEX) \ + struct ctype lt, rt, ct; \ + void *lp, *rp; \ + int ct_usr; \ + int ret; \ + \ + lua_settop(L, 2); \ + \ + lp = to_cdata(L, 1, <); \ + rp = to_cdata(L, 2, &rt); \ + \ + ret = call_user_binop(L, OPSTR, 1, 3, <, 2, 4, &rt); \ + if (ret >= 0) { \ + return ret; \ + } \ + \ + ct_usr = rank(<) > rank(&rt) ? 3 : 4; \ + ct = rank(<) > rank(&rt) ? lt : rt; \ + \ + if (IS_COMPLEX(ct.type)) { \ + complex_double res; \ + complex_double left = check_complex(L, 1, lp, <); \ + complex_double right = check_complex(L, 2, rp, &rt); \ + \ + DO_COMPLEX(left, right, res); \ + push_complex(L, res, ct_usr, &ct); \ + \ + } else if (lt.pointers || rt.pointers) { \ + luaL_error(L, "can't operate on a pointer value"); \ + \ + } else { \ + int64_t res; \ + int64_t left = check_intptr(L, 1, lp, <); \ + int64_t right = check_intptr(L, 2, rp, &rt); \ + \ + DO_NORMAL(left, right, res); \ + push_number(L, res, ct_usr, &ct); \ + } \ + \ + return 1 + +#define MUL(l,r,s) s = l * r +#define DIV(l,r,s) s = l / r +#define MOD(l,r,s) s = l % r +#define POW(l,r,s) s = pow(l, r) + +#ifdef HAVE_COMPLEX +#define MULC(l,r,s) s = l * r +#define DIVC(l,r,s) s = l / r +#define MODC(l,r,s) (void) l, (void) r, memset(&s, 0, sizeof(s)), luaL_error(L, "NYI: complex mod") +#define POWC(l,r,s) s = cpow(l, r) +#else +#define MULC(l,r,s) s.real = l.real * r.real - l.imag * r.imag, s.imag = l.real * r.imag + l.imag * r.real +#define DIVC(l,r,s) s.real = (l.real * r.real + l.imag * r.imag) / (r.real * r.real + r.imag * r.imag), \ + s.imag = (l.imag * r.real - l.real * r.imag) / (r.real * r.real + r.imag * r.imag) +#define MODC(l,r,s) (void) l, (void) r, memset(&s, 0, sizeof(s)), luaL_error(L, "NYI: complex mod") +#define POWC(l,r,s) (void) l, (void) r, memset(&s, 0, sizeof(s)), luaL_error(L, "NYI: complex pow") +#endif + +static int cdata_mul(lua_State* L) +{ NUMBER_ONLY_BINOP("__mul", MUL, MULC); } + +static int cdata_div(lua_State* L) +{ NUMBER_ONLY_BINOP("__div", DIV, DIVC); } + +static int cdata_mod(lua_State* L) +{ NUMBER_ONLY_BINOP("__mod", MOD, MODC); } + +static int cdata_pow(lua_State* L) +{ NUMBER_ONLY_BINOP("__pow", POW, POWC); } + +#define COMPARE_BINOP(OPSTR, OP, OPC) \ + struct ctype lt, rt; \ + void *lp, *rp; \ + int ret, res; \ + \ + lua_settop(L, 2); \ + \ + lp = to_cdata(L, 1, <); \ + rp = to_cdata(L, 2, &rt); \ + \ + ret = call_user_binop(L, OPSTR, 1, 3, <, 2, 4, &rt); \ + if (ret >= 0) { \ + return ret; \ + } \ + \ + if (IS_COMPLEX(lt.type) || IS_COMPLEX(rt.type)) { \ + complex_double left = check_complex(L, 1, lp, <); \ + complex_double right = check_complex(L, 2, rp, &rt); \ + \ + res = OPC(left, right); \ + \ + lua_pushboolean(L, res); \ + \ + } else { \ + int64_t left = check_intptr(L, 1, lp, <); \ + int64_t right = check_intptr(L, 2, rp, &rt); \ + \ + if (lt.pointers && rt.pointers) { \ + if (is_void_ptr(<) || is_void_ptr(&rt) || is_same_type(L, 3, 4, <, &rt)) { \ + res = OP((uint64_t) left, (uint64_t) right); \ + } else { \ + goto err; \ + } \ + \ + } else if (lt.is_null && rt.type == FUNCTION_PTR_TYPE) { \ + res = OP((uint64_t) left, (uint64_t) right); \ + \ + } else if (rt.is_null && lt.type == FUNCTION_PTR_TYPE) { \ + res = OP((uint64_t) left, (uint64_t) right); \ + \ + } else if (lt.pointers && rt.type == INTPTR_TYPE && rt.is_unsigned) {\ + res = OP((uint64_t) left, (uint64_t) right); \ + \ + } else if (rt.pointers && lt.type == INTPTR_TYPE && lt.is_unsigned) {\ + res = OP((uint64_t) left, (uint64_t) right); \ + \ + } else if (rt.pointers || lt.pointers) { \ + goto err; \ + \ + } else if (lt.is_unsigned && rt.is_unsigned) { \ + res = OP((uint64_t) left, (uint64_t) right); \ + \ + } else if (lt.is_unsigned) { \ + res = OP((int64_t) (uint64_t) left, right); \ + \ + } else if (rt.is_unsigned) { \ + res = OP(left, (int64_t) (uint64_t) right); \ + \ + } else { \ + res = OP(left, right); \ + } \ + \ + lua_pushboolean(L, res); \ + } \ + return 1 + +#define EQ(l, r) (l) == (r) +#define LT(l, r) (l) < (r) +#define LE(l, r) (l) <= (r) + +#ifdef HAVE_COMPLEX +#define EQC(l, r) (l) == (r) +#else +#define EQC(l, r) (l).real == (r).real && (l).imag == (r).imag +#endif + +#define LEC(l, r) EQC(l, r), luaL_error(L, "complex numbers are non-orderable") +#define LTC(l, r) EQC(l, r), luaL_error(L, "complex numbers are non-orderable") + +static int cdata_eq(lua_State* L) +{ + COMPARE_BINOP("__eq", EQ, EQC); +err: + lua_pushboolean(L, 0); + return 1; +} + +static int cdata_lt(lua_State* L) +{ + COMPARE_BINOP("__lt", LT, LTC); +err: + lua_getuservalue(L, 1); + lua_getuservalue(L, 2); + push_type_name(L, -2, <); + push_type_name(L, -2, <); + return luaL_error(L, "trying to compare incompatible types %s and %s", lua_tostring(L, -2), lua_tostring(L, -1)); +} + +static int cdata_le(lua_State* L) +{ + COMPARE_BINOP("__le", LE, LEC); +err: + lua_getuservalue(L, 1); + lua_getuservalue(L, 2); + push_type_name(L, -2, <); + push_type_name(L, -2, <); + return luaL_error(L, "trying to compare incompatible types %s and %s", lua_tostring(L, -2), lua_tostring(L, -1)); +} + +static const char* etype_tostring(int type) +{ + switch (type) { + case VOID_TYPE: return "void"; + case DOUBLE_TYPE: return "double"; + case FLOAT_TYPE: return "float"; + case COMPLEX_DOUBLE_TYPE: return "complex double"; + case COMPLEX_FLOAT_TYPE: return "complex float"; + case BOOL_TYPE: return "bool"; + case INT8_TYPE: return "int8"; + case INT16_TYPE: return "int16"; + case INT32_TYPE: return "int32"; + case INT64_TYPE: return "int64"; + case INTPTR_TYPE: return "intptr"; + case ENUM_TYPE: return "enum"; + case UNION_TYPE: return "union"; + case STRUCT_TYPE: return "struct"; + case FUNCTION_PTR_TYPE: return "function ptr"; + case FUNCTION_TYPE: return "function"; + default: return "invalid"; + } +} + +static void print_type(lua_State* L, const struct ctype* ct) +{ + lua_pushfstring(L, " sz %d %d %d align %d ptr %d %d %d type %s%s %d %d %d name %d call %d %d var %d %d %d bit %d %d %d %d jit %d", + /* sz */ + ct->base_size, + ct->array_size, + ct->offset, + /* align */ + ct->align_mask, + /* ptr */ + ct->is_array, + ct->pointers, + ct->const_mask, + /* type */ + ct->is_unsigned ? "u" : "", + etype_tostring(ct->type), + ct->is_reference, + ct->is_defined, + ct->is_null, + /* name */ + ct->has_member_name, + /* call */ + ct->calling_convention, + ct->has_var_arg, + /* var */ + ct->is_variable_array, + ct->is_variable_struct, + ct->variable_size_known, + /* bit */ + ct->is_bitfield, + ct->has_bitfield, + ct->bit_offset, + ct->bit_size, + /* jit */ + ct->is_jitted); +} + +static int ctype_tostring(lua_State* L) +{ + struct ctype ct; + assert(lua_type(L, 1) == LUA_TUSERDATA); + lua_settop(L, 1); + check_ctype(L, 1, &ct); + assert(lua_gettop(L) == 2); + push_type_name(L, -1, &ct); + lua_pushfstring(L, "ctype<%s>", lua_tostring(L, -1)); + + if (DEBUG_TOSTRING) { + print_type(L, &ct); + lua_concat(L, 2); + } + + return 1; +} + +static int cdata_tostring(lua_State* L) +{ + struct ctype ct; + char buf[64]; + void* p; + int ret; + + lua_settop(L, 1); + p = to_cdata(L, 1, &ct); + + ret = call_user_op(L, "__tostring", 1, 2, &ct); + if (ret >= 0) { + return ret; + } + + if (ct.pointers > 0 || ct.is_reference || ct.type == STRUCT_TYPE || ct.type == UNION_TYPE) { + push_type_name(L, -1, &ct); + lua_pushfstring(L, "cdata<%s>: %p", lua_tostring(L, -1), p); + + if (DEBUG_TOSTRING) { + print_type(L, &ct); + lua_concat(L, 2); + } + + return 1; + } + + switch (ct.type) { + case COMPLEX_DOUBLE_TYPE: + { + complex_double c = *(complex_double*) p; + lua_pushfstring(L, "%f+%fi", creal(c), cimag(c)); + } + return 1; + + case COMPLEX_FLOAT_TYPE: + { + complex_float c = *(complex_float*) p; + lua_pushfstring(L, "%f+%fi", crealf(c), cimagf(c)); + } + return 1; + + case FUNCTION_PTR_TYPE: + p = *(void**) p; + push_type_name(L, -1, &ct); + lua_pushfstring(L, "cdata<%s>: %p", lua_tostring(L, -1), *(void**) p); + return 1; + + case INTPTR_TYPE: + lua_pushfstring(L, "%p", *(uintptr_t*) p); + return 1; + + case INT64_TYPE: + sprintf(buf, ct.is_unsigned ? "%"PRIu64 : "%"PRId64, *(uint64_t*) p); + lua_pushstring(L, buf); + return 1; + + default: + sprintf(buf, ct.is_unsigned ? "%"PRId64 : "%"PRId64, (int64_t) check_intptr(L, 1, p, &ct)); + lua_pushstring(L, buf); + return 1; + } +} + +static int ffi_type(lua_State* L) +{ + if (lua_isuserdata(L, 1) && lua_getmetatable(L, 1)) { + if (equals_upval(L, -1, &cdata_mt_key) || equals_upval(L, -1, &ctype_mt_key)) { + lua_pushstring(L, "cdata"); + return 1; + } + lua_pop(L, 1); /* mt */ + } + + /* call the old _G.type, we use an upvalue as _G.type is set + * to this function */ + lua_pushvalue(L, lua_upvalueindex(1)); + lua_insert(L, 1); + lua_call(L, lua_gettop(L)-1, LUA_MULTRET); + return lua_gettop(L); +} + +static int ffi_number(lua_State* L) +{ + struct ctype ct; + void* data = to_cdata(L, 1, &ct); + + if (ct.type != INVALID_TYPE) { + lua_pushinteger(L, check_intptr(L, 1, data, &ct)); + return 1; + } else { + /* call the old _G.tonumber, we use an upvalue as _G.tonumber is set + * to this function */ + lua_pushvalue(L, lua_upvalueindex(1)); + lua_insert(L, 1); + lua_call(L, lua_gettop(L)-1, LUA_MULTRET); + return lua_gettop(L); + } +} + +static int ffi_string(lua_State* L) +{ + struct ctype ct; + char* data; + lua_settop(L, 2); + + data = (char*) check_cdata(L, 1, &ct); + + if (is_void_ptr(&ct)) { + lua_pushlstring(L, data, (size_t) luaL_checknumber(L, 2)); + return 1; + + } else if (ct.type == INT8_TYPE && ct.pointers == 1) { + size_t sz; + + if (lua_isuserdata(L, 2)) { + ptrdiff_t val; + if (!cdata_tointeger(L, 2, &val)) { + type_error(L, 2, "int", 0, NULL); + } + sz = (size_t) val; + } else if (!lua_isnil(L, 2)) { + sz = (size_t) luaL_checknumber(L, 2); + + } else if (ct.is_array && !ct.is_variable_array) { + char* nul = memchr(data, '\0', ct.array_size); + sz = nul ? nul - data : ct.array_size; + + } else { + sz = strlen(data); + } + + lua_pushlstring(L, data, sz); + return 1; + } + + return luaL_error(L, "unable to convert cdata to string"); +} + +static int ffi_copy(lua_State* L) +{ + struct ctype ft, tt; + char *to, *from; + + setmintop(L, 3); + to = (char*) check_pointer(L, 1, &tt); + from = (char*) check_pointer(L, 2, &ft); + + if (!lua_isnoneornil(L, 3)) { + memcpy(to, from, (size_t) luaL_checknumber(L, 3)); + + } else if (ft.type == INT8_TYPE && ft.pointers == 1) { + size_t sz = ft.is_array ? ft.array_size : strlen(from); + memcpy(to, from, sz); + to[sz] = '\0'; + } + + return 0; +} + +static int ffi_fill(lua_State* L) +{ + struct ctype ct; + void* to; + size_t sz; + int val = 0; + + setmintop(L, 3); + to = check_pointer(L, 1, &ct); + sz = (size_t) luaL_checknumber(L, 2); + + if (!lua_isnoneornil(L, 3)) { + val = (int) luaL_checkinteger(L, 3); + } + + memset(to, val, sz); + return 0; +} + +static int ffi_abi(lua_State* L) +{ + luaL_checkstring(L, 1); + push_upval(L, &abi_key); + lua_pushvalue(L, 1); + lua_rawget(L, -2); + lua_pushboolean(L, lua_toboolean(L, -1)); + return 1; +} + +static void* find_symbol(lua_State* L, int modidx, const char* asmname) +{ + size_t i; + void** libs; + size_t num; + void* sym = NULL; + + libs = (void**) lua_touserdata(L, modidx); + num = lua_rawlen(L, modidx) / sizeof(void*); + + for (i = 0; i < num && sym == NULL; i++) { + if (libs[i]) { + sym = GetProcAddressA(libs[i], asmname); + } + } + + return sym; +} + +/* pushes the user table */ +static void* lookup_global(lua_State* L, int modidx, int nameidx, const char** pname, struct ctype* ct) +{ + int top = lua_gettop(L); + void* sym; + + modidx = lua_absindex(L, modidx); + nameidx = lua_absindex(L, nameidx); + + *pname = luaL_checkstring(L, nameidx); + + /* get the ctype */ + push_upval(L, &functions_key); + lua_pushvalue(L, nameidx); + lua_rawget(L, -2); + if (lua_isnil(L, -1)) { + luaL_error(L, "missing declaration for function/global %s", *pname); + return NULL; + } + + /* leave just the ct_usr on the stack */ + *ct = *(const struct ctype*) lua_touserdata(L, -1); + lua_getuservalue(L, -1); + lua_replace(L, top + 1); + lua_pop(L, 1); + + assert(lua_gettop(L) == top + 1); + + /* get the assembly name */ + push_upval(L, &asmname_key); + lua_pushvalue(L, nameidx); + lua_rawget(L, -2); + if (lua_isstring(L, -1)) { + *pname = lua_tostring(L, -1); + } + lua_pop(L, 2); + + sym = find_symbol(L, modidx, *pname); + + assert(lua_gettop(L) == top + 1); + return sym; +} + +static int ffi_debug(lua_State* L) +{ + lua_newtable(L); + push_upval(L, &ctype_mt_key); + lua_setfield(L, -2, "ctype_mt"); + push_upval(L, &cdata_mt_key); + lua_setfield(L, -2, "cdata_mt"); + push_upval(L, &constants_key); + lua_setfield(L, -2, "constants"); + push_upval(L, &types_key); + lua_setfield(L, -2, "types"); + push_upval(L, &gc_key); + lua_setfield(L, -2, "gc"); + push_upval(L, &callbacks_key); + lua_setfield(L, -2, "callbacks"); + push_upval(L, &functions_key); + lua_setfield(L, -2, "functions"); + push_upval(L, &abi_key); + lua_setfield(L, -2, "abi"); + push_upval(L, &next_unnamed_key); + lua_setfield(L, -2, "next_unnamed"); + return 1; +} + +static int do64(lua_State* L, int is_unsigned) +{ + lua_Number low, high; + struct ctype ct; + int64_t val; + + lua_settop(L, 2); + + if (!lua_isnil(L, 2)) { + high = luaL_checknumber(L, 1); + low = luaL_checknumber(L, 2); + } else { + high = 0; + low = luaL_checknumber(L, 1); + } + + val = ((int64_t) (uint32_t) high << 32) | (int64_t) (uint32_t) low; + + if (!is_unsigned && (high < 0 || low < 0)) { + val = -val; + } + + memset(&ct, 0, sizeof(ct)); + ct.type = INT64_TYPE; + ct.is_unsigned = is_unsigned; + ct.is_defined = 1; + ct.base_size = sizeof(int64_t); + push_number(L, (int64_t) val, 0, &ct); + + return 1; +} + +static int ffi_i64(lua_State* L) +{ return do64(L, 0); } + +static int ffi_u64(lua_State* L) +{ return do64(L, 1); } + +static const luaL_Reg cdata_mt[] = { + {"__gc", &cdata_gc}, + {"__index", &cdata_index}, + {"__newindex", &cdata_newindex}, + {"__add", &cdata_add}, + {"__sub", &cdata_sub}, + {"__mul", &cdata_mul}, + {"__div", &cdata_div}, + {"__mod", &cdata_mod}, + {"__pow", &cdata_pow}, + {"__unm", &cdata_unm}, + {"__eq", &cdata_eq}, + {"__lt", &cdata_lt}, + {"__le", &cdata_le}, + {"__tostring", &cdata_tostring}, + {"__concat", &cdata_concat}, + {"__len", &cdata_len}, + {"__pairs", &cdata_pairs}, + {"__ipairs", &cdata_ipairs}, + {NULL, NULL} +}; + +static const luaL_Reg ctype_mt[] = { + {"__call", &ctype_call}, + {"__new", &ctype_new}, + {"__tostring", &ctype_tostring}, + {NULL, NULL} +}; + +static const luaL_Reg ffi_reg[] = { + {"cdef", &ffi_cdef}, + {"new", &ffi_new}, + {"typeof", &ffi_typeof}, + {"cast", &ffi_cast}, + {"metatype", &ffi_metatype}, + {"gc", &ffi_gc}, + {"sizeof", &ffi_sizeof}, + {"alignof", &ffi_alignof}, + {"offsetof", &ffi_offsetof}, + {"istype", &ffi_istype}, + {"string", &ffi_string}, + {"copy", &ffi_copy}, + {"fill", &ffi_fill}, + {"abi", &ffi_abi}, + {"debug", &ffi_debug}, + {"i64", &ffi_i64}, + {"u64", &ffi_u64}, + {NULL, NULL} +}; + +/* leaves the usr table on the stack */ +static void push_builtin(lua_State* L, struct ctype* ct, const char* name, int type, int size, int align, int is_unsigned) +{ + memset(ct, 0, sizeof(*ct)); + ct->type = type; + ct->base_size = size; + ct->align_mask = align; + ct->is_defined = 1; + ct->is_unsigned = is_unsigned; + + if (IS_COMPLEX(type)) { + lua_newtable(L); + } else { + lua_pushnil(L); + } + + push_upval(L, &types_key); + push_ctype(L, -2, ct); + lua_setfield(L, -2, name); + lua_pop(L, 2); /* types, usr table */ +} + +static void push_builtin_undef(lua_State* L, struct ctype* ct, const char* name, int type) +{ + memset(ct, 0, sizeof(*ct)); + ct->type = type; + + push_upval(L, &types_key); + push_ctype(L, 0, ct); + lua_setfield(L, -2, name); + lua_pop(L, 1); /* types */ +} + +static void add_typedef(lua_State* L, const char* from, const char* to) +{ + struct ctype ct; + struct parser P; + P.line = 1; + P.align_mask = DEFAULT_ALIGN_MASK; + P.next = P.prev = from; + + push_upval(L, &types_key); + parse_type(L, &P, &ct); + parse_argument(L, &P, -1, &ct, NULL, NULL); + push_ctype(L, -1, &ct); + + /* stack is at +4: types, type usr, arg usr, ctype */ + + lua_setfield(L, -4, to); + lua_pop(L, 3); /* types, type usr, arg usr */ +} + +static int setup_upvals(lua_State* L) +{ + /* setup builtin types */ + { + complex_double* pc; + struct {char ch; uint16_t v;} a16; + struct {char ch; uint32_t v;} a32; + struct {char ch; uint64_t v;} a64; + struct {char ch; float v;} af; + struct {char ch; double v;} ad; +#ifdef HAVE_LONG_DOUBLE + struct {char ch; long double v;} ald; +#endif + struct {char ch; uintptr_t v;} aptr; + struct ctype ct; + struct {char ch; complex_float v;} cf; + struct {char ch; complex_double v;} cd; +#if defined HAVE_LONG_DOUBLE && defined HAVE_COMPLEX + struct {char ch; complex long double v;} cld; +#endif + + push_builtin(L, &ct, "void", VOID_TYPE, 0, 0, 0); + push_builtin(L, &ct, "bool", BOOL_TYPE, sizeof(_Bool), sizeof(_Bool) -1, 1); + push_builtin(L, &ct, "uint8_t", INT8_TYPE, sizeof(uint8_t), 0, 1); + push_builtin(L, &ct, "int8_t", INT8_TYPE, sizeof(int8_t), 0, 0); + push_builtin(L, &ct, "uint16_t", INT16_TYPE, sizeof(uint16_t), ALIGNOF(a16), 1); + push_builtin(L, &ct, "int16_t", INT16_TYPE, sizeof(int16_t), ALIGNOF(a16), 0); + push_builtin(L, &ct, "uint32_t", INT32_TYPE, sizeof(uint32_t), ALIGNOF(a32), 1); + push_builtin(L, &ct, "int32_t", INT32_TYPE, sizeof(int32_t), ALIGNOF(a32), 0); + push_builtin(L, &ct, "uint64_t", INT64_TYPE, sizeof(uint64_t), ALIGNOF(a64), 1); + push_builtin(L, &ct, "int64_t", INT64_TYPE, sizeof(int64_t), ALIGNOF(a64), 0); + push_builtin(L, &ct, "float", FLOAT_TYPE, sizeof(float), ALIGNOF(af), 0); + push_builtin(L, &ct, "double", DOUBLE_TYPE, sizeof(double), ALIGNOF(ad), 0); +#ifdef HAVE_LONG_DOUBLE + push_builtin(L, &ct, "long double", LONG_DOUBLE_TYPE, sizeof(long double), ALIGNOF(ald), 0); +#else + push_builtin_undef(L, &ct, "long double", LONG_DOUBLE_TYPE); +#endif + push_builtin(L, &ct, "uintptr_t", INTPTR_TYPE, sizeof(uintptr_t), ALIGNOF(aptr), 1); + push_builtin(L, &ct, "intptr_t", INTPTR_TYPE, sizeof(uintptr_t), ALIGNOF(aptr), 0); + push_builtin(L, &ct, "complex float", COMPLEX_FLOAT_TYPE, sizeof(complex_float), ALIGNOF(cf), 0); + push_builtin(L, &ct, "complex double", COMPLEX_DOUBLE_TYPE, sizeof(complex_double), ALIGNOF(cd), 0); +#if defined HAVE_LONG_DOUBLE && defined HAVE_COMPLEX + push_builtin(L, &ct, "complex long double", COMPLEX_LONG_DOUBLE_TYPE, sizeof(complex long double), ALIGNOF(cld), 0); +#else + push_builtin_undef(L, &ct, "complex long double", COMPLEX_LONG_DOUBLE_TYPE); +#endif + + /* add NULL and i constants */ + push_upval(L, &constants_key); + + memset(&ct, 0, sizeof(ct)); + ct.type = VOID_TYPE; + ct.is_defined = 1; + ct.pointers = 1; + ct.is_null = 1; + + /* add ffi.C.NULL */ + push_cdata(L, 0, &ct); + lua_setfield(L, -2, "NULL"); + + /* add ffi.NULL */ + push_cdata(L, 0, &ct); + lua_setfield(L, 1, "NULL"); + + memset(&ct, 0, sizeof(ct)); + ct.type = COMPLEX_DOUBLE_TYPE; + ct.is_defined = 1; + ct.base_size = sizeof(complex_double); + pc = (complex_double*) push_cdata(L, 0, &ct); +#ifdef HAVE_COMPLEX + *pc = 1i; +#else + pc->real = 0; + pc->imag = 1; +#endif + lua_setfield(L, -2, "i"); + + lua_pop(L, 1); /* constants */ + } + + assert(lua_gettop(L) == 1); + + /* setup builtin typedefs */ + { + add_typedef(L, "bool", "_Bool"); + + if (sizeof(uint32_t) == sizeof(size_t)) { + add_typedef(L, "uint32_t", "size_t"); + add_typedef(L, "int32_t", "ssize_t"); + } else if (sizeof(uint64_t) == sizeof(size_t)) { + add_typedef(L, "uint64_t", "size_t"); + add_typedef(L, "int64_t", "ssize_t"); + } + + if (sizeof(int32_t) == sizeof(intptr_t)) { + add_typedef(L, "int32_t", "intptr_t"); + add_typedef(L, "int32_t", "ptrdiff_t"); + } else if (sizeof(int64_t) == sizeof(intptr_t)) { + add_typedef(L, "int64_t", "intptr_t"); + add_typedef(L, "int64_t", "ptrdiff_t"); + } + + if (sizeof(uint8_t) == sizeof(wchar_t)) { + add_typedef(L, "uint8_t", "wchar_t"); + } else if (sizeof(uint16_t) == sizeof(wchar_t)) { + add_typedef(L, "uint16_t", "wchar_t"); + } else if (sizeof(uint32_t) == sizeof(wchar_t)) { + add_typedef(L, "uint32_t", "wchar_t"); + } + + if (sizeof(va_list) == sizeof(char*)) { + add_typedef(L, "char*", "va_list"); + } else { + struct {char ch; va_list v;} av; + lua_pushfstring(L, "struct {char data[%d] __attribute__((align(%d)));}", (int) sizeof(va_list), (int) ALIGNOF(av) + 1); + add_typedef(L, lua_tostring(L, -1), "va_list"); + lua_pop(L, 1); + } + + add_typedef(L, "va_list", "__builtin_va_list"); + add_typedef(L, "va_list", "__gnuc_va_list"); + } + + assert(lua_gettop(L) == 1); + + /* setup ABI params table */ + push_upval(L, &abi_key); + +#if defined ARCH_X86 || defined ARCH_ARM + lua_pushboolean(L, 1); + lua_setfield(L, -2, "32bit"); +#elif defined ARCH_X64 || defined ARCH_PPC64 + lua_pushboolean(L, 1); + lua_setfield(L, -2, "64bit"); +#else +#error +#endif + +#if defined ARCH_X86 || defined ARCH_X64 || defined ARCH_ARM || defined ARCH_PPC64 + lua_pushboolean(L, 1); + lua_setfield(L, -2, "le"); +#else +#error +#endif + +#if defined ARCH_X86 || defined ARCH_X64 || defined ARCH_PPC64 + lua_pushboolean(L, 1); + lua_setfield(L, -2, "fpu"); +#elif defined ARCH_ARM + lua_pushboolean(L, 1); + lua_setfield(L, -2, "softfp"); +#else +#error +#endif + lua_pop(L, 1); /* abi tbl */ + + + /* GC table - shouldn't pin cdata values */ + push_upval(L, &gc_key); + lua_newtable(L); + lua_pushliteral(L, "k"); + lua_setfield(L, -2, "__mode"); + lua_setmetatable(L, -2); + lua_pop(L, 1); /* gc table */ + + + /* ffi.os */ +#if defined OS_CE + lua_pushliteral(L, "WindowsCE"); +#elif defined OS_WIN + lua_pushliteral(L, "Windows"); +#elif defined OS_OSX + lua_pushliteral(L, "OSX"); +#elif defined OS_LINUX + lua_pushliteral(L, "Linux"); +#elif defined OS_BSD + lua_pushliteral(L, "BSD"); +#elif defined OS_POSIX + lua_pushliteral(L, "POSIX"); +#else + lua_pushliteral(L, "Other"); +#endif + lua_setfield(L, 1, "os"); + + + /* ffi.arch */ +#if defined ARCH_X86 + lua_pushliteral(L, "x86"); +#elif defined ARCH_X64 + lua_pushliteral(L, "x64"); +#elif defined ARCH_ARM + lua_pushliteral(L, "arm"); +#elif defined ARCH_PPC64 + lua_pushliteral(L, "ppc64"); +#else +# error +#endif + lua_setfield(L, 1, "arch"); + + assert(lua_gettop(L) == 1); + + return 0; +} + +static void setup_mt(lua_State* L, const luaL_Reg* mt, int upvals) +{ + lua_pushboolean(L, 1); + lua_setfield(L, -upvals-2, "__metatable"); + luaL_setfuncs(L, mt, upvals); +} + +LUA_API int luaopen_lffi(lua_State* L) +{ + lua_settop(L, 0); + + lua_newtable(L); + set_upval(L, &niluv_key); + + lua_newtable(L); + setup_mt(L, ctype_mt, 0); + set_upval(L, &ctype_mt_key); + + lua_newtable(L); + set_upval(L, &callbacks_key); + + lua_newtable(L); + set_upval(L, &gc_key); + + lua_newtable(L); + push_upval(L, &callbacks_key); + push_upval(L, &gc_key); + setup_mt(L, cdata_mt, 2); + set_upval(L, &cdata_mt_key); + + lua_newtable(L); + set_upval(L, &constants_key); + + lua_newtable(L); + set_upval(L, &types_key); + + lua_newtable(L); + set_upval(L, &functions_key); + + lua_newtable(L); + set_upval(L, &asmname_key); + + lua_newtable(L); + set_upval(L, &abi_key); + + lua_pushinteger(L, 1); + set_upval(L, &next_unnamed_key); + + assert(lua_gettop(L) == 0); + + /* ffi table */ + lua_newtable(L); + luaL_setfuncs(L, ffi_reg, 0); + + /* setup_upvals(ffi tbl) */ + lua_pushcfunction(L, &setup_upvals); + lua_pushvalue(L, 1); + lua_call(L, 1, 0); + + assert(lua_gettop(L) == 1); + + lua_getglobal(L, "tonumber"); + lua_pushcclosure(L, &ffi_number, 1); + lua_pushvalue(L, -1); + lua_setglobal(L, "tonumber"); + lua_setfield(L, -2, "number"); /* ffi.number */ + + lua_getglobal(L, "type"); + lua_pushcclosure(L, &ffi_type, 1); + lua_pushvalue(L, -1); + lua_setglobal(L, "type"); + lua_setfield(L, -2, "type"); /* ffi.type */ + return 1; +} diff --git a/luaclib/src/lffi/ffi.h b/luaclib/src/lffi/ffi.h new file mode 100644 index 00000000..83278b1c --- /dev/null +++ b/luaclib/src/lffi/ffi.h @@ -0,0 +1,435 @@ +/* vim: ts=4 sw=4 sts=4 et tw=78 + * Portions copyright (c) 2015-present, Facebook, Inc. All rights reserved. + * Portions copyright (c) 2011 James R. McKaskill. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#pragma once + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +# include +# include +# include +} +# define EXTERN_C extern "C" +#else +# include +# include +# include +# define EXTERN_C extern +#endif + +#ifdef _WIN32 +#include +#else +#include +#include +#include +#include +#ifndef HAVE_COMPLEX + #define HAVE_COMPLEX + #include +#endif +#endif + +#ifndef NDEBUG +#define DASM_CHECKS +#endif + +struct jit; +#define Dst_DECL struct jit* Dst +#define Dst_REF (Dst->ctx) +#define DASM_EXTERN(a,b,c,d) get_extern(a,b,c,d) + +#if defined LUA_FFI_BUILD_AS_DLL +# define EXPORT __declspec(dllexport) +#elif defined __GNUC__ +# define EXPORT __attribute__((visibility("default"))) +#else +# define EXPORT +#endif + +static int lua_absindex2(lua_State* L, int idx) { + return (LUA_REGISTRYINDEX <= idx && idx < 0) + ? lua_gettop(L) + idx + 1 + : idx; +} +/* use our own version of lua_absindex such that lua_absindex(L, 0) == 0 */ +#define lua_absindex(L, idx) lua_absindex2(L, idx) + +#if LUA_VERSION_NUM == 501 +static void lua_callk(lua_State *L, int nargs, int nresults, int ctx, lua_CFunction k) +{ + lua_call(L, nargs, nresults); +} +/* +** set functions from list 'l' into table at top - 'nup'; each +** function gets the 'nup' elements at the top as upvalues. +** Returns with only the table at the stack. +*/ +static void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { + luaL_checkstack(L, nup, "too many upvalues"); + for (; l && l->name; l++) { /* fill the table with given functions */ + int i; + for (i = 0; i < nup; i++) /* copy upvalues to the top */ + lua_pushvalue(L, -nup); + lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ + lua_setfield(L, -(nup + 2), l->name); + } + lua_pop(L, nup); /* remove upvalues */ +} +#define lua_setuservalue lua_setfenv +#define lua_getuservalue lua_getfenv +#define lua_rawlen lua_objlen +static char* luaL_prepbuffsize(luaL_Buffer* B, size_t sz) { + if (sz > LUAL_BUFFERSIZE) { + luaL_error(B->L, "string too long"); + } + return luaL_prepbuffer(B); +} +#elif LUA_VERSION_NUM >= 503 +static void (lua_remove)(lua_State *L, int idx) { + lua_remove(L, idx); +} +#endif + +/* architectures */ +#if defined _WIN32 && defined UNDER_CE +# define OS_CE +#elif defined _WIN32 +# define OS_WIN +#elif defined __APPLE__ && defined __MACH__ +# define OS_OSX +#elif defined __linux__ +# define OS_LINUX +#elif defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__ +# define OS_BSD +#elif defined unix || defined __unix__ || defined __unix || defined _POSIX_VERSION || defined _XOPEN_VERSION +# define OS_POSIX +#endif + +/* architecture */ +#if defined __i386__ || defined _M_IX86 +# define ARCH_X86 +#elif defined __amd64__ || defined _M_X64 +# define ARCH_X64 +#elif defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __aarch64__ || defined _M_ARM +# define ARCH_ARM +#elif defined __powerpc64__ +# define ARCH_PPC64 +#else +# error +#endif + + +#ifdef _WIN32 + +# ifdef UNDER_CE + static void* DoLoadLibraryA(const char* name) { + wchar_t buf[MAX_PATH]; + int sz = MultiByteToWideChar(CP_UTF8, 0, name, -1, buf, 512); + if (sz > 0) { + buf[sz] = 0; + return LoadLibraryW(buf); + } else { + return NULL; + } + } +# define LoadLibraryA DoLoadLibraryA +# else +# define GetProcAddressA GetProcAddress +# endif + +# define LIB_FORMAT_1 "%s.dll" +# define AllocPage(size) VirtualAlloc(NULL, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE) +# define FreePage(data, size) VirtualFree(data, 0, MEM_RELEASE) +# define EnableExecute(data, size) do {DWORD old; VirtualProtect(data, size, PAGE_EXECUTE, &old); FlushInstructionCache(GetCurrentProcess(), data, size);} while (0) +# define EnableWrite(data, size) do {DWORD old; VirtualProtect(data, size, PAGE_READWRITE, &old);} while (0) + +#else +#ifdef OS_OSX +# define LIB_FORMAT_1 "%s.dylib" +# define LIB_FORMAT_2 "lib%s.dylib" +#else +# define LIB_FORMAT_1 "%s.so" +# define LIB_FORMAT_2 "lib%s.so" +#endif +# define LoadLibraryA(name) dlopen(name, RTLD_LAZY | RTLD_GLOBAL) +# define GetProcAddressA(lib, name) dlsym(lib, name) +# define AllocPage(size) mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0) +# define FreePage(data, size) munmap(data, size) +# define EnableExecute(data, size) mprotect(data, size, PROT_READ|PROT_EXEC) +# define EnableWrite(data, size) mprotect(data, size, PROT_READ|PROT_WRITE) +#endif + +#if defined ARCH_X86 || defined ARCH_X64 +#define ALLOW_MISALIGNED_ACCESS +#endif + +struct token; + +struct parser { + int line; + const char* next; + const char* prev; + unsigned align_mask; +}; + +struct page { + size_t size; + size_t off; + size_t freed; +}; + +struct jit { + lua_State* L; + int32_t last_errno; + size_t pagenum; + struct page** pages; + size_t align_page_size; + void** globals; + int function_extern; + void* lua_dll; + void* kernel32_dll; +}; + +#define ALIGN_DOWN(PTR, MASK) \ + (((uintptr_t) (PTR)) & (~ ((uintptr_t) (MASK)) )) +#define ALIGN_UP(PTR, MASK) \ + (( ((uintptr_t) (PTR)) + ((uintptr_t) (MASK)) ) & (~ ((uintptr_t) (MASK)) )) + +/* struct cdata/struct ctype */ + +#define PTR_ALIGN_MASK (sizeof(void*) - 1) +#define FUNCTION_ALIGN_MASK (sizeof(void (*)()) - 1) +#define DEFAULT_ALIGN_MASK 7 + +#ifdef OS_OSX +/* TODO: figure out why the alignof trick doesn't work on OS X */ +#define ALIGNED_DEFAULT 7 +#elif defined __GNUC__ +#define ALIGNED_DEFAULT (__alignof__(void* __attribute__((aligned))) - 1) +#else +#define ALIGNED_DEFAULT PTR_ALIGN_MASK +#endif + +extern int ctype_mt_key; +extern int cdata_mt_key; +extern int constants_key; +extern int types_key; +extern int gc_key; +extern int callbacks_key; +extern int functions_key; +extern int abi_key; +extern int next_unnamed_key; +extern int niluv_key; +extern int asmname_key; + +int equals_upval(lua_State* L, int idx, int* key); +void push_upval(lua_State* L, int* key); +void set_upval(lua_State* L, int* key); + +/* both ctype and cdata are stored as userdatas + * + * usr value is a table shared between the related subtypes which has: + * name -> member ctype (for structs and unions) + * +ves -> member ctype - in memory order (for structs) + * +ves -> argument ctype (for function prototypes) + * 0 -> return ctype (for function prototypes) + * light userdata -> misc + */ + +enum { + C_CALL, + STD_CALL, + FAST_CALL, +}; + +enum { + INVALID_TYPE, + VOID_TYPE, + FLOAT_TYPE, + DOUBLE_TYPE, + LONG_DOUBLE_TYPE, + COMPLEX_FLOAT_TYPE, + COMPLEX_DOUBLE_TYPE, + COMPLEX_LONG_DOUBLE_TYPE, + BOOL_TYPE, + INT8_TYPE, + INT16_TYPE, + INT32_TYPE, + INT64_TYPE, + INTPTR_TYPE, + ENUM_TYPE, + UNION_TYPE, + STRUCT_TYPE, + FUNCTION_TYPE, + FUNCTION_PTR_TYPE, +}; + +#define IS_CHAR_UNSIGNED (((char) -1) > 0) +#define IS_COMPLEX(type) ((type) == COMPLEX_FLOAT_TYPE || (type) == COMPLEX_DOUBLE_TYPE) + +#define POINTER_BITS 2 +#define POINTER_MAX ((1 << POINTER_BITS) - 1) + +#define ALIGNOF(S) ((int) ((char*) &S.v - (char*) &S - 1)) + +/* Note: if adding a new member that is associated with a struct/union + * definition then it needs to be copied over in ctype.c:set_defined for when + * we create types based off of the declaration alone. + * + * Since this is used as a header for every ctype and cdata, and we create a + * ton of them on the stack, we try and minimise its size. + */ +struct ctype { + size_t base_size; /* size of the base type in bytes */ + + union { + /* valid if is_bitfield */ + struct { + /* size of bitfield in bits */ + unsigned bit_size : 7; + /* offset within the current byte between 0-63 */ + unsigned bit_offset : 6; + }; + /* Valid if is_array */ + size_t array_size; + /* Valid for is_variable_struct or is_variable_array. If + * variable_size_known (only used for is_variable_struct) then this is + * the total increment otherwise this is the per element increment. + */ + size_t variable_increment; + }; + size_t offset; + unsigned align_mask : 4; /* as (align bytes - 1) eg 7 gives 8 byte alignment */ + unsigned pointers : POINTER_BITS; /* number of dereferences to get to the base type including +1 for arrays */ + unsigned const_mask : POINTER_MAX + 1; /* const pointer mask, LSB is current pointer, +1 for the whether the base type is const */ + unsigned type : 5; /* value given by type enum above */ + unsigned is_reference : 1; + unsigned is_array : 1; + unsigned is_defined : 1; + unsigned is_null : 1; + unsigned has_member_name : 1; + unsigned calling_convention : 2; + unsigned has_var_arg : 1; + unsigned is_variable_array : 1; /* set for variable array types where we don't know the variable size yet */ + unsigned is_variable_struct : 1; + unsigned variable_size_known : 1; /* used for variable structs after we know the variable size */ + unsigned is_bitfield : 1; + unsigned has_bitfield : 1; + unsigned is_jitted : 1; + unsigned is_packed : 1; + unsigned is_unsigned : 1; +}; + +#ifdef _MSC_VER +__declspec(align(16)) +#endif +struct cdata { + const struct ctype type +#ifdef __GNUC__ + __attribute__ ((aligned(16))) +#endif + ; +}; + +#ifdef HAVE_COMPLEX +typedef double complex complex_double; +typedef float complex complex_float; +static complex_double mk_complex_double(double real, double imag) { + return real + imag * 1i; +} +static complex_double mk_complex_float(double real, double imag) { + return real + imag * 1i; +} +#else +typedef struct { + double real, imag; +} complex_double; + +typedef struct { + float real, imag; +} complex_float; + +static complex_double mk_complex_double(double real, double imag) { + complex_double ret = { real, imag }; + return ret; +} +static complex_float mk_complex_float(double real, double imag) { + complex_float ret = { real, imag }; + return ret; +} +static double creal(complex_double c) { + return c.real; +} +static float crealf(complex_float c) { + return c.real; +} + +static double cimag(complex_double c) { + return c.imag; +} +static float cimagf(complex_float c) { + return c.imag; +} +#endif + +#define CALLBACK_FUNC_USR_IDX 1 + +void set_defined(lua_State* L, int ct_usr, struct ctype* ct); +struct ctype* push_ctype(lua_State* L, int ct_usr, const struct ctype* ct); +void* push_cdata(lua_State* L, int ct_usr, const struct ctype* ct); /* called from asm */ +void check_ctype(lua_State* L, int idx, struct ctype* ct); +void* to_cdata(lua_State* L, int idx, struct ctype* ct); +void* check_cdata(lua_State* L, int idx, struct ctype* ct); +size_t ctype_size(lua_State* L, const struct ctype* ct); + +int parse_type(lua_State* L, struct parser* P, struct ctype* type); +void parse_argument(lua_State* L, struct parser* P, int ct_usr, struct ctype* type, struct token* name, struct parser* asmname); +void push_type_name(lua_State* L, int usr, const struct ctype* ct); + +int push_user_mt(lua_State* L, int ct_usr, const struct ctype* ct); + +int ffi_cdef(lua_State* L); + +int x86_return_size(lua_State* L, int usr, const struct ctype* ct); +void compile_globals(struct jit* jit, lua_State* L); +int get_extern(struct jit* jit, uint8_t* addr, int idx, int type); + +/* WARNING: assembly needs to be updated for prototype changes of these functions */ +int check_bool(lua_State* L, int idx); +double check_double(lua_State* L, int idx); +double check_complex_imag(lua_State* L, int idx); +float check_float(lua_State* L, int idx); +uint64_t check_uint64(lua_State* L, int idx); +int64_t check_int64(lua_State* L, int idx); +int32_t check_int32(lua_State* L, int idx); +uint32_t check_uint32(lua_State* L, int idx); +uintptr_t check_uintptr(lua_State* L, int idx); +int32_t check_enum(lua_State* L, int idx, int to_usr, const struct ctype* tt); +/* these two will always push a value so that we can create structs/functions on the fly */ +void* check_typed_pointer(lua_State* L, int idx, int to_usr, const struct ctype* tt); +complex_double check_complex_double(lua_State* L, int idx); +complex_float check_complex_float(lua_State* L, int idx); + +void unpack_varargs_stack(lua_State* L, int first, int last, char* to); +void unpack_varargs_reg(lua_State* L, int first, int last, char* to); + +void unpack_varargs_stack_skip(lua_State* L, int first, int last, int ints_to_skip, int floats_to_skip, char* to); +void unpack_varargs_float(lua_State* L, int first, int last, int max, char* to); +void unpack_varargs_int(lua_State* L, int first, int last, int max, char* to); diff --git a/luaclib/src/lffi/makefile b/luaclib/src/lffi/makefile new file mode 100644 index 00000000..0fd4cd37 --- /dev/null +++ b/luaclib/src/lffi/makefile @@ -0,0 +1,27 @@ +.PHONY : build rebuild clean + +default : + @echo "=======================================" + @echo "Please use 'make build' command to build it.." + @echo "Please use 'make rebuild' command to build it.." + @echo "Please use 'make clean' command to clean all." + @echo "=======================================" + +CC = cc + +INCLUDE = -I/usr/local/include +LIB = -L/usr/local/lib -L../ -L../../../ + +CFLAGS = -O3 -Wall -shared -fPIC -Wno-unused-function -fno-strict-aliasing +DLL = -lcore + +build: + $(CC) -o lffi.so ffi.c parser.c ctype.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) + mv *.so ../../ + +rebuild: + $(CC) -o lffi.so ffi.c parser.c ctype.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) + mv *.so ../../ + +clean: + rm -rf *.o *.so diff --git a/luaclib/src/lffi/parser.c b/luaclib/src/lffi/parser.c new file mode 100644 index 00000000..b170b058 --- /dev/null +++ b/luaclib/src/lffi/parser.c @@ -0,0 +1,2614 @@ +/* vim: ts=4 sw=4 sts=4 et tw=78 + * Portions copyright (c) 2015-present, Facebook, Inc. All rights reserved. + * Portions copyright (c) 2011 James R. McKaskill. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "ffi.h" + +#define IS_CONST(tok) (IS_LITERAL(tok, "const") || IS_LITERAL(tok, "__const") || IS_LITERAL(tok, "__const__")) +#define IS_VOLATILE(tok) (IS_LITERAL(tok, "volatile") || IS_LITERAL(tok, "__volatile") || IS_LITERAL(tok, "__volatile__")) +#define IS_RESTRICT(tok) (IS_LITERAL(tok, "restrict") || IS_LITERAL(tok, "__restrict") || IS_LITERAL(tok, "__restrict__")) +#define IS_INLINE(tok) (IS_LITERAL(tok, "inline") || IS_LITERAL(tok, "__inline") || IS_LITERAL(tok, "__inline__")) + +enum etoken { + TOK_NIL, + TOK_NUMBER, + TOK_STRING, + TOK_TOKEN, + + /* the order of these values must match the token strings in lex.c */ + + TOK_3_BEGIN, + TOK_VA_ARG, + + TOK_2_BEGIN, + TOK_LEFT_SHIFT, TOK_RIGHT_SHIFT, TOK_LOGICAL_AND, TOK_LOGICAL_OR, TOK_LESS_EQUAL, + TOK_GREATER_EQUAL, TOK_EQUAL, TOK_NOT_EQUAL, + + TOK_1_BEGIN, + TOK_OPEN_CURLY, TOK_CLOSE_CURLY, TOK_SEMICOLON, TOK_COMMA, TOK_COLON, + TOK_ASSIGN, TOK_OPEN_PAREN, TOK_CLOSE_PAREN, TOK_OPEN_SQUARE, TOK_CLOSE_SQUARE, + TOK_DOT, TOK_AMPERSAND, TOK_LOGICAL_NOT, TOK_BITWISE_NOT, TOK_MINUS, + TOK_PLUS, TOK_STAR, TOK_DIVIDE, TOK_MODULUS, TOK_LESS, + TOK_GREATER, TOK_BITWISE_XOR, TOK_BITWISE_OR, TOK_QUESTION, TOK_POUND, + + TOK_REFERENCE = TOK_AMPERSAND, + TOK_MULTIPLY = TOK_STAR, + TOK_BITWISE_AND = TOK_AMPERSAND, +}; + +struct token { + enum etoken type; + int64_t integer; + const char* str; + size_t size; +}; + +#define IS_LITERAL(TOK, STR) \ + (((TOK).size == sizeof(STR) - 1) && 0 == memcmp((TOK).str, STR, sizeof(STR) - 1)) + +/* the order of tokens _must_ match the order of the enum etoken enum */ + +static char tok3[][4] = { + "...", /* unused ">>=", "<<=", */ +}; + +static char tok2[][3] = { + "<<", ">>", "&&", "||", "<=", + ">=", "==", "!=", + /* unused "+=", "-=", "*=", "/=", "%=", "&=", "^=", "|=", "++", "--", "->", "::", */ +}; + +static char tok1[] = { + '{', '}', ';', ',', ':', + '=', '(', ')', '[', ']', + '.', '&', '!', '~', '-', + '+', '*', '/', '%', '<', + '>', '^', '|', '?', '#' +}; + +static int next_token(lua_State* L, struct parser* P, struct token* tok) +{ + size_t i; + const char* s = P->next; + + /* UTF8 BOM */ + if (s[0] == '\xEF' && s[1] == '\xBB' && s[2] == '\xBF') { + s += 3; + } + + /* consume whitespace and comments */ + for (;;) { + /* consume whitespace */ + while(*s == '\t' || *s == '\n' || *s == ' ' || *s == '\v' || *s == '\r') { + if (*s == '\n') { + P->line++; + } + s++; + } + + /* consume comments */ + if (*s == '/' && *(s+1) == '/') { + + s = strchr(s, '\n'); + if (!s) { + luaL_error(L, "non-terminated comment"); + } + + } else if (*s == '/' && *(s+1) == '*') { + s += 2; + + for (;;) { + if (s[0] == '\0') { + luaL_error(L, "non-terminated comment"); + } else if (s[0] == '*' && s[1] == '/') { + s += 2; + break; + } else if (s[0] == '\n') { + P->line++; + } + s++; + } + + } else if (*s == '\0') { + tok->type = TOK_NIL; + return 0; + + } else { + break; + } + } + + P->prev = s; + + for (i = 0; i < sizeof(tok3) / sizeof(tok3[0]); i++) { + if (s[0] == tok3[i][0] && s[1] == tok3[i][1] && s[2] == tok3[i][2]) { + tok->type = (enum etoken) (TOK_3_BEGIN + 1 + i); + P->next = s + 3; + goto end; + } + } + + for (i = 0; i < sizeof(tok2) / sizeof(tok2[0]); i++) { + if (s[0] == tok2[i][0] && s[1] == tok2[i][1]) { + tok->type = (enum etoken) (TOK_2_BEGIN + 1 + i); + P->next = s + 2; + goto end; + } + } + + for (i = 0; i < sizeof(tok1) / sizeof(tok1[0]); i++) { + if (s[0] == tok1[i]) { + tok->type = (enum etoken) (TOK_1_BEGIN + 1 + i); + P->next = s + 1; + goto end; + } + } + + if (*s == '.' || *s == '-' || ('0' <= *s && *s <= '9')) { + /* number */ + tok->type = TOK_NUMBER; + + /* split out the negative case so we get the full range of bits for + * unsigned (eg to support 0xFFFFFFFF where sizeof(long) == 4) + */ + if (*s == '-') { + tok->integer = strtol(s, (char**) &s, 0); + } else { + tok->integer = strtoul(s, (char**) &s, 0); + } + + while (*s == 'u' || *s == 'U' || *s == 'l' || *s == 'L') { + s++; + } + + P->next = s; + goto end; + + } else if (*s == '\'' || *s == '\"') { + /* "..." or '...' */ + char quote = *s; + s++; /* jump over " */ + + tok->type = TOK_STRING; + tok->str = s; + + while (*s != quote) { + + if (*s == '\0' || (*s == '\\' && *(s+1) == '\0')) { + return luaL_error(L, "string not finished"); + } + + if (*s == '\\') { + s++; + } + + s++; + } + + tok->size = s - tok->str; + s++; /* jump over " */ + P->next = s; + goto end; + + } else if (('a' <= *s && *s <= 'z') || ('A' <= *s && *s <= 'Z') || *s == '_') { + /* tokens */ + tok->type = TOK_TOKEN; + tok->str = s; + + while (('a' <= *s && *s <= 'z') || ('A' <= *s && *s <= 'Z') || *s == '_' || ('0' <= *s && *s <= '9')) { + s++; + } + + tok->size = s - tok->str; + P->next = s; + goto end; + + } else { + return luaL_error(L, "invalid character %d", P->line); + } + +end: + /*fprintf(stderr, "token %d %d %.*s %.10s\n", tok->type, (int) tok->size, (tok->type == TOK_TOKEN || tok->type == TOK_STRING) ? (int) tok->size : 0, tok->str, P->next);*/ + return 1; +} + +#define require_token(L, P, tok) require_token_line(L, P, tok, __FILE__, __LINE__) + +static void require_token_line(lua_State* L, struct parser* P, struct token* tok, const char* file, int line) +{ + if (!next_token(L, P, tok)) { + luaL_error(L, "unexpected end on line %s:%d", file, line); + } +} + +static void check_token(lua_State* L, struct parser* P, int type, const char* str, const char* err, ...) +{ + struct token tok; + if (!next_token(L, P, &tok) || tok.type != type || (tok.type == TOK_TOKEN && (tok.size != strlen(str) || memcmp(tok.str, str, tok.size) != 0))) { + va_list ap; + va_start(ap, err); + lua_pushvfstring(L, err, ap); + lua_error(L); + } +} + +static void put_back(struct parser* P) +{ P->next = P->prev; } + + +int64_t calculate_constant(lua_State* L, struct parser* P); + +static int g_name_key; +static int g_front_name_key; +static int g_back_name_key; + +#ifndef max +#define max(a,b) ((a) < (b) ? (b) : (a)) +#endif + +#ifndef min +#define min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +enum test {TEST}; + +/* Parses an enum definition from after the open curly through to the close + * curly. Expects the user table to be on the top of the stack + */ +static int parse_enum(lua_State* L, struct parser* P, struct ctype* type) +{ + struct token tok; + int value = -1; + int ct_usr = lua_gettop(L); + + for (;;) { + require_token(L, P, &tok); + + assert(lua_gettop(L) == ct_usr); + + if (tok.type == TOK_CLOSE_CURLY) { + break; + } else if (tok.type != TOK_TOKEN) { + return luaL_error(L, "unexpected token in enum at line %d", P->line); + } + + lua_pushlstring(L, tok.str, tok.size); + + require_token(L, P, &tok); + + if (tok.type == TOK_COMMA || tok.type == TOK_CLOSE_CURLY) { + /* we have an auto calculated enum value */ + value++; + } else if (tok.type == TOK_ASSIGN) { + /* we have an explicit enum value */ + value = (int) calculate_constant(L, P); + require_token(L, P, &tok); + } else { + return luaL_error(L, "unexpected token in enum at line %d", P->line); + } + + assert(lua_gettop(L) == ct_usr + 1); + + /* add the enum value to the constants table */ + push_upval(L, &constants_key); + lua_pushvalue(L, -2); + lua_pushinteger(L, value); + lua_rawset(L, -3); + lua_pop(L, 1); + + assert(lua_gettop(L) == ct_usr + 1); + + /* add the enum value to the enum usr value table */ + lua_pushinteger(L, value); + lua_rawset(L, ct_usr); + + if (tok.type == TOK_CLOSE_CURLY) { + break; + } else if (tok.type != TOK_COMMA) { + return luaL_error(L, "unexpected token in enum at line %d", P->line); + } + } + + type->base_size = sizeof(enum test); + type->align_mask = sizeof(enum test) - 1; + + assert(lua_gettop(L) == ct_usr); + return 0; +} + +static void calculate_member_position(lua_State* L, struct parser* P, struct ctype* ct, struct ctype* mt, int* pbit_offset, int* pbitfield_type) +{ + int bit_offset = *pbit_offset; + + if (ct->type == UNION_TYPE) { + size_t msize; + + if (mt->is_variable_struct || mt->is_variable_array) { + luaL_error(L, "NYI: variable sized members in unions"); + return; + + } else if (mt->is_bitfield) { + msize = (mt->align_mask + 1); +#ifdef _WIN32 + /* MSVC has a bug where it doesn't update the alignment of + * a union for bitfield members. */ + mt->align_mask = 0; +#endif + + } else if (mt->is_array) { + msize = mt->array_size * (mt->pointers > 1 ? sizeof(void*) : mt->base_size); + + } else { + msize = mt->pointers ? sizeof(void*) : mt->base_size; + } + + ct->base_size = max(ct->base_size, msize); + + } else if (mt->is_bitfield) { + if (mt->has_member_name && mt->bit_size == 0) { + luaL_error(L, "zero length bitfields must be unnamed on line %d", P->line); + } + +#if defined _WIN32 + /* MSVC uses a seperate storage unit for each size. This is aligned + * before the first bitfield. :0 finishes up the storage unit using + * the greater alignment of the storage unit or the type used with the + * :0. This is equivalent to the :0 always creating a new storage + * unit, but not necesserily using it yet. + */ + + if (*pbitfield_type == -1 && mt->bit_size == 0) { + /* :0 not after a bitfield are ignored */ + return; + } + + { + int different_storage = mt->align_mask != *pbitfield_type; + int no_room_left = bit_offset + mt->bit_size > (mt->align_mask + 1) * CHAR_BIT; + + if (different_storage || no_room_left || !mt->bit_size) { + ct->base_size += (bit_offset + CHAR_BIT - 1) / CHAR_BIT; + bit_offset = 0; + if (*pbitfield_type >= 0) { + ct->base_size = ALIGN_UP(ct->base_size, *pbitfield_type); + } + ct->base_size = ALIGN_UP(ct->base_size, mt->align_mask); + } + } + + mt->bit_offset = bit_offset; + mt->offset = ct->base_size; + + *pbitfield_type = mt->align_mask; + bit_offset += mt->bit_size; + +// #elif defined OS_OSX +// /* OSX doesn't use containers and bitfields are not aligned. So +// * bitfields never add any padding, except for :0 which still forces +// * an alignment based off the type used with the :0 */ +// if (mt->bit_size) { +// mt->offset = ct->base_size; +// mt->bit_offset = bit_offset; +// bit_offset += mt->bit_size; +// ct->base_size += bit_offset / CHAR_BIT; +// bit_offset = bit_offset % CHAR_BIT; +// } else { +// ct->base_size += (bit_offset + CHAR_BIT - 1) / CHAR_BIT; +// ct->base_size = ALIGN_UP(ct->base_size, mt->align_mask); +// bit_offset = 0; +// } + +// if (!mt->has_member_name) { +// /* unnamed bitfields don't update the struct alignment */ +// mt->align_mask = 0; +// } + +#elif defined __GNUC__ + /* GCC tries to pack bitfields in as close as much as possible, but + * still making sure that they don't cross alignment boundaries. + * :0 forces an alignment based off the type used with the :0 + */ + + int bits_used = (ct->base_size - ALIGN_DOWN(ct->base_size, mt->align_mask)) * CHAR_BIT + bit_offset; + int need_to_realign = bits_used + mt->bit_size > mt->base_size * CHAR_BIT; + + if (!mt->is_packed && (!mt->bit_size || need_to_realign)) { + ct->base_size += (bit_offset + CHAR_BIT - 1) / CHAR_BIT; + ct->base_size = ALIGN_UP(ct->base_size, mt->align_mask); + bit_offset = 0; + } + + mt->bit_offset = bit_offset; + mt->offset = ct->base_size; + + bit_offset += mt->bit_size; + ct->base_size += bit_offset / CHAR_BIT; + bit_offset = bit_offset % CHAR_BIT; + + /* unnamed bitfields don't update the struct alignment */ + if (!mt->has_member_name) { + mt->align_mask = 0; + } +#else +#error +#endif + + } else { + /* finish up the current bitfield storage unit */ + ct->base_size += (bit_offset + CHAR_BIT - 1) / CHAR_BIT; + bit_offset = 0; + + if (*pbitfield_type >= 0) { + ct->base_size = ALIGN_UP(ct->base_size, *pbitfield_type); + } + + *pbitfield_type = -1; + + ct->base_size = ALIGN_UP(ct->base_size, mt->align_mask); + mt->offset = ct->base_size; + + if (mt->is_variable_array) { + ct->is_variable_struct = 1; + ct->variable_increment = mt->pointers > 1 ? sizeof(void*) : mt->base_size; + + } else if (mt->is_variable_struct) { + assert(!mt->variable_size_known && !mt->is_array && !mt->pointers); + ct->base_size += mt->base_size; + ct->is_variable_struct = 1; + ct->variable_increment = mt->variable_increment; + + } else if (mt->is_array) { + ct->base_size += mt->array_size * (mt->pointers > 1 ? sizeof(void*) : mt->base_size); + + } else { + ct->base_size += mt->pointers ? sizeof(void*) : mt->base_size; + } + } + + /* increase the outer struct/union alignment if needed */ + if (mt->align_mask > (int) ct->align_mask) { + ct->align_mask = mt->align_mask; + } + + if (mt->has_bitfield || mt->is_bitfield) { + ct->has_bitfield = 1; + } + + *pbit_offset = bit_offset; +} + +static int copy_submembers(lua_State* L, int to_usr, int from_usr, const struct ctype* ft, int* midx) +{ + struct ctype ct; + int i, sublen; + + from_usr = lua_absindex(L, from_usr); + to_usr = lua_absindex(L, to_usr); + + /* integer keys */ + sublen = (int) lua_rawlen(L, from_usr); + for (i = 1; i <= sublen; i++) { + lua_rawgeti(L, from_usr, i); + + ct = *(const struct ctype*) lua_touserdata(L, -1); + ct.offset += ft->offset; + lua_getuservalue(L, -1); + + push_ctype(L, -1, &ct); + lua_rawseti(L, to_usr, (*midx)++); + + lua_pop(L, 2); /* ctype, user value */ + } + + /* string keys */ + lua_pushnil(L); + while (lua_next(L, from_usr)) { + if (lua_type(L, -2) == LUA_TSTRING) { + struct ctype ct = *(const struct ctype*) lua_touserdata(L, -1); + ct.offset += ft->offset; + lua_getuservalue(L, -1); + + /* uservalue[sub_mname] = new_sub_mtype */ + lua_pushvalue(L, -3); + push_ctype(L, -2, &ct); + lua_rawset(L, to_usr); + + lua_pop(L, 1); /* remove submember user value */ + } + lua_pop(L, 1); + } + + return 0; +} + +static int add_member(lua_State* L, int ct_usr, int mname, int mbr_usr, const struct ctype* mt, int* midx) +{ + ct_usr = lua_absindex(L, ct_usr); + mname = lua_absindex(L, mname); + + push_ctype(L, mbr_usr, mt); + + /* usrvalue[mbr index] = pushed mtype */ + lua_pushvalue(L, -1); + lua_rawseti(L, ct_usr, (*midx)++); + + /* set usrvalue[mname] = pushed mtype */ + lua_pushvalue(L, mname); + lua_pushvalue(L, -2); + lua_rawset(L, ct_usr); + + /* set usrvalue[mtype] = mname */ + lua_pushvalue(L, -1); + lua_pushvalue(L, mname); + lua_rawset(L, ct_usr); + + lua_pop(L, 1); + + return 0; +} + +/* Parses a struct from after the open curly through to the close curly. + */ +static int parse_struct(lua_State* L, struct parser* P, int tmp_usr, const struct ctype* ct) +{ + struct token tok; + int midx = 1; + int top = lua_gettop(L); + + tmp_usr = lua_absindex(L, tmp_usr); + + /* parse members */ + for (;;) { + struct ctype mbase; + + assert(lua_gettop(L) == top); + + /* see if we're at the end of the struct */ + require_token(L, P, &tok); + if (tok.type == TOK_CLOSE_CURLY) { + break; + } else if (ct->is_variable_struct) { + return luaL_error(L, "can't have members after a variable sized member on line %d", P->line); + } else { + put_back(P); + } + + /* members are of the form + * , , ; + * eg struct foo bar, *bar2[2]; + * mbase is 'struct foo' + * mtype is '' then '*[2]' + * mname is 'bar' then 'bar2' + */ + + parse_type(L, P, &mbase); + + for (;;) { + struct token mname; + struct ctype mt = mbase; + + memset(&mname, 0, sizeof(mname)); + + if (ct->is_variable_struct) { + return luaL_error(L, "can't have members after a variable sized member on line %d", P->line); + } + + assert(lua_gettop(L) == top + 1); + parse_argument(L, P, -1, &mt, &mname, NULL); + assert(lua_gettop(L) == top + 2); + + if (!mt.is_defined && (mt.pointers - mt.is_array) == 0) { + return luaL_error(L, "member type is undefined on line %d", P->line); + } + + if (mt.type == VOID_TYPE && (mt.pointers - mt.is_array) == 0) { + return luaL_error(L, "member type can not be void on line %d", P->line); + } + + mt.has_member_name = (mname.size > 0); + lua_pushlstring(L, mname.str, mname.size); + + add_member(L, tmp_usr, -1, -2, &mt, &midx); + + /* pop the usr value from push_argument and the member name */ + lua_pop(L, 2); + assert(lua_gettop(L) == top + 1); + + require_token(L, P, &tok); + if (tok.type == TOK_SEMICOLON) { + break; + } else if (tok.type != TOK_COMMA) { + luaL_error(L, "unexpected token in struct definition on line %d", P->line); + } + } + + /* pop the usr value from push_type */ + lua_pop(L, 1); + } + + assert(lua_gettop(L) == top); + return 0; +} + +static int calculate_struct_offsets(lua_State* L, struct parser* P, int ct_usr, struct ctype* ct, int tmp_usr) +{ + int i; + int midx = 1; + int sz = (int) lua_rawlen(L, tmp_usr); + int bit_offset = 0; + int bitfield_type = -1; + + ct_usr = lua_absindex(L, ct_usr); + tmp_usr = lua_absindex(L, tmp_usr); + + for (i = 1; i <= sz; i++) { + struct ctype mt; + + /* get the member type */ + lua_rawgeti(L, tmp_usr, i); + mt = *(const struct ctype*) lua_touserdata(L, -1); + + /* get the member user table */ + lua_getuservalue(L, -1); + + /* get the member name */ + lua_pushvalue(L, -2); + lua_rawget(L, tmp_usr); + + calculate_member_position(L, P, ct, &mt, &bit_offset, &bitfield_type); + + if (mt.has_member_name) { + assert(!lua_isnil(L, -1)); + add_member(L, ct_usr, -1, -2, &mt, &midx); + + } else if (mt.type == STRUCT_TYPE || mt.type == UNION_TYPE) { + /* With an unnamed member we copy all of the submembers into our + * usr value adjusting the offset as necessary. Note ctypes are + * immutable so need to push a new ctype to update the offset. + */ + copy_submembers(L, ct_usr, -2, &mt, &midx); + + } else { + /* We ignore unnamed members that aren't structs or unions. These + * are there just to change the padding */ + } + + lua_pop(L, 3); + } + + /* finish up the current bitfield storage unit */ + ct->base_size += (bit_offset + CHAR_BIT - 1) / CHAR_BIT; + + /* only void is allowed 0 size */ + if (ct->base_size == 0) { + ct->base_size = 1; + } + + ct->base_size = ALIGN_UP(ct->base_size, ct->align_mask); + return 0; +} + +/* copy over attributes that could be specified before the typedef eg + * __attribute__(packed) const type_t */ +static void instantiate_typedef(struct parser* P, struct ctype* tt, const struct ctype* ft) +{ + struct ctype pt = *tt; + *tt = *ft; + + tt->const_mask |= pt.const_mask; + tt->is_packed = pt.is_packed; + + if (tt->is_packed) { + tt->align_mask = 0; + } else { + /* Instantiate the typedef in the current packing. This may be + * further updated if a pointer is added or another alignment + * attribute is applied. If pt.align_mask is already non-zero than an + * increased alignment via __declspec(aligned(#)) has been set. */ + tt->align_mask = max(min(P->align_mask, tt->align_mask), pt.align_mask); + } +} + +/* this parses a struct or union starting with the optional + * name before the opening brace + * leaves the type usr value on the stack + */ +static int parse_record(lua_State* L, struct parser* P, struct ctype* ct) +{ + struct token tok; + int top = lua_gettop(L); + + require_token(L, P, &tok); + + /* name is optional */ + if (tok.type == TOK_TOKEN) { + /* declaration */ + lua_pushlstring(L, tok.str, tok.size); + + assert(lua_gettop(L) == top+1); + + /* lookup the name to see if we've seen this type before */ + push_upval(L, &types_key); + lua_pushvalue(L, -2); + lua_rawget(L, top+2); + + assert(lua_gettop(L) == top+3); + + if (lua_isnil(L, -1)) { + lua_pop(L, 1); /* pop the nil usr value */ + lua_newtable(L); /* the new usr table */ + + /* stack layout is: + * top+1: record name + * top+2: types table + * top+3: new usr table + */ + + lua_pushlightuserdata(L, &g_name_key); + lua_pushvalue(L, top+1); + lua_rawset(L, top+3); /* usr[name_key] = name */ + + lua_pushvalue(L, top+1); + push_ctype(L, top+3, ct); + lua_rawset(L, top+2); /* types[name] = new_ctype */ + + } else { + /* get the exsting declared type */ + const struct ctype* prevt = (const struct ctype*) lua_touserdata(L, top+3); + + if (prevt->type != ct->type) { + lua_getuservalue(L, top+3); + push_type_name(L, -1, ct); + push_type_name(L, top+3, prevt); + luaL_error(L, "type '%s' previously declared as '%s'", lua_tostring(L, -2), lua_tostring(L, -1)); + } + + instantiate_typedef(P, ct, prevt); + + /* replace the ctype with its usr value */ + lua_getuservalue(L, -1); + lua_replace(L, -2); + } + + /* remove the extra name and types table */ + lua_replace(L, -3); + lua_pop(L, 1); + + assert(lua_gettop(L) == top + 1 && lua_istable(L, -1)); + + /* if a name is given then we may be at the end of the string + * eg for ffi.new('struct foo') + */ + if (!next_token(L, P, &tok)) { + return 0; + } + + } else { + /* create a new unnamed record */ + int num; + + /* get the next unnamed number */ + push_upval(L, &next_unnamed_key); + num = lua_tointeger(L, -1); + lua_pop(L, 1); + + /* increment the unnamed upval */ + lua_pushinteger(L, num + 1); + set_upval(L, &next_unnamed_key); + + lua_newtable(L); /* the new usr table - leave on stack */ + + /* usr[name_key] = num */ + lua_pushlightuserdata(L, &g_name_key); + lua_pushfstring(L, "%d", num); + lua_rawset(L, -3); + } + + if (tok.type != TOK_OPEN_CURLY) { + /* this may just be a declaration or use of the type as an argument or + * member */ + put_back(P); + return 0; + } + + if (ct->is_defined) { + return luaL_error(L, "redefinition in line %d", P->line); + } + + assert(lua_gettop(L) == top + 1 && lua_istable(L, -1)); + + if (ct->type == ENUM_TYPE) { + parse_enum(L, P, ct); + } else { + /* we do a two stage parse, where we parse the content first and build up + * the temp user table. We then iterate over that to calculate the offsets + * and fill out ct_usr. This is so we can handle out of order members + * (eg vtable) and attributes specified at the end of the struct. + */ + lua_newtable(L); + parse_struct(L, P, -1, ct); + calculate_struct_offsets(L, P, -2, ct, -1); + assert(lua_gettop(L) == top + 2 && lua_istable(L, -1)); + lua_pop(L, 1); + } + + assert(lua_gettop(L) == top + 1 && lua_istable(L, -1)); + set_defined(L, -1, ct); + assert(lua_gettop(L) == top + 1); + return 0; +} + +/* parses single or multi work built in types, and pushes it onto the stack */ +static int parse_type_name(lua_State* L, struct parser* P) +{ + struct token tok; + int flags = 0; + + enum { + UNSIGNED = 0x01, + SIGNED = 0x02, + LONG = 0x04, + SHORT = 0x08, + INT = 0x10, + CHAR = 0x20, + LONG_LONG = 0x40, + INT8 = 0x80, + INT16 = 0x100, + INT32 = 0x200, + INT64 = 0x400, + DOUBLE = 0x800, + FLOAT = 0x1000, + COMPLEX = 0x2000, + }; + + require_token(L, P, &tok); + + /* we have to manually decode the builtin types since they can take up + * more then one token + */ + for (;;) { + if (tok.type != TOK_TOKEN) { + break; + } else if (IS_LITERAL(tok, "unsigned")) { + flags |= UNSIGNED; + } else if (IS_LITERAL(tok, "signed")) { + flags |= SIGNED; + } else if (IS_LITERAL(tok, "short")) { + flags |= SHORT; + } else if (IS_LITERAL(tok, "char")) { + flags |= CHAR; + } else if (IS_LITERAL(tok, "long")) { + flags |= (flags & LONG) ? LONG_LONG : LONG; + } else if (IS_LITERAL(tok, "int")) { + flags |= INT; + } else if (IS_LITERAL(tok, "__int8")) { + flags |= INT8; + } else if (IS_LITERAL(tok, "__int16")) { + flags |= INT16; + } else if (IS_LITERAL(tok, "__int32")) { + flags |= INT32; + } else if (IS_LITERAL(tok, "__int64")) { + flags |= INT64; + } else if (IS_LITERAL(tok, "double")) { + flags |= DOUBLE; + } else if (IS_LITERAL(tok, "float")) { + flags |= FLOAT; + } else if (IS_LITERAL(tok, "complex") || IS_LITERAL(tok, "_Complex")) { + flags |= COMPLEX; + } else if (IS_LITERAL(tok, "register")) { + /* ignore */ + } else { + break; + } + + if (!next_token(L, P, &tok)) { + break; + } + } + + if (flags) { + put_back(P); + } + + if (flags & CHAR) { + if (flags & SIGNED) { + lua_pushliteral(L, "int8_t"); + } else if (flags & UNSIGNED) { + lua_pushliteral(L, "uint8_t"); + } else { + lua_pushstring(L, (((char) -1) > 0) ? "uint8_t" : "int8_t"); + } + + } else if (flags & INT8) { + lua_pushstring(L, (flags & UNSIGNED) ? "uint8_t" : "int8_t"); + } else if (flags & INT16) { + lua_pushstring(L, (flags & UNSIGNED) ? "uint16_t" : "int16_t"); + } else if (flags & INT32) { + lua_pushstring(L, (flags & UNSIGNED) ? "uint32_t" : "int32_t"); + } else if (flags & (INT64 | LONG_LONG)) { + lua_pushstring(L, (flags & UNSIGNED) ? "uint64_t" : "int64_t"); + + } else if (flags & COMPLEX) { + if (flags & LONG) { + lua_pushliteral(L, "complex long double"); + } else if (flags & FLOAT) { + lua_pushliteral(L, "complex float"); + } else { + lua_pushliteral(L, "complex double"); + } + + } else if (flags & DOUBLE) { + if (flags & LONG) { + lua_pushliteral(L, "long double"); + } else { + lua_pushliteral(L, "double"); + } + + } else if (flags & FLOAT) { + lua_pushliteral(L, "float"); + + } else if (flags & SHORT) { +#define SHORT_TYPE(u) (sizeof(short) == sizeof(int64_t) ? u "int64_t" : sizeof(short) == sizeof(int32_t) ? u "int32_t" : u "int16_t") + if (flags & UNSIGNED) { + lua_pushstring(L, SHORT_TYPE("u")); + } else { + lua_pushstring(L, SHORT_TYPE("")); + } +#undef SHORT_TYPE + + } else if (flags & LONG) { +#define LONG_TYPE(u) (sizeof(long) == sizeof(int64_t) ? u "int64_t" : u "int32_t") + if (flags & UNSIGNED) { + lua_pushstring(L, LONG_TYPE("u")); + } else { + lua_pushstring(L, LONG_TYPE("")); + } +#undef LONG_TYPE + + } else if (flags) { +#define INT_TYPE(u) (sizeof(int) == sizeof(int64_t) ? u "int64_t" : sizeof(int) == sizeof(int32_t) ? u "int32_t" : u "int16_t") + if (flags & UNSIGNED) { + lua_pushstring(L, INT_TYPE("u")); + } else { + lua_pushstring(L, INT_TYPE("")); + } +#undef INT_TYPE + + } else { + lua_pushlstring(L, tok.str, tok.size); + } + + return 0; +} + +/* parse_attribute parses a token to see if it is an attribute. It may then + * parse some following tokens to decode the attribute setting the appropriate + * fields in ct. It will return 1 if the token was used (and possibly some + * more following it) or 0 if not. If the token was used, the next token must + * be retrieved using next_token/require_token. + */ +static int parse_attribute(lua_State* L, struct parser* P, struct token* tok, struct ctype* ct, struct parser* asmname) +{ + if (tok->type != TOK_TOKEN) { + return 0; + + } else if (asmname && (IS_LITERAL(*tok, "__asm__") || IS_LITERAL(*tok, "__asm"))) { + check_token(L, P, TOK_OPEN_PAREN, NULL, "unexpected token after __asm__ on line %d", P->line); + *asmname = *P; + + require_token(L, P, tok); + while (tok->type == TOK_STRING) { + require_token(L, P, tok); + } + + if (tok->type != TOK_CLOSE_PAREN) { + luaL_error(L, "unexpected token after __asm__ on line %d", P->line); + } + return 1; + + } else if (IS_LITERAL(*tok, "__attribute__") || IS_LITERAL(*tok, "__declspec")) { + int parens = 1; + check_token(L, P, TOK_OPEN_PAREN, NULL, "expected parenthesis after __attribute__ or __declspec on line %d", P->line); + + for (;;) { + require_token(L, P, tok); + if (tok->type == TOK_OPEN_PAREN) { + parens++; + } else if (tok->type == TOK_CLOSE_PAREN) { + if (--parens == 0) { + break; + } + + } else if (tok->type != TOK_TOKEN) { + /* ignore unknown symbols within parentheses */ + + } else if (IS_LITERAL(*tok, "align") || IS_LITERAL(*tok, "aligned") || IS_LITERAL(*tok, "__aligned__")) { + unsigned align = 0; + require_token(L, P, tok); + + switch (tok->type) { + case TOK_CLOSE_PAREN: + align = ALIGNED_DEFAULT; + put_back(P); + break; + + case TOK_OPEN_PAREN: + require_token(L, P, tok); + + if (tok->type != TOK_NUMBER) { + luaL_error(L, "expected align(#) on line %d", P->line); + } + + switch (tok->integer) { + case 1: align = 0; break; + case 2: align = 1; break; + case 4: align = 3; break; + case 8: align = 7; break; + case 16: align = 15; break; + default: + luaL_error(L, "unsupported align size on line %d", P->line); + } + + check_token(L, P, TOK_CLOSE_PAREN, NULL, "expected align(#) on line %d", P->line); + break; + + default: + luaL_error(L, "expected align(#) on line %d", P->line); + } + + /* __attribute__(aligned(#)) is only supposed to increase alignment */ + ct->align_mask = max(align, ct->align_mask); + + } else if (IS_LITERAL(*tok, "packed") || IS_LITERAL(*tok, "__packed__")) { + ct->align_mask = 0; + ct->is_packed = 1; + + } else if (IS_LITERAL(*tok, "mode") || IS_LITERAL(*tok, "__mode__")) { + + check_token(L, P, TOK_OPEN_PAREN, NULL, "expected mode(MODE) on line %d", P->line); + + require_token(L, P, tok); + if (tok->type != TOK_TOKEN) { + luaL_error(L, "expected mode(MODE) on line %d", P->line); + } + + if (ct->type == FLOAT_TYPE || ct->type == DOUBLE_TYPE) { + struct {char ch; float v;} af; + struct {char ch; double v;} ad; + + if (IS_LITERAL(*tok, "SF") || IS_LITERAL(*tok, "__SF__")) { + ct->type = FLOAT_TYPE; + ct->base_size = sizeof(float); + ct->align_mask = ALIGNOF(af); + + } else if (IS_LITERAL(*tok, "DF") || IS_LITERAL(*tok, "__DF__")) { + ct->type = DOUBLE_TYPE; + ct->base_size = sizeof(double); + ct->align_mask = ALIGNOF(ad); + + } else { + luaL_error(L, "unexpected mode on line %d", P->line); + } + + } else { + struct {char ch; uint16_t v;} a16; + struct {char ch; uint32_t v;} a32; + struct {char ch; uint64_t v;} a64; + + if (IS_LITERAL(*tok, "QI") || IS_LITERAL(*tok, "__QI__") + || IS_LITERAL(*tok, "byte") || IS_LITERAL(*tok, "__byte__") + ) { + ct->type = INT8_TYPE; + ct->base_size = sizeof(uint8_t); + ct->align_mask = 0; + + } else if (IS_LITERAL(*tok, "HI") || IS_LITERAL(*tok, "__HI__")) { + ct->type = INT16_TYPE; + ct->base_size = sizeof(uint16_t); + ct->align_mask = ALIGNOF(a16); + + } else if (IS_LITERAL(*tok, "SI") || IS_LITERAL(*tok, "__SI__") +#if defined ARCH_X86 || defined ARCH_ARM + || IS_LITERAL(*tok, "word") || IS_LITERAL(*tok, "__word__") + || IS_LITERAL(*tok, "pointer") || IS_LITERAL(*tok, "__pointer__") +#endif + ) { + ct->type = INT32_TYPE; + ct->base_size = sizeof(uint32_t); + ct->align_mask = ALIGNOF(a32); + + } else if (IS_LITERAL(*tok, "DI") || IS_LITERAL(*tok, "__DI__") +#if defined ARCH_X64 || defined ARCH_PPC64 + || IS_LITERAL(*tok, "word") || IS_LITERAL(*tok, "__word__") + || IS_LITERAL(*tok, "pointer") || IS_LITERAL(*tok, "__pointer__") +#endif + ) { + ct->type = INT64_TYPE; + ct->base_size = sizeof(uint64_t); + ct->align_mask = ALIGNOF(a64); + + } else { + luaL_error(L, "unexpected mode on line %d", P->line); + } + } + + check_token(L, P, TOK_CLOSE_PAREN, NULL, "expected mode(MODE) on line %d", P->line); + + } else if (IS_LITERAL(*tok, "cdecl") || IS_LITERAL(*tok, "__cdecl__")) { + ct->calling_convention = C_CALL; + + } else if (IS_LITERAL(*tok, "fastcall") || IS_LITERAL(*tok, "__fastcall__")) { + ct->calling_convention = FAST_CALL; + + } else if (IS_LITERAL(*tok, "stdcall") || IS_LITERAL(*tok, "__stdcall__")) { + ct->calling_convention = STD_CALL; + } + /* ignore unknown tokens within parentheses */ + } + return 1; + + } else if (IS_LITERAL(*tok, "__cdecl")) { + ct->calling_convention = C_CALL; + return 1; + + } else if (IS_LITERAL(*tok, "__fastcall")) { + ct->calling_convention = FAST_CALL; + return 1; + + } else if (IS_LITERAL(*tok, "__stdcall")) { + ct->calling_convention = STD_CALL; + return 1; + + } else if (IS_LITERAL(*tok, "__extension__") || IS_LITERAL(*tok, "extern")) { + /* ignore */ + return 1; + + } else { + return 0; + } +} + +/* parses out the base type of a type expression in a function declaration, + * struct definition, typedef etc + * + * leaves the usr value of the type on the stack + */ +int parse_type(lua_State* L, struct parser* P, struct ctype* ct) +{ + struct token tok; + int top = lua_gettop(L); + + memset(ct, 0, sizeof(*ct)); + + require_token(L, P, &tok); + + /* get const/volatile before the base type */ + for (;;) { + if (tok.type != TOK_TOKEN) { + return luaL_error(L, "unexpected value before type name on line %d", P->line); + + } else if (IS_CONST(tok)) { + ct->const_mask = 1; + require_token(L, P, &tok); + + } else if (IS_VOLATILE(tok) || + IS_RESTRICT(tok) || + IS_LITERAL(tok, "static") || + IS_INLINE(tok)) { + /* ignored for now */ + require_token(L, P, &tok); + } else if (parse_attribute(L, P, &tok, ct, NULL)) { + /* get function attributes before the return type */ + require_token(L, P, &tok); + + } else { + break; + } + } + + /* get base type */ + if (tok.type != TOK_TOKEN) { + return luaL_error(L, "unexpected value before type name on line %d", P->line); + + } else if (IS_LITERAL(tok, "struct")) { + ct->type = STRUCT_TYPE; + parse_record(L, P, ct); + + } else if (IS_LITERAL(tok, "union")) { + ct->type = UNION_TYPE; + parse_record(L, P, ct); + + } else if (IS_LITERAL(tok, "enum")) { + ct->type = ENUM_TYPE; + parse_record(L, P, ct); + + } else { + put_back(P); + + /* lookup type */ + push_upval(L, &types_key); + parse_type_name(L, P); + lua_rawget(L, -2); + lua_remove(L, -2); + + if (lua_isnil(L, -1)) { + lua_pushlstring(L, tok.str, tok.size); + return luaL_error(L, "unknown type %s on line %d", lua_tostring(L, -1), P->line); + } + + instantiate_typedef(P, ct, (const struct ctype*) lua_touserdata(L, -1)); + + /* we only want the usr tbl from the ctype in the types tbl */ + lua_getuservalue(L, -1); + lua_replace(L, -2); + } + + while (next_token(L, P, &tok)) { + if (tok.type != TOK_TOKEN) { + put_back(P); + break; + + } else if (IS_CONST(tok) || IS_VOLATILE(tok)) { + /* ignore for now */ + + } else { + put_back(P); + break; + } + } + + assert(lua_gettop(L) == top + 1 && (lua_istable(L, -1) || lua_isnil(L, -1))); + return 0; +} + +enum name_type { + BOTH, + FRONT, + BACK, +}; + +static void append_type_name(luaL_Buffer* B, int usr, const struct ctype* ct, enum name_type type) +{ + size_t i; + lua_State* L = B->L; + + usr = lua_absindex(L, usr); + + if (type == FRONT || type == BOTH) { + if (ct->type != FUNCTION_PTR_TYPE && (ct->const_mask & (1 << ct->pointers))) { + luaL_addstring(B, "const "); + } + + if (ct->is_unsigned) { + luaL_addstring(B, "unsigned "); + } + + switch (ct->type) { + case ENUM_TYPE: + luaL_addstring(B, "enum "); + goto get_name; + + case STRUCT_TYPE: + luaL_addstring(B, "struct "); + goto get_name; + + case UNION_TYPE: + luaL_addstring(B, "union "); + goto get_name; + + get_name: + lua_pushlightuserdata(L, &g_name_key); + lua_rawget(L, usr); + luaL_addvalue(B); + break; + + case FUNCTION_TYPE: + case FUNCTION_PTR_TYPE: + lua_pushlightuserdata(L, &g_front_name_key); + lua_rawget(L, usr); + luaL_addvalue(B); + break; + + case VOID_TYPE: + luaL_addstring(B, "void"); + break; + case BOOL_TYPE: + luaL_addstring(B, "bool"); + break; + case DOUBLE_TYPE: + luaL_addstring(B, "double"); + break; + case LONG_DOUBLE_TYPE: + luaL_addstring(B, "long double"); + break; + case FLOAT_TYPE: + luaL_addstring(B, "float"); + break; + case COMPLEX_LONG_DOUBLE_TYPE: + luaL_addstring(B, "long complex double"); + break; + case COMPLEX_DOUBLE_TYPE: + luaL_addstring(B, "complex double"); + break; + case COMPLEX_FLOAT_TYPE: + luaL_addstring(B, "complex float"); + break; + case INT8_TYPE: + luaL_addstring(B, "char"); + break; + case INT16_TYPE: + luaL_addstring(B, "short"); + break; + case INT32_TYPE: + luaL_addstring(B, "int"); + break; + case INT64_TYPE: + luaL_addstring(B, "long long"); + break; + + case INTPTR_TYPE: + if (sizeof(intptr_t) == sizeof(int32_t)) { + luaL_addstring(B, "long"); + } else if (sizeof(intptr_t) == sizeof(int64_t)) { + luaL_addstring(B, "long long"); + } else { + luaL_error(L, "internal error - bad type"); + } + break; + + default: + luaL_error(L, "internal error - bad type %d", ct->type); + } + + for (i = 0; i < ct->pointers - ct->is_array; i++) { + luaL_addchar(B, '*'); + if (ct->const_mask & (1 << (ct->pointers - i - 1))) { + luaL_addstring(B, " const"); + } + } + } + + if (type == BOTH || type == BACK) { + if (ct->is_reference) { + luaL_addstring(B, " &"); + } + + if (ct->is_variable_array && !ct->variable_size_known) { + luaL_addstring(B, "[?]"); + } else if (ct->is_array) { + lua_pushfstring(L, "[%d]", (int) ct->array_size); + luaL_addvalue(B); + } + + if (ct->type == FUNCTION_PTR_TYPE || ct->type == FUNCTION_TYPE) { + lua_pushlightuserdata(L, &g_back_name_key); + lua_rawget(L, usr); + luaL_addvalue(B); + } + + if (ct->is_bitfield) { + lua_pushfstring(L, " : %d", (int) ct->bit_size); + luaL_addvalue(B); + } + } +} + +void push_type_name(lua_State* L, int usr, const struct ctype* ct) +{ + luaL_Buffer B; + usr = lua_absindex(L, usr); + luaL_buffinit(L, &B); + append_type_name(&B, usr, ct, BOTH); + luaL_pushresult(&B); +} + +static void push_function_type_strings(lua_State* L, int usr, const struct ctype* ct) +{ + size_t i, args; + luaL_Buffer B; + int top = lua_gettop(L); + const struct ctype* ret_ct; + + int arg_ct = top+3; + int arg_usr = top+4; + int ret_usr = top+6; + + usr = lua_absindex(L, usr); + + /* return type */ + lua_settop(L, top+4); /* room for two returns and two temp positions */ + lua_rawgeti(L, usr, 0); + lua_getuservalue(L, -1); + ret_ct = (const struct ctype*) lua_touserdata(L, -2); + + luaL_buffinit(L, &B); + append_type_name(&B, ret_usr, ret_ct, FRONT); + + if (ret_ct->type != FUNCTION_TYPE && ret_ct->type != FUNCTION_PTR_TYPE) { + luaL_addchar(&B, ' '); + } + + switch (ct->calling_convention) { + case STD_CALL: + luaL_addstring(&B, "(__stdcall *"); + break; + case FAST_CALL: + luaL_addstring(&B, "(__fastcall *"); + break; + case C_CALL: + luaL_addstring(&B, "(*"); + break; + default: + luaL_error(L, "internal error - unknown calling convention"); + } + + luaL_pushresult(&B); + lua_replace(L, top+1); + + luaL_buffinit(L, &B); + luaL_addstring(&B, ")("); + + /* arguments */ + args = lua_rawlen(L, usr); + for (i = 1; i <= args; i++) { + if (i > 1) { + luaL_addstring(&B, ", "); + } + + /* note push the arg and user value below the indexes used by the buffer + * and use indexes relative to top to avoid problems due to the buffer + * system pushing a variable number of arguments onto the stack */ + lua_rawgeti(L, usr, (int) i); + lua_replace(L, arg_ct); + lua_getuservalue(L, arg_ct); + lua_replace(L, arg_usr); + append_type_name(&B, arg_usr, (const struct ctype*) lua_touserdata(L, arg_ct), BOTH); + } + + luaL_addstring(&B, ")"); + append_type_name(&B, ret_usr, ret_ct, BACK); + luaL_pushresult(&B); + lua_replace(L, top+2); + + lua_settop(L, top+2); + assert(lua_isstring(L, top+1) && lua_isstring(L, top+2)); +} + +/* parses from after the opening paranthesis to after the closing parenthesis */ +static void parse_function_arguments(lua_State* L, struct parser* P, int ct_usr, struct ctype* ct) +{ + struct token tok; + int args = 0; + int top = lua_gettop(L); + + ct_usr = lua_absindex(L, ct_usr); + + for (;;) { + require_token(L, P, &tok); + + if (tok.type == TOK_CLOSE_PAREN) { + break; + } + + if (args) { + if (tok.type != TOK_COMMA) { + luaL_error(L, "unexpected token in function argument %d on line %d", args, P->line); + } + + require_token(L, P, &tok); + } + + if (tok.type == TOK_VA_ARG) { + ct->has_var_arg = true; + check_token(L, P, TOK_CLOSE_PAREN, "", "unexpected token after ... in function on line %d", P->line); + break; + + } else if (tok.type == TOK_TOKEN) { + struct ctype at; + + put_back(P); + parse_type(L, P, &at); + parse_argument(L, P, -1, &at, NULL, NULL); + + assert(lua_gettop(L) == top + 2); + + /* array arguments are just treated as their base pointer type */ + at.is_array = 0; + + /* check for the c style int func(void) and error on other uses of arguments of type void */ + if (at.type == VOID_TYPE && at.pointers == 0) { + if (args) { + luaL_error(L, "can't have argument of type void on line %d", P->line); + } + + check_token(L, P, TOK_CLOSE_PAREN, "", "unexpected void in function on line %d", P->line); + lua_pop(L, 2); + break; + } + + push_ctype(L, -1, &at); + lua_rawseti(L, ct_usr, ++args); + + lua_pop(L, 2); /* parse_type and parse_argument at_usr */ + + } else { + luaL_error(L, "unexpected token in function argument %d on line %d", args+1, P->line); + } + } + + assert(lua_gettop(L) == top); +} + +static int max_bitfield_size(int type) +{ + switch (type) { + case BOOL_TYPE: + return 1; + case INT8_TYPE: + return 8; + case INT16_TYPE: + return 16; + case INT32_TYPE: + case ENUM_TYPE: + return 32; + case INT64_TYPE: + return 64; + default: + return -1; + } +} + +static struct ctype* parse_argument2(lua_State* L, struct parser* P, int ct_usr, struct ctype* ct, struct token* name, struct parser* asmname); + +/* parses from after the first ( in a function declaration or function pointer + * can be one of: + * void foo(...) before ... + * void (foo)(...) before foo + * void (* <>)(...) before <> which is the inner type + */ +static struct ctype* parse_function(lua_State* L, struct parser* P, int ct_usr, struct ctype* ct, struct token* name, struct parser* asmname) +{ + /* We have a function pointer or a function. The usr table will + * get replaced by the canonical one (if there is one) in + * find_canonical_usr after all the arguments and returns have + * been parsed. */ + struct token tok; + int top = lua_gettop(L); + struct ctype* ret; + + lua_newtable(L); + ret = push_ctype(L, ct_usr, ct); + lua_rawseti(L, -2, 0); + ct_usr = lua_gettop(L); + + memset(ct, 0, sizeof(*ct)); + ct->base_size = sizeof(void (*)()); + ct->align_mask = min(FUNCTION_ALIGN_MASK, P->align_mask); + ct->type = FUNCTION_TYPE; + ct->is_defined = 1; + + if (name->type == TOK_NIL) { + for (;;) { + require_token(L, P, &tok); + + if (tok.type == TOK_STAR) { + + if (ct->type == FUNCTION_TYPE) { + ct->type = FUNCTION_PTR_TYPE; + } else if (ct->pointers == POINTER_MAX) { + luaL_error(L, "maximum number of pointer derefs reached - use a struct to break up the pointers on line %d", P->line); + } else { + ct->pointers++; + ct->const_mask <<= 1; + } + + } else if (parse_attribute(L, P, &tok, ct, asmname)) { + /* parse_attribute sets the appropriate fields */ + + } else { + /* call parse_argument to handle the inner contents + * e.g. the <> in "void (* <>) (...)". Note that the + * inner contents can itself be a function, a function + * ptr, array, etc (e.g. "void (*signal(int sig, void + * (*func)(int)))(int)" ). + */ + put_back(P); + ct = parse_argument2(L, P, ct_usr, ct, name, asmname); + break; + } + } + + check_token(L, P, TOK_CLOSE_PAREN, NULL, "unexpected token in function on line %d", P->line); + check_token(L, P, TOK_OPEN_PAREN, NULL, "unexpected token in function on line %d", P->line); + } + + parse_function_arguments(L, P, ct_usr, ct); + + /* if we have an inner function then set the outer function ptr as its + * return type and return the inner function + * e.g. for void (* )(int) inner is + * surrounded by <>, return type is void (*)(int) + */ + if (lua_gettop(L) == ct_usr+1) { + lua_replace(L, ct_usr); + } + + assert(lua_gettop(L) == top + 1 && lua_istable(L, -1)); + return ret; +} + +static struct ctype* parse_argument2(lua_State* L, struct parser* P, int ct_usr, struct ctype* ct, struct token* name, struct parser* asmname) +{ + struct token tok; + int top = lua_gettop(L); + int ft_usr = 0; + + luaL_checkstack(L, 10, "function too complex"); + ct_usr = lua_absindex(L, ct_usr); + + for (;;) { + if (!next_token(L, P, &tok)) { + /* we've reached the end of the string */ + break; + + } else if (tok.type == TOK_STAR) { + if (ct->pointers == POINTER_MAX) { + luaL_error(L, "maximum number of pointer derefs reached - use a struct to break up the pointers on line %d", P->line); + } + + ct->pointers++; + ct->const_mask <<= 1; + + /* __declspec(align(#)) may come before the type in a member */ + if (!ct->is_packed) { + ct->align_mask = max(min(PTR_ALIGN_MASK, P->align_mask), ct->align_mask); + } + + } else if (tok.type == TOK_REFERENCE) { + ct->is_reference = 1; + + } else if (parse_attribute(L, P, &tok, ct, asmname)) { + /* parse attribute has filled out appropriate fields in type */ + + } else if (tok.type == TOK_OPEN_PAREN) { + ct = parse_function(L, P, ct_usr, ct, name, asmname); + ft_usr = lua_gettop(L); + + } else if (tok.type == TOK_OPEN_SQUARE) { + /* array */ + if (ct->pointers == POINTER_MAX) { + luaL_error(L, "maximum number of pointer derefs reached - use a struct to break up the pointers"); + } + ct->is_array = 1; + ct->pointers++; + ct->const_mask <<= 1; + require_token(L, P, &tok); + + if (ct->pointers == 1 && !ct->is_defined) { + luaL_error(L, "array of undefined type on line %d", P->line); + } + + if (ct->is_variable_struct || ct->is_variable_array) { + luaL_error(L, "can't have an array of a variably sized type on line %d", P->line); + } + + if (tok.type == TOK_QUESTION) { + ct->is_variable_array = 1; + ct->variable_increment = (ct->pointers > 1) ? sizeof(void*) : ct->base_size; + check_token(L, P, TOK_CLOSE_SQUARE, "", "invalid character in array on line %d", P->line); + + } else if (tok.type == TOK_CLOSE_SQUARE) { + ct->array_size = 0; + + } else if (tok.type == TOK_TOKEN && IS_RESTRICT(tok)) { + /* odd gcc extension foo[__restrict] for arguments */ + ct->array_size = 0; + check_token(L, P, TOK_CLOSE_SQUARE, "", "invalid character in array on line %d", P->line); + + } else { + int64_t asize; + put_back(P); + asize = calculate_constant(L, P); + if (asize < 0) { + luaL_error(L, "array size can not be negative on line %d", P->line); + } + ct->array_size = (size_t) asize; + check_token(L, P, TOK_CLOSE_SQUARE, "", "invalid character in array on line %d", P->line); + } + + } else if (tok.type == TOK_COLON) { + int64_t bsize = calculate_constant(L, P); + + if (ct->pointers || bsize < 0 || bsize > max_bitfield_size(ct->type)) { + luaL_error(L, "invalid bitfield on line %d", P->line); + } + + ct->is_bitfield = 1; + ct->bit_size = (unsigned) bsize; + + } else if (tok.type != TOK_TOKEN) { + /* we've reached the end of the declaration */ + put_back(P); + break; + + } else if (IS_CONST(tok)) { + ct->const_mask |= 1; + + } else if (IS_VOLATILE(tok) || IS_RESTRICT(tok)) { + /* ignored for now */ + + } else { + *name = tok; + } + } + + assert((ft_usr == 0 && lua_gettop(L) == top) || (lua_gettop(L) == top + 1 && ft_usr == top + 1 && (lua_istable(L, -1) || lua_isnil(L, -1)))); + return ct; +} + +static void find_canonical_usr(lua_State* L, int ct_usr, const struct ctype *ct) +{ + struct ctype rt; + int top = lua_gettop(L); + int types; + + if (ct->type != FUNCTION_PTR_TYPE && ct->type != FUNCTION_TYPE) { + return; + } + + luaL_checkstack(L, 10, "function too complex"); + ct_usr = lua_absindex(L, ct_usr); + + /* check to see if we already have the canonical usr table */ + lua_pushlightuserdata(L, &g_name_key); + lua_rawget(L, ct_usr); + if (!lua_isnil(L, -1)) { + lua_pop(L, 1); + assert(top == lua_gettop(L)); + return; + } + lua_pop(L, 1); + + assert(top == lua_gettop(L)); + + /* first canonize the return type */ + lua_rawgeti(L, ct_usr, 0); + rt = *(struct ctype*) lua_touserdata(L, -1); + lua_getuservalue(L, -1); + find_canonical_usr(L, -1, &rt); + push_ctype(L, -1, &rt); + lua_rawseti(L, ct_usr, 0); + lua_pop(L, 2); /* return ctype and usr */ + + assert(top == lua_gettop(L)); + + /* look up the type string in the types table */ + push_upval(L, &types_key); + types = lua_gettop(L); + + push_function_type_strings(L, ct_usr, ct); + lua_pushvalue(L, -2); + lua_pushvalue(L, -2); + lua_concat(L, 2); + + lua_pushvalue(L, -1); + lua_rawget(L, types); + + assert(lua_gettop(L) == types + 4 && types == top + 1); + /* stack: types, front, back, both, looked up value */ + + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + + lua_pushlightuserdata(L, &g_front_name_key); + lua_pushvalue(L, -4); + lua_rawset(L, ct_usr); + + lua_pushlightuserdata(L, &g_back_name_key); + lua_pushvalue(L, -3); + lua_rawset(L, ct_usr); + + lua_pushlightuserdata(L, &g_name_key); + lua_pushvalue(L, -2); + lua_rawset(L, ct_usr); + + lua_pushvalue(L, -1); + push_ctype(L, ct_usr, ct); + lua_rawset(L, types); + } else { + lua_getuservalue(L, -1); + lua_replace(L, ct_usr); + lua_pop(L, 1); + } + + lua_pop(L, 4); + assert(top == lua_gettop(L) && types == top + 1); +} + + +/* parses after the main base type of a typedef, function argument or + * struct/union member + * eg for const void* bar[3] the base type is void with the subtype so far of + * const, this parses the "* bar[3]" and updates the type argument + * + * ct_usr and type must be as filled out by parse_type + * + * pushes the updated user value on the top of the stack + */ +void parse_argument(lua_State* L, struct parser* P, int ct_usr, struct ctype* ct, struct token* pname, struct parser* asmname) +{ + struct token tok, name; + int top = lua_gettop(L); + + memset(&name, 0, sizeof(name)); + parse_argument2(L, P, ct_usr, ct, &name, asmname); + + for (;;) { + if (!next_token(L, P, &tok)) { + break; + } else if (parse_attribute(L, P, &tok, ct, asmname)) { + /* parse_attribute sets the appropriate fields */ + } else { + put_back(P); + break; + } + } + + if (lua_gettop(L) == top) { + lua_pushvalue(L, ct_usr); + } + + find_canonical_usr(L, -1, ct); + + if (pname) { + *pname = name; + } +} + +static void parse_typedef(lua_State* L, struct parser* P) +{ + struct token tok; + struct ctype base_type; + int top = lua_gettop(L); + + parse_type(L, P, &base_type); + + for (;;) { + struct ctype arg_type = base_type; + struct token name; + + memset(&name, 0, sizeof(name)); + + assert(lua_gettop(L) == top + 1); + parse_argument(L, P, -1, &arg_type, &name, NULL); + assert(lua_gettop(L) == top + 2); + + if (!name.size) { + luaL_error(L, "Can't have a typedef without a name on line %d", P->line); + } else if (arg_type.is_variable_array) { + luaL_error(L, "Can't typedef a variable length array on line %d", P->line); + } + + push_upval(L, &types_key); + lua_pushlstring(L, name.str, name.size); + push_ctype(L, -3, &arg_type); + lua_rawset(L, -3); + lua_pop(L, 2); /* types and parse_argument usr tbl */ + + require_token(L, P, &tok); + + if (tok.type == TOK_SEMICOLON) { + break; + } else if (tok.type != TOK_COMMA) { + luaL_error(L, "Unexpected character in typedef on line %d", P->line); + } + } + + lua_pop(L, 1); /* parse_type usr tbl */ + assert(lua_gettop(L) == top); +} + +static bool is_hex(char ch) +{ return ('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F'); } + +static bool is_digit(char ch) +{ return '0' <= ch && ch <= '9'; } + +static int from_hex(char ch) +{ + if (ch >= 'a') { + return ch - 'a' + 10; + } else if (ch >= 'A') { + return ch - 'A' + 10; + } else { + return ch - '0'; + } +} + +static void push_strings(lua_State* L, struct parser* P) +{ + luaL_Buffer B; + luaL_buffinit(L, &B); + + for (;;) { + const char *p, *e; + char *t, *s; + struct token tok; + + require_token(L, P, &tok); + if (tok.type != TOK_STRING) { + break; + } + + p = tok.str; + e = p + tok.size; + + t = luaL_prepbuffsize(&B, tok.size); + s = t; + + while (p < e) { + if (*p == '\\') { + if (++p == e) { + luaL_error(L, "parse error in string"); + } + switch (*p) { + case '\\': *(t++) = '\\'; p++; break; + case '\"': *(t++) = '\"'; p++; break; + case '\'': *(t++) = '\''; p++; break; + case 'n': *(t++) = '\n'; p++; break; + case 'r': *(t++) = '\r'; p++; break; + case 'b': *(t++) = '\b'; p++; break; + case 't': *(t++) = '\t'; p++; break; + case 'f': *(t++) = '\f'; p++; break; + case 'a': *(t++) = '\a'; p++; break; + case 'v': *(t++) = '\v'; p++; break; + case 'e': *(t++) = 0x1B; p++; break; + case 'x': + { + uint8_t u; + p++; + if (p + 2 > e || !is_hex(p[0]) || !is_hex(p[1])) { + luaL_error(L, "parse error in string"); + } + u = (from_hex(p[0]) << 4) | from_hex(p[1]); + *(t++) = *(char*) &u; + p += 2; + break; + } + default: + { + uint8_t u; + const char* e2 = min(p + 3, e); + if (!is_digit(*p)) { + luaL_error(L, "parse error in string"); + } + u = *p - '0'; + p++; + while (is_digit(*p) && p < e2) { + u = 10*u + *p-'0'; + p++; + } + *(t++) = *(char*) &u; + break; + } + } + } else { + *(t++) = *(p++); + } + } + + luaL_addsize(&B, t-s); + } + + luaL_pushresult(&B); +} + +static void parse_constant_assignemnt(lua_State* L, + struct parser* P, + const struct ctype* type, + const struct token* name) +{ + int64_t val = calculate_constant(L, P); + + check_token(L, P, TOK_SEMICOLON, "", "expected ; after constant definition on line %d", P->line); + + push_upval(L, &constants_key); + lua_pushlstring(L, name->str, name->size); + + switch (type->type) { + case INT8_TYPE: + case INT16_TYPE: + case INT32_TYPE: + if (type->is_unsigned) + lua_pushinteger(L, (unsigned int) val); + else + lua_pushinteger(L, (int) val); + break; + + default: + luaL_error(L, "expected a valid 8-, 16-, or 32-bit signed or unsigned integer type after 'static const' on line %d", P->line); + } + + lua_rawset(L, -3); + lua_pop(L, 2); /*constants and type*/ +} + +#define END 0 +#define PRAGMA_POP 1 + +static int parse_root(lua_State* L, struct parser* P) +{ + int top = lua_gettop(L); + struct token tok; + + while (next_token(L, P, &tok)) { + /* we can have: + * struct definition + * enum definition + * union definition + * struct/enum/union declaration + * typedef + * function declaration + * pragma pack + */ + + assert(lua_gettop(L) == top); + + if (tok.type == TOK_SEMICOLON) { + /* empty semicolon in root continue on */ + + } else if (tok.type == TOK_POUND) { + + check_token(L, P, TOK_TOKEN, "pragma", "unexpected pre processor directive on line %d", P->line); + check_token(L, P, TOK_TOKEN, "pack", "unexpected pre processor directive on line %d", P->line); + check_token(L, P, TOK_OPEN_PAREN, "", "invalid pack directive on line %d", P->line); + + require_token(L, P, &tok); + + if (tok.type == TOK_NUMBER) { + if (tok.integer != 1 && tok.integer != 2 && tok.integer != 4 && tok.integer != 8 && tok.integer != 16) { + luaL_error(L, "pack directive with invalid pack size on line %d", P->line); + } + + P->align_mask = (unsigned) (tok.integer - 1); + check_token(L, P, TOK_CLOSE_PAREN, "", "invalid pack directive on line %d", P->line); + + } else if (tok.type == TOK_TOKEN && IS_LITERAL(tok, "push")) { + int line = P->line; + unsigned previous_alignment = P->align_mask; + + check_token(L, P, TOK_CLOSE_PAREN, "", "invalid pack directive on line %d", P->line); + + if (parse_root(L, P) != PRAGMA_POP) { + luaL_error(L, "reached end of string without a pragma pop to match the push on line %d", line); + } + + P->align_mask = previous_alignment; + + } else if (tok.type == TOK_TOKEN && IS_LITERAL(tok, "pop")) { + check_token(L, P, TOK_CLOSE_PAREN, "", "invalid pack directive on line %d", P->line); + return PRAGMA_POP; + + } else { + luaL_error(L, "invalid pack directive on line %d", P->line); + } + + + } else if (tok.type != TOK_TOKEN) { + return luaL_error(L, "unexpected character on line %d", P->line); + + } else if (IS_LITERAL(tok, "__extension__")) { + /* ignore */ + continue; + + } else if (IS_LITERAL(tok, "extern")) { + /* ignore extern as data and functions can only be extern */ + continue; + + } else if (IS_LITERAL(tok, "typedef")) { + parse_typedef(L, P); + + } else { + /* type declaration, type definition, or function declaration */ + struct ctype type; + struct token name; + struct parser asmname; + + memset(&name, 0, sizeof(name)); + memset(&asmname, 0, sizeof(asmname)); + + put_back(P); + parse_type(L, P, &type); + + for (;;) { + parse_argument(L, P, -1, &type, &name, &asmname); + + if (name.size) { + /* global/function declaration */ + + /* set asmname_tbl[name] = asmname */ + if (asmname.next) { + push_upval(L, &asmname_key); + lua_pushlstring(L, name.str, name.size); + push_strings(L, &asmname); + lua_rawset(L, -3); + lua_pop(L, 1); /* asmname upval */ + } + + push_upval(L, &functions_key); + lua_pushlstring(L, name.str, name.size); + push_ctype(L, -3, &type); + lua_rawset(L, -3); + lua_pop(L, 1); /* functions upval */ + } else { + /* type declaration/definition - already been processed */ + } + + lua_pop(L, 1); + + if (!next_token(L, P, &tok)) { + break; + } + + if (tok.type == TOK_COMMA) { + continue; + } + + if (tok.type == TOK_OPEN_CURLY) { + int line = P->line; + int remaining = 1; + while (remaining > 0 && next_token(L, P, &tok)) { + if (tok.type == TOK_CLOSE_CURLY) { + remaining--; + } else if (tok.type == TOK_OPEN_CURLY) { + remaining++; + } + } + if (remaining > 0) { + luaL_error(L, "missing closing bracket for line %d", line); + } + } else if (tok.type == TOK_ASSIGN) { + parse_constant_assignemnt(L, P, &type, &name); + } else if (tok.type != TOK_SEMICOLON) { + luaL_error(L, "missing semicolon on line %d", P->line); + } + break; + } + + lua_pop(L, 1); + } + } + + return END; +} + +int ffi_cdef(lua_State* L) +{ + struct parser P; + + P.line = 1; + P.prev = P.next = luaL_checkstring(L, 1); + P.align_mask = DEFAULT_ALIGN_MASK; + + if (parse_root(L, &P) == PRAGMA_POP) { + luaL_error(L, "pragma pop without an associated push on line %d", P.line); + } + + return 0; +} + +/* calculate_constant handles operator precedence by having a number of + * recursive commands each of which computes the result at that level of + * precedence and above. calculate_constant1 is the highest precedence + */ + +static int64_t string_to_int(const char* str, size_t size) +{ + const char* end = str + size; + char c = *str++; + if (str < end) + { + if (c == '\\') { + c = *str++; + switch (c) { + case '\'': c = '\''; break; + case '\"': c = '\"'; break; + case '\?': c = '\?'; break; + case '\\': c = '\\'; break; + case 'a': c = '\a'; break; + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + case 'e': c = 27; break; + case 'x': + c = 0; + while (str < end) { + char d = *str++; + c *= 16; + if (d >= '0' && d <= '9') c += (d - '0'); + else c += (d - 'a' + 10); + } + break; + default: + c = c - '0'; + while (str < end) { + char d = *str++; + c = c*8 + (d - '0'); + } + break; + } + } + } + return c; +} + +static int try_cast(lua_State* L) +{ + struct parser* P = (struct parser*) lua_touserdata(L, 1); + struct ctype ct; + struct token name, tok; + memset(&name, 0, sizeof(name)); + + parse_type(L, P, &ct); + parse_argument(L, P, -1, &ct, &name, NULL); + + require_token(L, P, &tok); + if (tok.type != TOK_CLOSE_PAREN || name.size) { + return luaL_error(L, "invalid cast"); + } + + if (ct.pointers/* || ct.type != INT32_TYPE*/) { + return luaL_error(L, "unsupported cast on line %d", P->line); + } + + return 0; +} + +static int64_t calculate_constant2(lua_State* L, struct parser* P, struct token* tok); + +/* () */ +static int64_t calculate_constant1(lua_State* L, struct parser* P, struct token* tok) +{ + int64_t ret; + + if (tok->type == TOK_NUMBER) { + ret = tok->integer; + next_token(L, P, tok); + return ret; + + } else if (tok->type == TOK_TOKEN) { + /* look up name in constants table */ + push_upval(L, &constants_key); + lua_pushlstring(L, tok->str, tok->size); + lua_rawget(L, -2); + lua_remove(L, -2); /* constants table */ + + if (!lua_isnumber(L, -1)) { + lua_pushlstring(L, tok->str, tok->size); + luaL_error(L, "use of undefined constant %s on line %d", lua_tostring(L, -1), P->line); + } + + ret = (int64_t) lua_tonumber(L, -1); + lua_pop(L, 1); + next_token(L, P, tok); + return ret; + + } else if (tok->type == TOK_OPEN_PAREN) { + struct parser before_cast = *P; + int top = lua_gettop(L); + + /* see if this is a numeric cast, which we ignore */ + lua_pushcfunction(L, &try_cast); + lua_pushlightuserdata(L, P); + if (!lua_pcall(L, 1, 0, 0)) { + next_token(L, P, tok); + return calculate_constant2(L, P, tok); + } + lua_settop(L, top); + + *P = before_cast; + ret = calculate_constant(L, P); + + require_token(L, P, tok); + if (tok->type != TOK_CLOSE_PAREN) { + luaL_error(L, "error whilst parsing constant at line %d", P->line); + } + + next_token(L, P, tok); + return ret; + + } else if (tok->type == TOK_STRING) { + ret = string_to_int(tok->str, tok->size); + + next_token(L, P, tok); + return ret; + + } else { + return luaL_error(L, "unexpected token whilst parsing constant at line %d", P->line); + } +} + +/* ! and ~, unary + and -, and sizeof */ +static int64_t calculate_constant2(lua_State* L, struct parser* P, struct token* tok) +{ + if (tok->type == TOK_LOGICAL_NOT) { + require_token(L, P, tok); + return !calculate_constant2(L, P, tok); + + } else if (tok->type == TOK_BITWISE_NOT) { + require_token(L, P, tok); + return ~calculate_constant2(L, P, tok); + + } else if (tok->type == TOK_PLUS) { + require_token(L, P, tok); + return calculate_constant2(L, P, tok); + + } else if (tok->type == TOK_MINUS) { + require_token(L, P, tok); + return -calculate_constant2(L, P, tok); + + } else if (tok->type == TOK_TOKEN && + (IS_LITERAL(*tok, "sizeof") + || IS_LITERAL(*tok, "alignof") + || IS_LITERAL(*tok, "__alignof__") + || IS_LITERAL(*tok, "__alignof"))) { + + bool issize = IS_LITERAL(*tok, "sizeof"); + struct ctype type; + + require_token(L, P, tok); + if (tok->type != TOK_OPEN_PAREN) { + luaL_error(L, "invalid sizeof at line %d", P->line); + } + + parse_type(L, P, &type); + parse_argument(L, P, -1, &type, NULL, NULL); + lua_pop(L, 2); + + require_token(L, P, tok); + if (tok->type != TOK_CLOSE_PAREN) { + luaL_error(L, "invalid sizeof at line %d", P->line); + } + + next_token(L, P, tok); + + return issize ? ctype_size(L, &type) : type.align_mask + 1; + + } else { + return calculate_constant1(L, P, tok); + } +} + +/* binary * / and % (left associative) */ +static int64_t calculate_constant3(lua_State* L, struct parser* P, struct token* tok) +{ + int64_t left = calculate_constant2(L, P, tok); + + for (;;) { + if (tok->type == TOK_MULTIPLY) { + require_token(L, P, tok); + left *= calculate_constant2(L, P, tok); + + } else if (tok->type == TOK_DIVIDE) { + require_token(L, P, tok); + left /= calculate_constant2(L, P, tok); + + } else if (tok->type == TOK_MODULUS) { + require_token(L, P, tok); + left %= calculate_constant2(L, P, tok); + + } else { + return left; + } + } +} + +/* binary + and - (left associative) */ +static int64_t calculate_constant4(lua_State* L, struct parser* P, struct token* tok) +{ + int64_t left = calculate_constant3(L, P, tok); + + for (;;) { + if (tok->type == TOK_PLUS) { + require_token(L, P, tok); + left += calculate_constant3(L, P, tok); + + } else if (tok->type == TOK_MINUS) { + require_token(L, P, tok); + left -= calculate_constant3(L, P, tok); + + } else { + return left; + } + } +} + +/* binary << and >> (left associative) */ +static int64_t calculate_constant5(lua_State* L, struct parser* P, struct token* tok) +{ + int64_t left = calculate_constant4(L, P, tok); + + for (;;) { + if (tok->type == TOK_LEFT_SHIFT) { + require_token(L, P, tok); + left <<= calculate_constant4(L, P, tok); + + } else if (tok->type == TOK_RIGHT_SHIFT) { + require_token(L, P, tok); + left >>= calculate_constant4(L, P, tok); + + } else { + return left; + } + } +} + +/* binary <, <=, >, and >= (left associative) */ +static int64_t calculate_constant6(lua_State* L, struct parser* P, struct token* tok) +{ + int64_t left = calculate_constant5(L, P, tok); + + for (;;) { + if (tok->type == TOK_LESS) { + require_token(L, P, tok); + left = (left < calculate_constant5(L, P, tok)); + + } else if (tok->type == TOK_LESS_EQUAL) { + require_token(L, P, tok); + left = (left <= calculate_constant5(L, P, tok)); + + } else if (tok->type == TOK_GREATER) { + require_token(L, P, tok); + left = (left > calculate_constant5(L, P, tok)); + + } else if (tok->type == TOK_GREATER_EQUAL) { + require_token(L, P, tok); + left = (left >= calculate_constant5(L, P, tok)); + + } else { + return left; + } + } +} + +/* binary ==, != (left associative) */ +static int64_t calculate_constant7(lua_State* L, struct parser* P, struct token* tok) +{ + int64_t left = calculate_constant6(L, P, tok); + + for (;;) { + if (tok->type == TOK_EQUAL) { + require_token(L, P, tok); + left = (left == calculate_constant6(L, P, tok)); + + } else if (tok->type == TOK_NOT_EQUAL) { + require_token(L, P, tok); + left = (left != calculate_constant6(L, P, tok)); + + } else { + return left; + } + } +} + +/* binary & (left associative) */ +static int64_t calculate_constant8(lua_State* L, struct parser* P, struct token* tok) +{ + int64_t left = calculate_constant7(L, P, tok); + + for (;;) { + if (tok->type == TOK_BITWISE_AND) { + require_token(L, P, tok); + left = (left & calculate_constant7(L, P, tok)); + + } else { + return left; + } + } +} + +/* binary ^ (left associative) */ +static int64_t calculate_constant9(lua_State* L, struct parser* P, struct token* tok) +{ + int64_t left = calculate_constant8(L, P, tok); + + for (;;) { + if (tok->type == TOK_BITWISE_XOR) { + require_token(L, P, tok); + left = (left ^ calculate_constant8(L, P, tok)); + + } else { + return left; + } + } +} + +/* binary | (left associative) */ +static int64_t calculate_constant10(lua_State* L, struct parser* P, struct token* tok) +{ + int64_t left = calculate_constant9(L, P, tok); + + for (;;) { + if (tok->type == TOK_BITWISE_OR) { + require_token(L, P, tok); + left = (left | calculate_constant9(L, P, tok)); + + } else { + return left; + } + } +} + +/* binary && (left associative) */ +static int64_t calculate_constant11(lua_State* L, struct parser* P, struct token* tok) +{ + int64_t left = calculate_constant10(L, P, tok); + + for (;;) { + if (tok->type == TOK_LOGICAL_AND) { + require_token(L, P, tok); + left = (left && calculate_constant10(L, P, tok)); + + } else { + return left; + } + } +} + +/* binary || (left associative) */ +static int64_t calculate_constant12(lua_State* L, struct parser* P, struct token* tok) +{ + int64_t left = calculate_constant11(L, P, tok); + + for (;;) { + if (tok->type == TOK_LOGICAL_OR) { + require_token(L, P, tok); + left = (left || calculate_constant11(L, P, tok)); + + } else { + return left; + } + } +} + +/* ternary ?: (right associative) */ +static int64_t calculate_constant13(lua_State* L, struct parser* P, struct token* tok) +{ + int64_t left = calculate_constant12(L, P, tok); + + if (tok->type == TOK_QUESTION) { + int64_t middle, right; + require_token(L, P, tok); + middle = calculate_constant13(L, P, tok); + if (tok->type != TOK_COLON) { + luaL_error(L, "invalid ternery (? :) in constant on line %d", P->line); + } + require_token(L, P, tok); + right = calculate_constant13(L, P, tok); + return left ? middle : right; + + } else { + return left; + } +} + +int64_t calculate_constant(lua_State* L, struct parser* P) +{ + struct token tok; + int64_t ret; + require_token(L, P, &tok); + ret = calculate_constant13(L, P, &tok); + + if (tok.type != TOK_NIL) { + put_back(P); + } + + return ret; +} From 3d084348589cc6589ea37803a886a6cd58ebfa04 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 30 Jul 2019 10:57:16 +0800 Subject: [PATCH 253/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BF=BD=E7=95=A5?= =?UTF-8?q?=E7=BC=96=E8=AF=91=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lffi/makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/luaclib/src/lffi/makefile b/luaclib/src/lffi/makefile index 0fd4cd37..3117d37d 100644 --- a/luaclib/src/lffi/makefile +++ b/luaclib/src/lffi/makefile @@ -12,7 +12,7 @@ CC = cc INCLUDE = -I/usr/local/include LIB = -L/usr/local/lib -L../ -L../../../ -CFLAGS = -O3 -Wall -shared -fPIC -Wno-unused-function -fno-strict-aliasing +CFLAGS = -O3 -Wall -shared -fPIC -Wno-unused-function -fno-strict-aliasing -Wno-uninitialized DLL = -lcore build: From 3083cafcc1bc2e688a0481ef3b3400c011cc93e8 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 30 Jul 2019 13:00:30 +0800 Subject: [PATCH 254/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0test=5Fffi=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_ffi.lua | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 script/test_ffi.lua diff --git a/script/test_ffi.lua b/script/test_ffi.lua new file mode 100644 index 00000000..7d339a01 --- /dev/null +++ b/script/test_ffi.lua @@ -0,0 +1,37 @@ +local Log = require "logging":new() +local ffi = require "lffi" + +-- -- 数据类型长度测试 +-- Log:DEBUG("uint8_t长度为:"..ffi.sizeof(ffi.new("uint8_t"))) +-- Log:DEBUG("uint16_t长度为:"..ffi.sizeof(ffi.new("uint16_t"))) +-- Log:DEBUG("uint32_t长度为:"..ffi.sizeof(ffi.new("uint32_t"))) +-- Log:DEBUG("uint64_t长度为:"..ffi.sizeof(ffi.new("uint64_t"))) + +-- -- 字符串测试 +-- local cdata = ffi.new("char [?]", #"admin", "admin") +-- Log:DEBUG("将lua字符串转换为cdata:", cdata) +-- local str = ffi.string(cdata) +-- Log:DEBUG("将cdata转换为lua字符串:", str) +-- +-- Log:DEBUG("测试cdata字符串类型是否可以索引:", cdata[0], cdata[1], cdata[2], cdata[3], cdata[4]) +-- Log:DEBUG("测试cdata字符串类型是否可以转换:", string.char(cdata[0])..string.char(cdata[1])..string.char(cdata[2])..string.char(cdata[3])..string.char(cdata[4])) + +-- -- 整型数组测试 +-- local array = ffi.new("int[?]", 3, 1, 2, 3) -- 初始化方法 1 +-- local array = ffi.new("int[3]", 1, 2, 3) -- 初始化方法 2 +-- Log:DEBUG(array[0], array[1], array[2]) + +-- 结构体创建测试 +-- ffi.cdef [[ +-- typedef struct cuboid { uint8_t h, w, l; } cuboid_t; +-- ]] +-- +-- local cuboid = ffi.new("cuboid_t", 2 ^ 4, 2 ^ 5, 2 ^ 6) +-- Log:DEBUG("创建长方体", cuboid, cuboid.h, cuboid.w, cuboid.l) +-- Log:DEBUG("计算体积", cuboid.h * cuboid.w * cuboid.l) +-- +-- local cuboid_array = ffi.new("cuboid_t[3]", {{11, 12, 13}, {21, 22, 23}, {31, 32, 33}}) +-- Log:DEBUG("创建3个长方体并且初始化", cuboid_array) +-- Log:DEBUG("3个长方体的长度分别为:", cuboid_array[0].l, cuboid_array[1].l, cuboid_array[2].l) +-- Log:DEBUG("3个长方体的宽度分别为:", cuboid_array[0].w, cuboid_array[1].w, cuboid_array[2].w) +-- Log:DEBUG("3个长方体的高度分别为:", cuboid_array[0].h, cuboid_array[1].h, cuboid_array[2].h) From d3dc86bf24c698adf6a1585c947d4204620d9fc5 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 31 Jul 2019 01:03:41 +0800 Subject: [PATCH 255/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0Makefile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lffi/makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/luaclib/src/lffi/makefile b/luaclib/src/lffi/makefile index 3117d37d..e3352855 100644 --- a/luaclib/src/lffi/makefile +++ b/luaclib/src/lffi/makefile @@ -12,15 +12,15 @@ CC = cc INCLUDE = -I/usr/local/include LIB = -L/usr/local/lib -L../ -L../../../ -CFLAGS = -O3 -Wall -shared -fPIC -Wno-unused-function -fno-strict-aliasing -Wno-uninitialized +CFLAGS = -O3 -Wall -shared -fPIC -Wno-unused-function -fno-strict-aliasing -Wno-uninitialized -Wno-ignored-attributes DLL = -lcore build: - $(CC) -o lffi.so ffi.c parser.c ctype.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) + $(CC) -o lffi.so ffi.c parser.c ctype.c $(CFLAGS) $(INCLUDE) $(LIB) $(DLL) mv *.so ../../ rebuild: - $(CC) -o lffi.so ffi.c parser.c ctype.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) + $(CC) -o lffi.so ffi.c parser.c ctype.c $(CFLAGS) $(INCLUDE) $(LIB) $(DLL) mv *.so ../../ clean: From 328d4c1afef314dedd62c25c07543c66afa37543 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 31 Jul 2019 22:50:21 +0800 Subject: [PATCH 256/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 44 ++++++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 3b1e4c65..8e46988f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## core_framework 一个基于libev的轻量级lua网络开发框架 +# Lua 轻量级网络开发框架

                  @@ -6,41 +6,33 @@ + +

                  -> 大道至简, 返璞归真. 简单? 不简单! - -> 没有其它框架比它更容易入手! 如果有, 那就是借鉴了它. - -> 使用cf后: 你无需[996](https://github.com/996icu/996.ICU), 不会进入[ICU](https://github.com/996icu/996.ICU) + **大道至简, 返璞归真.** ---- + **简单? 不简单!** -## 介绍一下CF +## 介绍 -> 文档在这里: + * [CF是什么?](https://github.com/CandyMi/core_framework/wiki/home) -* [CF是什么?](https://github.com/CandyMi/core_framework/wiki/home) - -* [CF使用到的技术栈?](https://github.com/CandyMi/core_framework/wiki/MAP) + * [CF使用到的技术栈?](https://github.com/CandyMi/core_framework/wiki/MAP) ## 第一次安装CF -> [CF如何安装?](https://github.com/CandyMi/core_framework/wiki/install) - -> [CF如何运行?](https://github.com/CandyMi/core_framework/wiki/RUN) - -> [如何在容器内运行?](https://github.com/CandyMi/core_framework/wiki/Docker) + * [CF如何安装?](https://github.com/CandyMi/core_framework/wiki/install) -## 第一次使用CF遇到的问题 + * [CF如何运行?](https://github.com/CandyMi/core_framework/wiki/RUN) -> [我有一些cf使用上的问题?](https://github.com/CandyMi/core_framework/wiki/QA) + * [如何在容器内运行?](https://github.com/CandyMi/core_framework/wiki/Docker) -## 看一下cf的通用后台开发模板预览图吧! +## 内置后台预览图

                  @@ -58,19 +50,19 @@

                  -> [如何快速体验cfadmin后台?](https://github.com/CandyMi/core_framework/wiki/cfadmin) + **[快速体验cfadmin后台](https://github.com/CandyMi/core_framework/wiki/cfadmin)** ## 联系作者 -> [issues](https://github.com/CandyMi/core_framework/issues) + * [issues](https://github.com/CandyMi/core_framework/issues) -> 作者邮箱 + * 作者邮箱 -> QQ群:**727531854** + * QQ群:**[727531854](https://shang.qq.com/wpa/qunwpa?idkey=5cc977ebaf4eb17391b2c6b03eb0ee36e3d3c1871bc95ba3c96ffc426a9dc907)** ## 支持 -> 如果您觉得cf还不错, 可以支持一杯咖啡予以鼓励. + **如果您觉得cf还不错, 可以支持一杯咖啡予以鼓励.**

                  @@ -79,4 +71,4 @@ ## 授权协议 -[BSD LICENSE](https://github.com/CandyMi/core_framework/blob/master/LICENSE) + [BSD LICENSE](https://github.com/CandyMi/core_framework/blob/master/LICENSE) From a16d5d245f8d431954f1dd566157f26f3e1f1441 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 31 Jul 2019 23:26:44 +0800 Subject: [PATCH 257/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 42 +++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 8e46988f..a16d7663 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,32 @@ # Lua 轻量级网络开发框架

                  - - - - - - - - + + + +

                  -

                  - -

                  +

                  - **大道至简, 返璞归真.** +> **大道至简, 返璞归真. 简单? 不简单!** - **简单? 不简单!** +## 优势 -## 介绍 + * **高效率** —— 高效的静态语言与高效的虚拟机实现优秀的运行时框架. - * [CF是什么?](https://github.com/CandyMi/core_framework/wiki/home) + * **生态多** —— 集成社区库最多的框架之一, 并自行实现了一些网络协议生态. - * [CF使用到的技术栈?](https://github.com/CandyMi/core_framework/wiki/MAP) + * **可读性高** —— 可读性始终是维护框架的标准, 无痛入门才是值得提倡的. -## 第一次安装CF + * **稳定性好** —— 目前已有在一些企业内部已经开始使用, 使用者也正在逐渐增多. - * [CF如何安装?](https://github.com/CandyMi/core_framework/wiki/install) +## 文档 - * [CF如何运行?](https://github.com/CandyMi/core_framework/wiki/RUN) + * [WIKI](https://github.com/CandyMi/core_framework/wiki) - * [如何在容器内运行?](https://github.com/CandyMi/core_framework/wiki/Docker) + * [在线文档](https://candymi.github.io/LuaWeb) -## 内置后台预览图 +## 预览图

                  @@ -52,13 +46,11 @@ **[快速体验cfadmin后台](https://github.com/CandyMi/core_framework/wiki/cfadmin)** -## 联系作者 +## 反馈 * [issues](https://github.com/CandyMi/core_framework/issues) - * 作者邮箱 - - * QQ群:**[727531854](https://shang.qq.com/wpa/qunwpa?idkey=5cc977ebaf4eb17391b2c6b03eb0ee36e3d3c1871bc95ba3c96ffc426a9dc907)** + * **QQ群**:[727531854](https://shang.qq.com/wpa/qunwpa?idkey=5cc977ebaf4eb17391b2c6b03eb0ee36e3d3c1871bc95ba3c96ffc426a9dc907) ## 支持 From bfa7b76b0726d1ebda16cfc7ecb883d4d33feae0 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 31 Jul 2019 23:37:28 +0800 Subject: [PATCH 258/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=B8=80=E4=BA=9BAPI?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lsys.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/luaclib/src/lsys.c b/luaclib/src/lsys.c index d5b8dfed..2ff3564e 100644 --- a/luaclib/src/lsys.c +++ b/luaclib/src/lsys.c @@ -13,8 +13,7 @@ static int /* 此方法可用于检查是否为有效ipv4地址*/ lipv4(lua_State *L){ const char *IP = lua_tostring(L, 1); if (!IP) return luaL_error(L, "ipv4 error: 请至少传递一个string类型参数\n"); - if (ipv4(IP)) lua_pushboolean(L, 1); - else lua_pushboolean(L, 0); + lua_pushboolean(L, ipv4(IP)); return 1; } @@ -22,8 +21,7 @@ static int /* 此方法可用于检查是否为有效ipv6地址*/ lipv6(lua_State *L){ const char *IP = lua_tostring(L, 1); if (!IP) return luaL_error(L, "ipv6 error: 请至少传递一个string类型参数\n"); - if (ipv6(IP)) lua_pushboolean(L, 1); - else lua_pushboolean(L, 0); + lua_pushboolean(L, ipv6(IP)); return 1; } From 21a708bff9614b5418ea976b502b34e9588ea77c Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 1 Aug 2019 12:57:01 +0800 Subject: [PATCH 259/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=9C=AA=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E5=8F=82=E6=95=B0=E7=B1=BB=E5=9E=8B=E5=88=A4=E6=96=AD?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/DB/init.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index ac68e2c9..c1b14df7 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -494,7 +494,10 @@ function DB:execute (rkey, ...) local req1 = {} for q = 1, qua do local key = arg_key..q - local value = arg_values[q]:gsub("'", "\\'") + local value = arg_values[q] + if type(value) == 'string' then + value = value:gsub("'", "\\'") + end arg_keys[#arg_keys+1] = key req1[#req1+1] = concat({key, "=", "'", value, "'"}) -- Log:DEBUG(key, value) From 46503b3ac1f64fa7d5aa322e0c445df861e0e5a3 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 1 Aug 2019 13:45:36 +0800 Subject: [PATCH 260/956] =?UTF-8?q?=E4=BC=98=E5=8C=96admin=E7=9A=84?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/db/header.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lualib/admin/db/header.lua b/lualib/admin/db/header.lua index 0536044f..b93318ad 100644 --- a/lualib/admin/db/header.lua +++ b/lualib/admin/db/header.lua @@ -1,4 +1,4 @@ -local tonumber = tonumber +local toint = math.tointeger local os_time = os.time local fmt = string.format @@ -6,8 +6,8 @@ local header = {} -- header 列表 function header.header_list (db, opt) - local limit = tonumber(opt.limit) or 10 - local page = tonumber(opt.page) or 1 + local limit = toint(opt.limit) or 10 + local page = toint(opt.page) or 1 return db:query(fmt([[SELECT id, name, url, create_at, update_at FROM cfadmin_headers WHERE active = '1' ORDER BY id LIMIT %s, %s]], limit * (page - 1) , limit)) end From aec274cf5861361ff6fae5044c015d166ecb2d8b Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 1 Aug 2019 14:08:51 +0800 Subject: [PATCH 261/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=81=B6=E6=84=8F=E6=95=B0=E6=8D=AE=E9=80=A0=E6=88=90=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/db/user.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lualib/admin/db/user.lua b/lualib/admin/db/user.lua index 58806054..362b82d8 100644 --- a/lualib/admin/db/user.lua +++ b/lualib/admin/db/user.lua @@ -34,7 +34,7 @@ end -- 用户是否存在 function user.user_exists (db, username, uid) - return db:query(fmt([[ + local user, err = db:query(fmt([[ SELECT `cfadmin_users`.id, `cfadmin_users`.name, @@ -44,7 +44,11 @@ function user.user_exists (db, username, uid) WHERE `cfadmin_users`.active = '1' AND `cfadmin_users`.username = '%s' OR `cfadmin_users`.id = '%s' LIMIT 1]], - tostring(username), toint(uid)))[1] + tostring(username), toint(uid))) + if not user then + return + end + return user[1] end -- 用户名或者登录名是否存在 From a1d8664855ce4238fcf72640220f72a40f8a013e Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 1 Aug 2019 16:23:35 +0800 Subject: [PATCH 262/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8DDB=E9=A2=84=E7=BC=96?= =?UTF-8?q?=E8=AF=91=E7=9A=84=E4=B8=80=E4=BA=9B=E9=97=AE=E9=A2=98.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/DB/init.lua | 380 +++------------------------------------------ 1 file changed, 19 insertions(+), 361 deletions(-) diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index c1b14df7..c28c572d 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -1,11 +1,14 @@ +local class = require "class" local mysql = require "protocol.mysql" local timer = require "internal.Timer" -local co = require "internal.Co" -local class = require "class" + local log = require "logging" -local crypt = require "crypt" local Log = log:new({ dump = true, path = 'DB'}) +local crypt = require "crypt" +local hashkey = crypt.hashkey + +local co = require "internal.Co" local co_self = co.self local co_wait = co.wait local co_wakeup = co.wakeup @@ -30,44 +33,6 @@ local remove = table.remove local concat = table.concat local unpack = table.unpack -local randomkey = crypt.randomkey - -local SELECT = "SELECT" - -local INSERT = "INSERT INTO" - -local UPDATE = "UPDATE" - -local DELETE = "DELETE" - -local SET = "SET" - -local FROM = "FROM" - -local WHERE = "WHERE" - -local IN = "IN" - -local IS = "IS" - -local NOT = "NOT" - -local BETWEEN = "BETWEEN" - -local AND = " AND " - -local LIMIT = "LIMIT" - -local DESC = "DESC" - -local ASC = "ASC" - -local ORDERBY = "ORDER BY" - -local GROUPBY = "GROUP BY" - -local COMMA = ", " - -- 空闲连接时间 local WAIT_TIMEOUT = 31536000 @@ -91,8 +56,10 @@ local function DB_CREATE (opt) db:query(fmt('SET GLOBAL wait_timeout=%s', WAIT_TIMEOUT)) db:query(fmt('SET GLOBAL interactive_timeout=%s', WAIT_TIMEOUT)) end - if opt.precomp then - return opt:reprepare() + if opt.stmts then + for rkey, stmt in pairs(opt.stmts) do + assert(db:query(stmt), "["..stmt.."] 预编译失败.") + end end return db end @@ -122,222 +89,6 @@ local function pop_db(self) return co_wait() end - --- 格式化处理函数 --- 将['field', '=', 'a'] 格式化为 field = a --- 将['field', 'NOT', 'IN', '(1, 2, 3)'] 格式化为 field NOT IN (1, 2, 3) -local function format(t) - local tab = {} - for index=1, #t, 1 do - tab[#tab+1] = "%s" - end - return fmt(concat(tab, ' '), unpack(t)) -end - -local function format_value1(t) - return fmt("%s %s '%s'", unpack(t)) -end - -local function format_value2(t, split) - local tmp = {} - for i=1, #t do - tmp[i] = "'%s'" - end - return fmt(concat(tmp, split), unpack(t)) -end - -local function format_value3(t) - return fmt("%s %s '%s'", unpack(t)) -end - -local function format_value4(t) - return fmt("%s %s %s '%s'", unpack(t)) -end - -local function limit(query, limit1, limit2) - local t1 = type(limit1) - local t2 = type(limit2) - assert(query and type(query) == 'table' and (t1 == "number" or t1 == "string" ), "错误的限制条件(limit):"..tostring(limit1)) - - insert(query, LIMIT) - - local limits = {tostring(limit1)} - if t2 == "number" or tpy == "string" then - insert(limits, tostring(limit2)) - end - - insert(query, concat(limits, COMMA)) - - return query -end - --- 降序 -local function desc(query) - insert(query, DESC) - return query -end --- 升序 -local function asc(query) - insert(query, ASC) - return query -end - --- 聚合字段 -local function groupby(query, fields) - local tpy = type(fields) - assert(query and (tpy == "string" or tpy == "table"), "错误的聚合字段类型(fields):"..tostring(fields)) - insert(query, GROUPBY) - if tpy == "string" then - insert(query, fields) - end - if tpy == "table" then - insert(query, concat(fields, COMMA)) - end - return query -end - --- 排序字段 -local function orderby(query, orders) - local tpy = type(orders) - assert(query and (tpy == "string" or tpy == "table"), "错误的排序条件(orders):"..tostring(orders)) - insert(query, ORDERBY) - if tpy == "string" then - insert(query, orders) - end - if tpy == "table" then - insert(query, concat(orders, COMMA)) - end - return query -end - --- 条件 -local function where(query, conditions) - local tpy = type(conditions) - assert(query and (tpy == "string" or tpy == "table"), "错误的条件类型(where):"..tostring(conditions)) - insert(query, WHERE) - if tpy == "string" then - insert(query, conditions) - end - if tpy == "table" then - local CONDITIONS = {} - for index, condition in ipairs(conditions) do - if type(condition) == "table" and #condition == 3 then - local con2 = upper(condition[2]) - if con2 == IN or con2 == BETWEEN then -- 假设比较符是IN 或者 BETWEEN - local LEFT, RIGHT = '', '' - local c = AND - if con2 == IN then - c = COMMA - LEFT, RIGHT = '(', ')' - end - insert(CONDITIONS, format({condition[1], con2, LEFT..format_value2(condition[3], c)..RIGHT})) - elseif con2 == IS then - insert(CONDITIONS, concat(condition, " ")) - elseif find(condition[3], condition[1]) or find(condition[3], '`') then - insert(CONDITIONS, concat(condition, " ")) - else - insert(CONDITIONS, format_value3(condition)) - end - elseif type(condition) == "table" and #condition == 4 then - local con2 = upper(condition[2]) - local con3 = upper(condition[3]) - if con3 == IN or con3 == BETWEEN then -- 假设比较符是IN 或者 BETWEEN - local LEFT, RIGHT = '', '' - local c = AND - if con3 == IN then - c = COMMA - LEFT, RIGHT = '(', ')' - end - insert(CONDITIONS, format({condition[1], con2, con3, LEFT..format_value2(condition[4], c)..RIGHT})) - elseif con2 == IS then - insert(CONDITIONS, concat(condition, " ")) - else - insert(CONDITIONS, format_value4(condition)) - end - else -- 假设condition为 "AND" 、 "OR" - insert(CONDITIONS, upper(condition)) - end - end - insert(query, concat(CONDITIONS, " ")) - end - query.WHERE = true - return query -end - --- 表(s) -local function from(query, tables) - local tpy = type(tables) - assert(query and (tpy == "string" or tpy == "table"), "错误的表名:"..tostring(tables)) - insert(query, FROM) - if tpy == "string" then - insert(query, tables) - end - if tpy == "table" then - insert(query, concat(tables, COMMA)) - end - query.FROM = true - return query -end - --- 插入语句专用函数 -- -local function values(query, values) - local tpy = type(values) - assert(tpy == "table" and #values > 0 and type(values[1]) == "table" , "错误的值类型(values):"..tostring(values)) - local VALUES = {} - for _, value in ipairs(values) do - insert(VALUES, "("..format_value2(value, COMMA)..")") - end - insert(query, "VALUES") - insert(query, concat(VALUES, COMMA)) - query.VALUES = true - return query -end - -local function fields(query, fields) - local tpy = type(fields) - assert(tpy == "table" and #fields > 0, "错误的字段类型(values):"..tostring(fields)) - insert(query, "("..concat(fields, COMMA)..")") - query.FILEDS = true - return query -end --- 插入语句专用函数 -- - --- 更新语句专用 -- -local function set(query, values) - local tpy = type(values) - assert(tpy == "table" and #values > 0 and type(values[1]) == "table" , "错误的值类型(values):"..tostring(values)) - local VALUES = {} - for _, value in ipairs(values) do - insert(VALUES, format_value1(value)) - end - insert(query, SET) - insert(query, concat(VALUES, COMMA)) - return query -end --- 更新语句专用 -- - --- 执行 -local function execute(query) - -- Log:DEBUG(query) - if query.SELECT then - assert(query.FROM and query.WHERE, "查询语句必须使用from方法与where条件.") - end - if query.DELETE then - assert(query.WHERE, "删除语句请加上where条件") - end - if query.INSERT then - assert(query.FILEDS and query.VALUES, "插入语句请加上fields与values") - end - if query.UPDATE then - assert(query.WHERE, "更新语句请加上where条件") - end - local QUERY = concat(query, " ") - -- Log:DEBUG(QUERY) - local self = query.self - query.self = nil - return self:query(QUERY) -end - local DB = class("DB") function DB:ctor(opt) @@ -364,123 +115,30 @@ function DB:connect () return self.INITIALIZATION end --- 查询语句 -function DB:select(fields) - if not self.INITIALIZATION then - return nil, "DB尚未初始化" - end - local tpy = type(fields) - assert(tpy == "string" or tpy == "table", "错误的字段类型(fields):"..tostring(fields)) - local query = { - self = self, - [1] = SELECT, - SELECT = true, - from = from, - where = where, - orderby = orderby, - groupby = groupby, - desc = desc, - asc = asc, - limit = limit, - execute = execute, - } - if tpy == "string" then - insert(query, fields) - end - if tpy == "table" then - insert(query, concat(fields, COMMA)) - end - return query -end - - --- 更新语句 -function DB:update(table_name) - if not self.INITIALIZATION then - return nil, "DB尚未初始化" - end - local tpy = type(table_name) - assert(tpy == "string" or tpy == "tables", "错误的表名(table_name):"..tostring(table_name)) - return { - self = self, - [1] = UPDATE, - [2] = table_name, - UPDATE = true, - set = set, - where = where, - limit = limit, - execute = execute, - } -end - - --- 插入语句 -function DB:insert(table_name) - if not self.INITIALIZATION then - return nil, "DB尚未初始化" - end - local tpy = type(table_name) - assert(tpy == "string", "错误的表名(table_name):"..tostring(table_name)) - return { - self = self, - [1] = INSERT, - [2] = table_name, - INSERT = true, - fields = fields, - values = values, - execute = execute, - } -end - --- 删除语句 -function DB:delete(table_name) - if not self.INITIALIZATION then - return nil, "DB尚未初始化" - end - local tpy = type(table_name) - assert(tpy == "string" or tpy == "tables", "错误的表名(table_name):"..tostring(table_name)) - return { - self = self, - [1] = DELETE, - [2] = FROM, - [3] = table_name, - DELETE = true, - where = where, - limit = limit, - orderby = orderby, - execute = execute, - } -end - --- 重新执行编译 -function DB:reprepare () - for rkey, stmt in pairs(self.prepare) do - assert(self:query(stmt), "["..stmt.."] 预编译失败.") - end - return true -end - -- PREPARE function DB:prepare (sql) if type(sql) ~= 'string' or sql == '' then return nil, "试图传递一个无效的SQL语句" end - if not self.precomp then - self.precomp = {} + if not self.stmts then + self.stmts = {} + end + local rkey = hashkey(sql, true) + if self.stmts[rkey] then + return rkey end - local rkey = randomkey(true) local stmt = fmt([[PREPARE %s FROM "%s"]], rkey, sql) assert(self:query(stmt), "["..sql.."] 预编译失败.") - self.precomp[rkey] = stmt + self.stmts[rkey] = stmt return rkey end -- EXECUTE function DB:execute (rkey, ...) - if not self.precomp then + if not self.stmts then return nil, "尚未有任何预编译语句" end - local stmt = self.precomp[rkey] + local stmt = self.stmts[rkey] if not stmt then return nil, "找不到这个预编译语句." end From 7e8353fa1b5e07bbb27b9cb77d728fa02adc31cf Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 1 Aug 2019 16:29:06 +0800 Subject: [PATCH 263/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0DB=E9=A2=84=E7=BC=96?= =?UTF-8?q?=E8=AF=91=E4=B8=8E=E6=89=A7=E8=A1=8C=E8=AF=AD=E5=8F=A5=E7=A4=BA?= =?UTF-8?q?=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_DB.lua | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/script/test_DB.lua b/script/test_DB.lua index bd2295a3..4cddc96b 100644 --- a/script/test_DB.lua +++ b/script/test_DB.lua @@ -146,3 +146,14 @@ cf.fork(function ( ... ) var_dump(ret) end) + + +cf.fork(function ( ... ) + local rkey = db:prepare([[SELECT version() AS version]]) + local ret, err = db:execute(rkey) + if not ret then + return print(err) + end + + var_dump(ret) +end) From 391df6d831c9933d511fa117bc2ccf2baa5e1c9a Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 1 Aug 2019 16:49:50 +0800 Subject: [PATCH 264/956] =?UTF-8?q?=E4=BC=98=E5=8C=96admin=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=88=A4=E6=96=AD,=20=E4=BC=98=E5=8C=96=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E6=8F=90=E7=A4=BA=E5=8F=8B=E5=A5=BD=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/db/user.lua | 4 ++-- lualib/admin/http/profile.lua | 12 +++++++++--- lualib/admin/http/system/header.lua | 12 +++++++++--- lualib/admin/http/system/user.lua | 10 ++++++++-- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/lualib/admin/db/user.lua b/lualib/admin/db/user.lua index 362b82d8..f7d10a64 100644 --- a/lualib/admin/db/user.lua +++ b/lualib/admin/db/user.lua @@ -104,12 +104,12 @@ end -- 更新用户密码 function user.user_update_password (db, opt) - return db:query(fmt([[UPDATE cfadmin_users SET password = '%s' WHERE id = '%s' AND active = '1']], opt.password, opt.id)) + return db:query(fmt([[UPDATE cfadmin_users SET password = '%s', update_at = '%s' WHERE id = '%s' AND active = '1']], opt.password, os_time(), opt.id)) end -- 更新用户信息 function user.user_update_info (db, opt) - return db:query(fmt([[UPDATE cfadmin_users SET name = '%s', phone = '%s', email = '%s' WHERE id = '%s' AND active = '1']], opt.name, opt.phone, opt.email, opt.id)) + return db:query(fmt([[UPDATE cfadmin_users SET name = '%s', phone = '%s', email = '%s', update_at = '%s' WHERE id = '%s' AND active = '1']], opt.name, opt.phone, opt.email, os_time(), opt.id)) end return user diff --git a/lualib/admin/http/profile.lua b/lualib/admin/http/profile.lua index ec6d92a8..2be5e7fc 100644 --- a/lualib/admin/http/profile.lua +++ b/lualib/admin/http/profile.lua @@ -128,17 +128,23 @@ function profile.response (content) if user_info.password == args.password then return json_encode({code = 403, msg = "4. 当前密码不正确或新老密码一致" }) end - user.user_update_password(db, args) + local ok = user.user_update_password(db, args) + if not ok then + return json_encode({code = 401, msg = '5. 密码更新失败'}) + end user_token.token_delete(db, args.id) return json_encode({code = 0, msg = 'Success: 密码修改成功'}) end if args.action == 'update_userinfo' then if not args.name or not args.email or not args.phone then - return json_encode({code = 500, msg = "2. 错误的info参数" }) + return json_encode({code = 500, msg = "1. 错误的info参数" }) end args.name = url_decode(args.name) args.email = url_decode(args.email) - user.user_update_info(db, args) + local ok = user.user_update_info(db, args) + if not ok then + return json_encode({code = 401, msg = '2. 用户信息更新失败'}) + end user_token.token_delete(db, args.id) return json_encode({code = 0, msg = 'Success: 用户信息更新成功'}) end diff --git a/lualib/admin/http/system/header.lua b/lualib/admin/http/system/header.lua index 05c63692..d2f95d89 100644 --- a/lualib/admin/http/system/header.lua +++ b/lualib/admin/http/system/header.lua @@ -154,11 +154,14 @@ function system.header_response (content) end if args.action == 'add' then if not args.url or not args.name then - return json_encode({ code = 400, data = null, msg = "添加导航栏参数错误"}) + return json_encode({ code = 400, data = null, msg = "1. 添加导航栏参数错误"}) end args.url = url_decode(args.url) args.name = url_decode(args.name) - header.header_add(db, args) + local ok = header.header_add(db, args) + if not ok then + return json_encode({code = 401, msg = "2. 添加导航失败"}) + end return json_encode({code = 0, msg = "添加成功"}) end if args.action == 'edit' then @@ -172,7 +175,10 @@ function system.header_response (content) end args.url = url_decode(args.url) args.name = url_decode(args.name) - header.header_update(db, args) + local ok = header.header_update(db, args) + if not ok then + return json_encode({code = 401, msg = "3. 修改失败"}) + end return json_encode({ code = 0, msg = "修改成功"}) end return json_encode({code = 500, data = null, msg = '恭喜您完美的错过了所有正确参数'}) diff --git a/lualib/admin/http/system/user.lua b/lualib/admin/http/system/user.lua index 9578b1fa..1493a6de 100644 --- a/lualib/admin/http/system/user.lua +++ b/lualib/admin/http/system/user.lua @@ -152,7 +152,10 @@ function system.user_response (content) return json_encode({code = 400, data = null, msg = '4. 用户已存在'}) end args.password = crypt.hexencode(crypt.sha1(args.password)) - user.user_add(db, args) + local ok = user.user_add(db, args) + if not ok then + return json_encode({code = 401, msg = "5. 添加用户失败"}) + end return json_encode({code = 0, msg = "添加成功"}) end -- 删除用户 @@ -194,7 +197,10 @@ function system.user_response (content) end args.email = url_decode(args.email) args.password = crypt.hexencode(crypt.sha1(url_decode(args.password))) - user.user_update(db, args) + local ok = user.user_update(db, args) + if not ok then + return json_encode({code = 401, msg = "5. 更新用户信息失败"}) + end user_token.token_delete(db, args.id) -- 清除Token return json_encode({code = 0, msg = "SUCCESS"}) end From e89be8ad4aea9b3a5e245223eda1fc76afdcd4a8 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 1 Aug 2019 17:00:33 +0800 Subject: [PATCH 265/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=8D=E8=83=BD?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=BD=93=E5=89=8D=E7=94=A8=E6=88=B7=E5=88=A4?= =?UTF-8?q?=E6=96=AD,=20=E4=BC=98=E5=8C=96=E5=88=A0=E9=99=A4admin=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/http/system/user.lua | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lualib/admin/http/system/user.lua b/lualib/admin/http/system/user.lua index 1493a6de..b42547bf 100644 --- a/lualib/admin/http/system/user.lua +++ b/lualib/admin/http/system/user.lua @@ -160,13 +160,16 @@ function system.user_response (content) end -- 删除用户 if action == 'delete' then - local uid = toint(args.uid) + local uid = toint(args.id) if not uid then - return json_encode({code = 400, data = null, msg = '1. 未知的uid'}) + return json_encode({code = 400, data = null, msg = '1. 未知的用户ID'}) + end + if exists.uid == uid then + return json_encode({code = 401, data = null, msg = "2. 不能删除当前用户"}) end local exists = user.user_exists(db, nil, uid) if not exists then - return json_encode({code = 400, data = null, msg = '2. 试图删除不存在的用户'}) + return json_encode({code = 403, data = null, msg = '3. 试图删除不存在的用户'}) end user.user_delete(db, uid) user_token.token_delete(db, uid) -- 清除Token From 9dc7fee5118a75b3fc84b2298220b773a89567c5 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 1 Aug 2019 18:35:04 +0800 Subject: [PATCH 266/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=A7=92=E8=89=B2?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E4=B8=8E=E5=88=A0=E9=99=A4=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=E7=9A=84=E6=8F=90=E7=A4=BA,=20=E4=BC=98=E5=8C=96=E4=B8=9A?= =?UTF-8?q?=E5=8A=A1=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/db/role.lua | 11 +++++++++-- lualib/admin/html/system/role/role-add.html | 2 +- lualib/admin/http/system/role.lua | 14 ++++++++++---- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/lualib/admin/db/role.lua b/lualib/admin/db/role.lua index 3dd9504a..2b7becca 100644 --- a/lualib/admin/db/role.lua +++ b/lualib/admin/db/role.lua @@ -69,9 +69,16 @@ end function role.role_delete (db, id) local now = os_time() -- 删除角色 - db:query(fmt([[UPDATE cfadmin_roles SET active = '0', update_at = '%s' WHERE id = '%s' AND active = '1']], now, id)) + local ok = db:query(fmt([[UPDATE cfadmin_roles SET active = '0', update_at = '%s' WHERE id = '%s' AND active = '1']], now, id)) + if not ok then + return + end -- 删除角色对应的权限 - db:query(fmt([[UPDATE cfadmin_permissions SET active = '0', update_at = '%s' WHERE role_id = '%s' AND active = '1']], now, id)) + local ok = db:query(fmt([[UPDATE cfadmin_permissions SET active = '0', update_at = '%s' WHERE role_id = '%s' AND active = '1']], now, id)) + if not ok then + return + end + return true end -- 更新role相关数据 diff --git a/lualib/admin/html/system/role/role-add.html b/lualib/admin/html/system/role/role-add.html index 5d795b08..fdf2d07c 100644 --- a/lualib/admin/html/system/role/role-add.html +++ b/lualib/admin/html/system/role/role-add.html @@ -80,7 +80,7 @@ } // 没菜单不允许修改 if (permissions.length <= 0) { - layer.msg('permissions was empty', {timeout: 2000}, function () { + layer.msg('创建失败: 请先创建菜单后再进行角色关联.', {timeout: 2000}, function () { $('#submit').show(); }); return false; diff --git a/lualib/admin/http/system/role.lua b/lualib/admin/http/system/role.lua index 14c36239..5dae7141 100644 --- a/lualib/admin/http/system/role.lua +++ b/lualib/admin/http/system/role.lua @@ -184,14 +184,20 @@ function system.role_response (content) if args.action == 'delete' then local id = toint(args.id) if not id then - return json_encode({code = 400, msg = "1. 删除失败"}) + return json_encode({code = 400, msg = "1. 错误的参数"}) end local exists = role.role_id_exists(db, id) if not exists then - return json_encode({code = 400, msg = "2. 试图删除一个不存在的role"}) + return json_encode({code = 400, msg = "2. 该角色不存在"}) end - role.role_delete(db, id) - return json_encode({code = 0, msg = "role删除成功"}) + if user_info.role == id then + return json_encode({code = 400, msg = "3. 不可删除此角色"}) + end + local ok = role.role_delete(db, id) + if not ok then + return json_encode({code = 400, msg = "4. 删除此角色失败"}) + end + return json_encode({code = 0, msg = "角色删除成功"}) end return json_encode({code = 500, msg = '恭喜您完美的错过了所有正确参数'}) end From 4ec878dd2bdbb1015059bfb045ec0d5e66371a67 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 2 Aug 2019 02:46:09 +0800 Subject: [PATCH 267/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AAco?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/TCP.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index 2c332a4a..eff909bb 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -315,6 +315,7 @@ function TCP:ssl_connect(domain, port) if not self.ssl_ctx or not self.ssl then return nil, "create a ssl ctx error." end + local co = co_self() self.CONNECT_IO = tcp_pop() self.connect_current_co = co_self() self.connect_co = co_new(function () From d455d9572d3c321473a632d4427cb97c1dcc5e73 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 2 Aug 2019 12:56:26 +0800 Subject: [PATCH 268/956] =?UTF-8?q?=E4=BC=98=E5=8C=96httpc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/protocol.lua | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua index d5180171..16f761de 100644 --- a/lualib/httpc/protocol.lua +++ b/lualib/httpc/protocol.lua @@ -5,6 +5,10 @@ local FILEMIME = HTTP.FILEMIME local PARSER_HTTP_RESPONSE = HTTP.PARSER_HTTP_RESPONSE local RESPONSE_CHUNKED_PARSER = HTTP.RESPONSE_CHUNKED_PARSER +local type = type +local assert = assert +local ipairs = ipairs +local tostring = tostring local random = math.random local find = string.find @@ -17,10 +21,6 @@ local insert = table.insert local concat = table.concat local toint = math.tointeger local fmt = string.format -local type = type -local assert = assert -local ipairs = ipairs -local tostring = tostring local CRLF = '\x0d\x0a' local CRLF2 = '\x0d\x0a\x0d\x0a' @@ -142,8 +142,6 @@ local function httpc_response(sock, SSL) local posA, posB = find(DATA, CRLF2) if posB then VERSION, CODE, STATUS, HEADER = PARSER_HTTP_RESPONSE(DATA) - -- CODE = RESPONSE_PROTOCOL_PARSER(split(DATA, 1, posB)) - -- HEADER = RESPONSE_HEADER_PARSER(split(DATA, 1, posB)) if not CODE or not HEADER then return nil, SSL.." can't resolvable protocol." end @@ -248,7 +246,7 @@ local function build_post_req (opt) } if type(opt.headers) == "table" then for _, header in ipairs(opt.headers) do - assert(string.lower(header[1]) ~= 'content-length', "please don't give Content-Length") + assert(lower(header[1]) ~= 'content-length', "please don't give Content-Length") assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") insert(request, header[1]..': '..header[2]..CRLF) end From 006d1afdbe36bdb2996b1eeba099e4cc32f89602 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 2 Aug 2019 15:40:42 +0800 Subject: [PATCH 269/956] =?UTF-8?q?=E4=BC=98=E5=8C=96mail=E5=BA=93?= =?UTF-8?q?=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/mail/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lualib/mail/init.lua b/lualib/mail/init.lua index a274858d..bd18543a 100644 --- a/lualib/mail/init.lua +++ b/lualib/mail/init.lua @@ -1,6 +1,6 @@ local smtp = require "protocol.smtp" -local tonumber = tonumber +local toint = math.tointeger local match = string.match local os_date = os.date @@ -28,7 +28,7 @@ function mail.send(opt) if not check_mail(opt.from) or not check_mail(opt.to) then return nil, "发送者与接受者邮箱格式不正确" end - if not opt.host or not opt.port or opt.host == '' or (tonumber(opt.port) <= 0 or tonumber(opt.port) > 65535) then + if not opt.host or not opt.port or opt.host == '' or (not toint(opt.port)) or (toint(opt.port) <= 0 or toint(opt.port) > 65535) then return nil, "邮件server配置错误, 请检查配置参数." end if not opt.subject or opt.subject == '' then From 24be78837e4549c324eff9605911f2c84c6e73e0 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 2 Aug 2019 16:06:22 +0800 Subject: [PATCH 270/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0system=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/system/init.lua | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lualib/system/init.lua b/lualib/system/init.lua index fb9895a9..ff846674 100644 --- a/lualib/system/init.lua +++ b/lualib/system/init.lua @@ -1,17 +1,19 @@ local sys = require "sys" - -local string = string -local fmt = string.format - +local now = sys.now local is_ipv4 = sys.ipv4 local is_ipv6 = sys.ipv6 -local now = sys.now local type = type +local pairs = pairs +local ipairs = ipairs local os_date = os.date local os_time = os.time +local modf = math.modf + +local fmt = string.format + local System = { -- 类型转换函数 toint = math.modf, @@ -26,7 +28,7 @@ function System.is_int(number) if type(number) ~= 'number' then return false end - local int, float = math.modf(number) + local int, float = modf(number) return float == 0. end @@ -35,7 +37,7 @@ function System.is_float(number) if type(number) ~= 'number' then return false end - local int, float = math.modf(number) + local int, float = modf(number) return float ~= 0. end From fa7e0f3828fb04a728735b738e9e35745c58c55b Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 11 Aug 2019 02:43:43 +0800 Subject: [PATCH 271/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0log=E4=B8=8Epid?= =?UTF-8?q?=E5=BF=BD=E7=95=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 80ced870..c6257980 100644 --- a/.gitignore +++ b/.gitignore @@ -37,11 +37,15 @@ *.hex # Debug files -*.dSYM/ +*.dSYM *.su *.idb *.pdb +# PID And Log +*.log +*.PID + # Code Edit .vscode cfadmin From f3a59b9b2c5c7a61eab9fdaeae1608e941dcbcdf Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 11 Aug 2019 03:31:26 +0800 Subject: [PATCH 272/956] =?UTF-8?q?=E5=9C=A8=E6=A0=B9=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0pid=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_start.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/core_start.c b/src/core_start.c index d24249d5..ecaf6bde 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -1,16 +1,33 @@ #include "core.h" +void +write_pid(const char *filename) { + errno = 0; + FILE *f = fopen(filename, "w"); + if (!f) { + LOG("ERROR", strerror(errno)); + return exit(-1); + } + fprintf(f, "%d\n", getpid()); + fclose(f); +} + int main(int argc, char const *argv[]) { // #if !defined(__APPLE__) - /* 后台运行 */ - if (argc > 1 && 0 == strcmp("-d", argv[argc-1])) daemon(1, 0); + /* 后台运行 */ + if (argc > 1 && 0 == strcmp("-d", argv[argc-1])) daemon(1, 0); // #endif - /* 系统初始化 */ - core_sys_init(); - /* 运行事件循环 */ - core_sys_run(); - return 0; + /* 建立Pid文件 */ + write_pid("cfadmin.pid"); + + /* 系统初始化 */ + core_sys_init(); + + /* 运行事件循环 */ + core_sys_run(); + + return 0; } From edffde70ec8eb6617cc5b0425505ac917e90126f Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 11 Aug 2019 04:13:42 +0800 Subject: [PATCH 273/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E6=89=93=E5=8D=B0=E6=95=88=E7=8E=87,=20=E6=8F=90=E5=8D=87?= =?UTF-8?q?=E5=BC=82=E6=AD=A5=E6=97=A5=E5=BF=97=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/init.lua | 15 ++++++---- lualib/logging/init.lua | 61 +++++++++++++++++++++++++++-------------- 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua index f35e9f08..6b81de87 100644 --- a/lualib/httpd/init.lua +++ b/lualib/httpd/init.lua @@ -16,7 +16,6 @@ local match = string.match local io_write = io.write local toint = math.tointeger - -- 请求解析 local EVENT_DISPATCH = HTTP.EVENT_DISPATCH @@ -139,14 +138,18 @@ function httpd:log(path) end end -local log_fmt = "[%s] - %s - %s - %s - %s - %d - req_time: %0.6f/Sec\n" +-- CLOSE_LOG指定为true后将不会产生任何请求日志, 这样能提升更高的性能. +local CLOSE_LOG = false +-- LOG_FMT用于构建日志格式 +local LOG_FMT = "[%s] - %s - %s - %s - %s - %d - req_time: %0.6f/Sec\n" function httpd:tolog(code, path, ip, ip_list, method, speed) - if self.logging then - self.logging:dump(fmt(log_fmt, os_date("%Y/%m/%d %H:%M:%S"), ip, ip_list, path, method, code, speed)) + local now = os_date("%Y/%m/%d %H:%M:%S") + if self.logging and not CLOSE_LOG then + self.logging:dump(fmt(LOG_FMT, now, ip, ip_list, path, method, code, speed)) end - if self.output then - io_write(fmt(log_fmt, os_date("%Y/%m/%d %H:%M:%S"), ip, ip_list, path, method, code, speed)) + if self.output and not CLOSE_LOG then + io_write(fmt(LOG_FMT, now, ip, ip_list, path, method, code, speed)) end end diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua index f208eedc..b64221f9 100644 --- a/lualib/logging/init.lua +++ b/lualib/logging/init.lua @@ -1,4 +1,6 @@ -- logging 核心配置 +local cf = require "cf" + local class = require "class" local os_date = require("sys").date @@ -22,19 +24,22 @@ local io_type = io.type local fmt = string.format local concat = table.concat -local cf = require "cf" +-- 可以在这里手动设置是否使用异步日志 +local SYNC = false -if io_type(io.output()) == 'file' then - io.output():setvbuf("full", 1 << 20) - cf.at(0.3, function () - return io_flush() -- 定期刷新缓冲, 减少日志缓冲频繁导致的性能问题 - end) +if not SYNC then + if io_type(io.output()) == 'file' then + io.output():setvbuf("full", 2 ^ 20) + cf.at(0.5, function () + return io_flush() -- 定期刷新缓冲, 减少日志缓冲频繁导致的性能问题 + end) + end end -- 格式化时间: [年-月-日 时:分:秒,毫秒] local function fmt_Y_m_d_H_M_S() local ts, f = modf(now()) - return concat({'[', os_date('%Y-%m-%d %H:%M:%S'), ',', fmt("%003d", modf(f * 1e3)), ']'}) + return concat({'[', os_date('%Y-%m-%d %H:%M:%S', ts), ',', fmt("%003d", modf(f * 1e3)), ']'}) end -- 格式化时间: [年-月-日 时:分:秒] @@ -117,40 +122,56 @@ end -- 常规日志 function Log:INFO (...) - io_write(FMT("\27[32m"..debuginfo(), "[INFO]".."\27[0m", ...)) - self:dump(FMT(debuginfo(), "[INFO]", ...)) + local info = debuginfo() + io_write(FMT("\27[32m"..info, "[INFO]".."\27[0m", ...)) + if not self.dumped or type(self.path) ~= 'string' then + return + end + self:dump(FMT(info, "[INFO]", ...)) end -- 错误日志 function Log:ERROR (...) - io_write(FMT("\27[31m"..debuginfo(), "[ERROR]".."\27[0m", ...)) - self:dump(FMT(debuginfo(), "[ERROR]", ...)) + local info = debuginfo() + io_write(FMT("\27[31m"..info, "[ERROR]".."\27[0m", ...)) + if not self.dumped or type(self.path) ~= 'string' then + return + end + self:dump(FMT(info, "[ERROR]", ...)) end -- 调试日志 function Log:DEBUG (...) - io_write(FMT("\27[36m"..debuginfo(), "[DEBUG]".."\27[0m", ...)) - self:dump(FMT(debuginfo(), "[DEBUG]", ...)) + local info = debuginfo() + io_write(FMT("\27[36m"..info, "[DEBUG]".."\27[0m", ...)) + if not self.dumped or type(self.path) ~= 'string' then + return + end + self:dump(FMT(info, "[DEBUG]", ...)) end -- 警告日志 function Log:WARN (...) - io_write(FMT("\27[33m"..debuginfo(), "[WARN]".."\27[0m", ...)) - self:dump(FMT(debuginfo(), "[WARN]", ...)) + local info = debuginfo() + io_write(FMT("\27[33m"..info, "[WARN]".."\27[0m", ...)) + if not self.dumped or type(self.path) ~= 'string' then + return + end + self:dump(FMT(info, "[WARN]", ...)) end +-- 可以在这里手动设置日志路径 +local LOG_FOLDER = 'logs/' + -- dump日志到磁盘 function Log:dump(log) - if not self.dumped or type(self.path) ~= 'string' then - return - end local today = Y_m_d() if today ~= self.today then if self.file then self.file:close() self.file = nil end - local file, err = io_open('logs/'..self.path..'_'..today..'.log', 'a') + local file, err = io_open(LOG_FOLDER..self.path..'_'..today..'.log', 'a') if not file then return io_type(io.output()) == 'file' and io_write('打开文件失败.'..(err or '')..'\n') end @@ -158,7 +179,7 @@ function Log:dump(log) file:setvbuf("line") end if not self.file then - local file, err = io_open('logs/'..self.path..'_'..today..'.log', 'a') + local file, err = io_open(LOG_FOLDER..self.path..'_'..today..'.log', 'a') if not file then return io_type(io.output()) == 'file' and io_write('打开文件失败.'..(err or '')..'\n') end From 5e4cca7ae3f35a401bb08d1bfa6e33bdab2f1a2c Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 13 Aug 2019 10:55:59 +0800 Subject: [PATCH 274/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E6=96=B9=E5=BC=8F=E4=B8=8E=E4=BB=A3=E7=A0=81,=20=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E5=B0=86=E5=BA=93=E6=96=87=E4=BB=B6=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E5=88=B0=E7=B3=BB=E7=BB=9F=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 2 +- build.sh | 4 ++-- luaclib/Makefile | 22 +++++++++++----------- luaclib/src/lcjson/Makefile | 2 +- luaclib/src/lcrypt/Makefile | 3 +-- luaclib/src/lffi/makefile | 4 ++-- luaclib/src/lfs/Makefile | 4 ++-- luaclib/src/lhttpparser/Makefile | 2 +- luaclib/src/lmsgpack/Makefile | 2 +- luaclib/src/lpbc/Makefile | 2 +- luaclib/src/lpeg/makefile | 9 +++++---- src/Makefile | 6 +++--- src/core_sys.h | 6 +++--- 13 files changed, 34 insertions(+), 34 deletions(-) diff --git a/Makefile b/Makefile index 99e589f0..43869c46 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ rebuild : cd 3rd && make clean && make rebuild clean : - rm -rf main cfadmin *.so + rm -rf main cfadmin *.so lib* *.pid cd src && make clean cd luaclib && make clean cd 3rd && make clean diff --git a/build.sh b/build.sh index 980abe5c..8561b11b 100755 --- a/build.sh +++ b/build.sh @@ -10,10 +10,10 @@ git clone https://github.com/CandyMi/libev -b v4.25 cd ${current}/build/libev && sh autogen.sh && ./configure --prefix=/usr/local && - make && make install + make && cp ev*.h ${current}/src && cp .libs/libev* ${current}/ cd ${current}/build/lua && make all MYCFLAGS=-fPIC MYCFLAGS+=-DLUA_USE_POSIX MYCFLAGS+=-DLUA_USE_DLOPEN MYLIBS="-ldl -lreadline" && - cp lua.h luaconf.h lualib.h lauxlib.h /usr/local/include && cp liblua.* /usr/local/lib + cp lua.h luaconf.h lualib.h lauxlib.h ${current}/src && cp liblua.* ${current}/ cd ${current} && rm -rf build diff --git a/luaclib/Makefile b/luaclib/Makefile index 43cc5c0f..dece33d1 100644 --- a/luaclib/Makefile +++ b/luaclib/Makefile @@ -7,18 +7,18 @@ default : @echo "Please use 'make clean' command to clean all." @echo "=======================================" -INCLUDES += -I/usr/local/include +INCLUDES += -I../src -I/usr/local/include LIBS += -L./ -L../ -L/usr/local/lib # CFLAGS = -Wall -O3 -fPIC --shared -DJEMALLOC -ljemalloc # CFLAGS = -Wall -O3 -fPIC --shared -DTCMALLOC -ltcmalloc CFLAGS = -Wall -O3 -fPIC --shared build : - $(CC) -o sys.so src/lsys.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore - $(CC) -o udp.so src/ludp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore - $(CC) -o task.so src/ltask.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore - $(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore - $(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore + $(CC) -o sys.so src/lsys.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua + $(CC) -o udp.so src/ludp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua + $(CC) -o task.so src/ltask.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua + $(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua + $(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore -llua ### 以下为内置第三方库编译位置 ### cd src/lfs && rm -rf *.o *.so && make build # 增加lfs库 cd src/lffi && rm -rf *.o *.so && make build # 增加lffi库 @@ -30,11 +30,11 @@ build : cd src/lhttpparser && rm -rf *.o *.so && make build # 增加PicoHTTPParser库 rebuild : - $(CC) -o sys.so src/lsys.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore - $(CC) -o udp.so src/ludp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore - $(CC) -o task.so src/ltask.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore - $(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore - $(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore + $(CC) -o sys.so src/lsys.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua + $(CC) -o udp.so src/ludp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua + $(CC) -o task.so src/ltask.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua + $(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua + $(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore -llua ### 以下为内置第三方库编译位置 ### cd src/lfs && rm -rf *.o *.so && make build # 增加lfs库 cd src/lffi && rm -rf *.o *.so && make build # 增加lffi库 diff --git a/luaclib/src/lcjson/Makefile b/luaclib/src/lcjson/Makefile index 387cbbee..234cb404 100644 --- a/luaclib/src/lcjson/Makefile +++ b/luaclib/src/lcjson/Makefile @@ -14,7 +14,7 @@ CC = cc INCLUDE = -I/usr/local/include LIB = -L/usr/local/lib -L../ -L../../../ -DLL = -lcore +DLL = -lcore -llua build: $(CC) -o cjson.so lua_cjson.c fpconv.c strbuf.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) diff --git a/luaclib/src/lcrypt/Makefile b/luaclib/src/lcrypt/Makefile index de6c63f6..483519c9 100644 --- a/luaclib/src/lcrypt/Makefile +++ b/luaclib/src/lcrypt/Makefile @@ -10,8 +10,7 @@ default : CC = cc CFLAGS = -O3 -Wall -shared -fPIC -fno-strict-aliasing - -DLL = -lcore +DLL = -lcore -llua INCLUDE = -I/usr/local/include LIB = -L/usr/local/lib -L../ -L../../../ diff --git a/luaclib/src/lffi/makefile b/luaclib/src/lffi/makefile index e3352855..25cc0d2f 100644 --- a/luaclib/src/lffi/makefile +++ b/luaclib/src/lffi/makefile @@ -9,11 +9,11 @@ default : CC = cc -INCLUDE = -I/usr/local/include +INCLUDE = -I/usr/local/include -I../../../src LIB = -L/usr/local/lib -L../ -L../../../ CFLAGS = -O3 -Wall -shared -fPIC -Wno-unused-function -fno-strict-aliasing -Wno-uninitialized -Wno-ignored-attributes -DLL = -lcore +DLL = -lcore -llua build: $(CC) -o lffi.so ffi.c parser.c ctype.c $(CFLAGS) $(INCLUDE) $(LIB) $(DLL) diff --git a/luaclib/src/lfs/Makefile b/luaclib/src/lfs/Makefile index 03af3eae..f5cdb52f 100644 --- a/luaclib/src/lfs/Makefile +++ b/luaclib/src/lfs/Makefile @@ -9,11 +9,11 @@ default : CC = cc -INCLUDE = -I/usr/local/include +INCLUDE = -I/usr/local/include -I../../../src LIB = -L/usr/local/lib -L../ -L../../../ CFLAGS = -O3 -Wall -shared -fPIC -DLL = -lcore +DLL = -lcore -llua build: $(CC) -o lfs.so lfs.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) diff --git a/luaclib/src/lhttpparser/Makefile b/luaclib/src/lhttpparser/Makefile index 6483fc51..4501bd2b 100644 --- a/luaclib/src/lhttpparser/Makefile +++ b/luaclib/src/lhttpparser/Makefile @@ -16,7 +16,7 @@ INCLUDE = -I/usr/local/include LIB = -L/usr/local/lib -L../ -L../../../ CFLAGS = -O3 -Wall -shared -fPIC -DLL = -lcore +DLL = -lcore -llua build: diff --git a/luaclib/src/lmsgpack/Makefile b/luaclib/src/lmsgpack/Makefile index 85ad0eef..472158dd 100644 --- a/luaclib/src/lmsgpack/Makefile +++ b/luaclib/src/lmsgpack/Makefile @@ -13,7 +13,7 @@ INCLUDE = -I/usr/local/include LIB = -L/usr/local/lib -L../ -L../../../ CFLAGS = -O3 -Wall -shared -fPIC -DLL = -lcore +DLL = -lcore -llua build: $(CC) -o lmsgpack.so lmsgpack.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) diff --git a/luaclib/src/lpbc/Makefile b/luaclib/src/lpbc/Makefile index a9f352e8..2524ae52 100644 --- a/luaclib/src/lpbc/Makefile +++ b/luaclib/src/lpbc/Makefile @@ -13,7 +13,7 @@ INCLUDE = -I/usr/local/include LIB = -L/usr/local/lib -L../ -L../../../ CFLAGS = -O3 -Wall -shared -fPIC -DLL = -lcore +DLL = -lcore -llua build: $(CC) -o lprotobuf.so lpb.c $(INCLUDE) $(LIB) $(CFLAGS) $(DLL) diff --git a/luaclib/src/lpeg/makefile b/luaclib/src/lpeg/makefile index 39bfc285..940f09de 100644 --- a/luaclib/src/lpeg/makefile +++ b/luaclib/src/lpeg/makefile @@ -8,16 +8,17 @@ default : @echo "=======================================" CC = cc -LIBS = -L/usr/local/lib -L../ -L../../../ +INCLUDES += -I../../../src +LIBS += -L../ -L../../../ -L/usr/local/lib CFLAGS = -O3 -shared -fPIC -DLL = -lcore +DLL = -lcore -llua build: - $(CC) -o lpeg.so lpcap.c lpcode.c lpprint.c lptree.c lpvm.c $(CFLAGS) $(LIBS) $(DLL) + $(CC) -o lpeg.so lpcap.c lpcode.c lpprint.c lptree.c lpvm.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) mv *.so ../../ rebuild: - $(CC) -o lpeg.so lpcap.c lpcode.c lpprint.c lptree.c lpvm.c $(CFLAGS) $(LIBS) $(DLL) + $(CC) -o lpeg.so lpcap.c lpcode.c lpprint.c lptree.c lpvm.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) mv *.so ../../ clean: diff --git a/src/Makefile b/src/Makefile index b76205fb..ae6e6f3f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -9,8 +9,8 @@ default : CC = cc -LIBS += -L. -L/usr/local/lib -INCLUDES += -I/usr/local/include +LIBS += -L. -L.. -L/usr/local/lib +INCLUDES += -I. -I/usr/local/include # 使用jemalloc内存分配器请启用这段 # CFLAGS += -Wall -Os -fPIC --shared -DJEMALLOC -fno-strict-aliasing -Wl,-rpath,/usr/local/lib @@ -45,4 +45,4 @@ rebuild: clean : - rm -rf *.o *.so + rm -rf *.o *.so l*.h ev* diff --git a/src/core_sys.h b/src/core_sys.h index b063142a..6ade2183 100644 --- a/src/core_sys.h +++ b/src/core_sys.h @@ -22,9 +22,9 @@ #include #include -#include -#include -#include +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" #if LUA_VERSION_NUM >= 504 #ifndef CO_GCRESET From a739a9444cd0490f99c4cc96acf192e312888f4b Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 13 Aug 2019 11:03:54 +0800 Subject: [PATCH 275/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 2 +- src/Makefile | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.sh b/build.sh index 8561b11b..f8c3d377 100755 --- a/build.sh +++ b/build.sh @@ -10,7 +10,7 @@ git clone https://github.com/CandyMi/libev -b v4.25 cd ${current}/build/libev && sh autogen.sh && ./configure --prefix=/usr/local && - make && cp ev*.h ${current}/src && cp .libs/libev* ${current}/ + make && cp e*.h ${current}/src && cp .libs/libev* ${current}/ cd ${current}/build/lua && make all MYCFLAGS=-fPIC MYCFLAGS+=-DLUA_USE_POSIX MYCFLAGS+=-DLUA_USE_DLOPEN MYLIBS="-ldl -lreadline" && diff --git a/src/Makefile b/src/Makefile index ae6e6f3f..2b6fd445 100644 --- a/src/Makefile +++ b/src/Makefile @@ -9,8 +9,8 @@ default : CC = cc -LIBS += -L. -L.. -L/usr/local/lib -INCLUDES += -I. -I/usr/local/include +LIBS += -L. -L../ -L/usr/local/lib +INCLUDES += -I. -L../ -I/usr/local/include # 使用jemalloc内存分配器请启用这段 # CFLAGS += -Wall -Os -fPIC --shared -DJEMALLOC -fno-strict-aliasing -Wl,-rpath,/usr/local/lib @@ -23,7 +23,7 @@ INCLUDES += -I. -I/usr/local/include # MACRO += -w -O3 -Wl,-rpath,./ -DTCMALLOC # 默认情况下使用系统内存分配器 -CFLAGS += -Wall -O3 -fPIC --shared -fno-strict-aliasing -Wl,-rpath,/usr/local/lib +CFLAGS += -Wall -O3 -fPIC --shared -fno-strict-aliasing -Wl,-rpath,../ DLL += -lev -llua -ldl MACRO += -w -O3 -Wl,-rpath,./ From 24ee068cd51ee12ef70e3197fe8d372d6a6b7270 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 13 Aug 2019 11:06:32 +0800 Subject: [PATCH 276/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0git=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index c6257980..018e4295 100644 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,10 @@ # Code Edit .vscode +*.la +*.lai +ev*.h +src/l*.h cfadmin # Kernel Module Compile Results From e7b347d3b1b361d8d13e0d4f0f3e76768eb61c87 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 13 Aug 2019 11:10:53 +0800 Subject: [PATCH 277/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 4 ++-- clean.sh | 3 +++ src/Makefile | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) create mode 100755 clean.sh diff --git a/Makefile b/Makefile index 43869c46..ca8ea44d 100644 --- a/Makefile +++ b/Makefile @@ -18,13 +18,13 @@ build : cd 3rd && make build rebuild : - rm -rf main cfadmin *.so + rm -rf main cfadmin libcore.so *.pid cd src && make clean && make rebuild cd luaclib && make clean && make rebuild cd 3rd && make clean && make rebuild clean : - rm -rf main cfadmin *.so lib* *.pid + rm -rf main cfadmin libcore.so *.pid cd src && make clean cd luaclib && make clean cd 3rd && make clean diff --git a/clean.sh b/clean.sh new file mode 100755 index 00000000..acbf6645 --- /dev/null +++ b/clean.sh @@ -0,0 +1,3 @@ + +rm -rf libev* liblua* +rm -rf src/l* src/ev* diff --git a/src/Makefile b/src/Makefile index 2b6fd445..fab00989 100644 --- a/src/Makefile +++ b/src/Makefile @@ -45,4 +45,4 @@ rebuild: clean : - rm -rf *.o *.so l*.h ev* + rm -rf *.o *.so From 2217f08d22829f2cac718e317213b225c5577301 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 13 Aug 2019 11:19:07 +0800 Subject: [PATCH 278/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_ev.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core_ev.h b/src/core_ev.h index 6898a67b..1396e78d 100644 --- a/src/core_ev.h +++ b/src/core_ev.h @@ -42,7 +42,7 @@ #define EV_USE_KQUEUE 1 #endif -#include +#include "ev.h" #define CORE_LOOP core_default_loop() From e507ad359e8d5f89e86a570a909451e59eef6b94 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 13 Aug 2019 12:02:00 +0800 Subject: [PATCH 279/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E9=85=8D=E7=BD=AE,=20=E5=87=8F=E5=B0=91=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E5=A4=8D=E6=9D=82=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 1 - src/Makefile | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/build.sh b/build.sh index f8c3d377..78a033b0 100755 --- a/build.sh +++ b/build.sh @@ -1,4 +1,3 @@ -#!/bin/bash # 运行这个文件可以安装libev与lua current=`pwd` diff --git a/src/Makefile b/src/Makefile index fab00989..ad58d562 100644 --- a/src/Makefile +++ b/src/Makefile @@ -23,9 +23,9 @@ INCLUDES += -I. -L../ -I/usr/local/include # MACRO += -w -O3 -Wl,-rpath,./ -DTCMALLOC # 默认情况下使用系统内存分配器 -CFLAGS += -Wall -O3 -fPIC --shared -fno-strict-aliasing -Wl,-rpath,../ +CFLAGS += -Wall -O3 -fPIC --shared -fno-strict-aliasing -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib DLL += -lev -llua -ldl -MACRO += -w -O3 -Wl,-rpath,./ +MACRO += -w -O3 -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib build : From bf3488120e37372eaa31675eb84f33b2b31b1d18 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 13 Aug 2019 12:23:40 +0800 Subject: [PATCH 280/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E5=BA=93=E8=84=9A=E6=9C=AC=E5=B9=B6=E4=B8=94=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E7=BC=96=E8=AF=91=E6=AD=A5=E5=A5=8F=E6=B3=A8=E9=87=8A=E8=AF=B4?= =?UTF-8?q?=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 78a033b0..23c2b1f1 100755 --- a/build.sh +++ b/build.sh @@ -1,4 +1,16 @@ -# 运行这个文件可以安装libev与lua +# Run this file to install libev and lua; if you already have lua and libev in your environment, you can ignore this file and try to compile directly using makefile. +# 运行这个文件可以安装libev与lua; 如果您的环境中已经有了lua与libev后可以忽略此文件并且直接使用makefile尝试编译. + +# Note: This script requires a bash environment! You need to ensure that the bash environment has been installed, otherwise you may have unintended consequences. +# 注意: 此脚本需要bash环境! 您需要保证bash环境已经被安装, 否则可能会出现意想不到的后果. + +# This file must be executed in the current folder directory, otherwise the installation will be wrong. Beginners need to keep in mind. +# 必须在当前文件夹目录执行此文件, 否则安装将会出错. 初学者需要谨记. + +# Before executing this build file, you need to make sure that these software environments are installed: gcc/clang autoconf automake make libtool git readline-devel openssl-devel. +# 执行这个编译文件之前需要确保安装了这些软件环境: gcc/clang autoconf automake make libtool git readline-devel openssl-devel. 如果未安装或者缺少安装, 请仔细检查并且自行尝试安装依赖环境. + +bash current=`pwd` @@ -16,3 +28,5 @@ cd ${current}/build/lua && cp lua.h luaconf.h lualib.h lauxlib.h ${current}/src && cp liblua.* ${current}/ cd ${current} && rm -rf build + +exit From d211bbdaba458c3a47517e82e05373376fc7165a Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 13 Aug 2019 12:24:17 +0800 Subject: [PATCH 281/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=B8=85=E7=90=86?= =?UTF-8?q?=E7=BC=96=E8=AF=91=E5=BA=93=E8=84=9A=E6=9C=AC=E5=B9=B6=E4=B8=94?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B8=85=E7=90=86=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- clean.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clean.sh b/clean.sh index acbf6645..d40a9b58 100755 --- a/clean.sh +++ b/clean.sh @@ -1,3 +1,5 @@ +# This script is used to clean up build.sh to install the header files and library files brought by libev and lua. You can ignore this file when you already have lua and libev in your environment. +# 此脚本为清理build.sh安装libev与lua带来的头文件与库文件, 当您的环境中已经有了lua与libev后可以忽略此文件. rm -rf libev* liblua* rm -rf src/l* src/ev* From 6432dde74862ac25fef70b50a548af0aa0bd47b5 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 13 Aug 2019 12:38:48 +0800 Subject: [PATCH 282/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=9E=84=E5=BB=BA?= =?UTF-8?q?=E6=96=87=E4=BB=B6,=20=E5=85=BC=E5=AE=B9=E5=A4=9A=E7=A7=8Dshell?= =?UTF-8?q?=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/build.sh b/build.sh index 23c2b1f1..727a78dd 100755 --- a/build.sh +++ b/build.sh @@ -1,18 +1,13 @@ # Run this file to install libev and lua; if you already have lua and libev in your environment, you can ignore this file and try to compile directly using makefile. # 运行这个文件可以安装libev与lua; 如果您的环境中已经有了lua与libev后可以忽略此文件并且直接使用makefile尝试编译. -# Note: This script requires a bash environment! You need to ensure that the bash environment has been installed, otherwise you may have unintended consequences. -# 注意: 此脚本需要bash环境! 您需要保证bash环境已经被安装, 否则可能会出现意想不到的后果. - # This file must be executed in the current folder directory, otherwise the installation will be wrong. Beginners need to keep in mind. # 必须在当前文件夹目录执行此文件, 否则安装将会出错. 初学者需要谨记. # Before executing this build file, you need to make sure that these software environments are installed: gcc/clang autoconf automake make libtool git readline-devel openssl-devel. # 执行这个编译文件之前需要确保安装了这些软件环境: gcc/clang autoconf automake make libtool git readline-devel openssl-devel. 如果未安装或者缺少安装, 请仔细检查并且自行尝试安装依赖环境. -bash - -current=`pwd` +set current=`pwd` rm -rf build && mkdir build && cd build @@ -28,5 +23,3 @@ cd ${current}/build/lua && cp lua.h luaconf.h lualib.h lauxlib.h ${current}/src && cp liblua.* ${current}/ cd ${current} && rm -rf build - -exit From 12c712703aeb8c02f93307b6012d3c00e95819e1 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 13 Aug 2019 13:22:36 +0800 Subject: [PATCH 283/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=84=9A=E6=9C=AC?= =?UTF-8?q?=E7=8E=AF=E5=A2=83=E5=85=BC=E5=AE=B9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/build.sh b/build.sh index 727a78dd..025b505b 100755 --- a/build.sh +++ b/build.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + # Run this file to install libev and lua; if you already have lua and libev in your environment, you can ignore this file and try to compile directly using makefile. # 运行这个文件可以安装libev与lua; 如果您的环境中已经有了lua与libev后可以忽略此文件并且直接使用makefile尝试编译. @@ -7,19 +9,19 @@ # Before executing this build file, you need to make sure that these software environments are installed: gcc/clang autoconf automake make libtool git readline-devel openssl-devel. # 执行这个编译文件之前需要确保安装了这些软件环境: gcc/clang autoconf automake make libtool git readline-devel openssl-devel. 如果未安装或者缺少安装, 请仔细检查并且自行尝试安装依赖环境. -set current=`pwd` +current=`pwd` rm -rf build && mkdir build && cd build git clone https://github.com/CandyMi/lua -b v5.3.5 git clone https://github.com/CandyMi/libev -b v4.25 -cd ${current}/build/libev && - sh autogen.sh && ./configure --prefix=/usr/local && +echo "========== build libev ==========" && + cd ${current}/build/libev && sh autogen.sh && ./configure --prefix=/usr/local && make && cp e*.h ${current}/src && cp .libs/libev* ${current}/ -cd ${current}/build/lua && - make all MYCFLAGS=-fPIC MYCFLAGS+=-DLUA_USE_POSIX MYCFLAGS+=-DLUA_USE_DLOPEN MYLIBS="-ldl -lreadline" && +echo "========== build lua ==========" && + cd ${current}/build/lua && make all MYCFLAGS=-fPIC MYCFLAGS+=-DLUA_USE_POSIX MYCFLAGS+=-DLUA_USE_DLOPEN MYLIBS="-ldl -lreadline" && cp lua.h luaconf.h lualib.h lauxlib.h ${current}/src && cp liblua.* ${current}/ -cd ${current} && rm -rf build +echo "========== clean build ==========" && cd ${current} && rm -rf build From d719079a7754fc4e892dbb1003e4a8315184e2a2 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 13 Aug 2019 14:40:59 +0800 Subject: [PATCH 284/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=9E=84=E5=BB=BA?= =?UTF-8?q?=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 025b505b..a1788436 100755 --- a/build.sh +++ b/build.sh @@ -6,6 +6,9 @@ # This file must be executed in the current folder directory, otherwise the installation will be wrong. Beginners need to keep in mind. # 必须在当前文件夹目录执行此文件, 否则安装将会出错. 初学者需要谨记. +# Running this script in some embedded environments may lack the scripting tools that should be present (eg ls/printf/grep, etc.), you should find a way to install this command. +# 在一些嵌入式环境下运行此脚本可能会缺少本应存在的脚本工具(如: ls/printf/grep等等), 您应该想办法安装此命令. + # Before executing this build file, you need to make sure that these software environments are installed: gcc/clang autoconf automake make libtool git readline-devel openssl-devel. # 执行这个编译文件之前需要确保安装了这些软件环境: gcc/clang autoconf automake make libtool git readline-devel openssl-devel. 如果未安装或者缺少安装, 请仔细检查并且自行尝试安装依赖环境. @@ -18,7 +21,7 @@ git clone https://github.com/CandyMi/libev -b v4.25 echo "========== build libev ==========" && cd ${current}/build/libev && sh autogen.sh && ./configure --prefix=/usr/local && - make && cp e*.h ${current}/src && cp .libs/libev* ${current}/ + make && cp e*.h ${current}/src && cd .libs && cp $(printf "%s" "`ls | grep libev | grep -v la`") ${current}/ echo "========== build lua ==========" && cd ${current}/build/lua && make all MYCFLAGS=-fPIC MYCFLAGS+=-DLUA_USE_POSIX MYCFLAGS+=-DLUA_USE_DLOPEN MYLIBS="-ldl -lreadline" && From 6129fe949e704781a35b16f8a1a61d85c223ca41 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 13 Aug 2019 14:41:15 +0800 Subject: [PATCH 285/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index a16d7663..b551929c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Lua 轻量级网络开发框架 +## Lua 轻量级网络开发框架 (A lua Lightweight Network Development Framework)

                  @@ -8,25 +8,35 @@

                  -> **大道至简, 返璞归真. 简单? 不简单!** +> **Keep it simple and stupid ! Simple ? Not simple !** -## 优势 +> **大道至简, 返璞归真. 简单? 不简单!** + +### 优势 (Advantage) + + * **High efficiency** - Efficient static language and efficient virtual machine to achieve excellent runtime framework. * **高效率** —— 高效的静态语言与高效的虚拟机实现优秀的运行时框架. + * **Multi-ecology** —— Integrate most community third-party libraries and implement many protocols on their own + * **生态多** —— 集成社区库最多的框架之一, 并自行实现了一些网络协议生态. + * **High readability** —— Readability is always the standard for maintaining the framework, and painless getting started is worth promoting. + * **可读性高** —— 可读性始终是维护框架的标准, 无痛入门才是值得提倡的. + * **High readability** —— At present, it has been used within some enterprises, and users are gradually increasing. + * **稳定性好** —— 目前已有在一些企业内部已经开始使用, 使用者也正在逐渐增多. -## 文档 +### 文档 (Document) - * [WIKI](https://github.com/CandyMi/core_framework/wiki) + * [WIKI](https://github.com/CandyMi/core_framework/wiki)(Sorry! Only Chinese.) - * [在线文档](https://candymi.github.io/LuaWeb) + * [在线文档](https://candymi.github.io/LuaWeb)(Sorry! Only Chinese.) -## 预览图 +### 预览图 (preview)

                  @@ -44,15 +54,15 @@

                  - **[快速体验cfadmin后台](https://github.com/CandyMi/core_framework/wiki/cfadmin)** + **[快速体验cfadmin后台](https://github.com/CandyMi/core_framework/wiki/cfadmin)(Quick experience)** -## 反馈 +### 反馈 (feedback) * [issues](https://github.com/CandyMi/core_framework/issues) * **QQ群**:[727531854](https://shang.qq.com/wpa/qunwpa?idkey=5cc977ebaf4eb17391b2c6b03eb0ee36e3d3c1871bc95ba3c96ffc426a9dc907) -## 支持 +### 支持 (support) **如果您觉得cf还不错, 可以支持一杯咖啡予以鼓励.** @@ -61,6 +71,6 @@

                  -## 授权协议 +### 授权协议 (License agreement) [BSD LICENSE](https://github.com/CandyMi/core_framework/blob/master/LICENSE) From f6dfaafd98ddf9446e439feaf3d67b8abb3d8493 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 14 Aug 2019 19:57:15 +0800 Subject: [PATCH 286/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 47 ++++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index b551929c..d4c66867 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,33 @@ -## Lua 轻量级网络开发框架 (A lua Lightweight Network Development Framework) -

                  - - - - +# + +

                  + +

                  +
                  Lua 轻量级网络开发框架(A lua Lightweight Network Development Framework)
                  +

                  + + + +

                  -

                  +### 优势 (Advantage) -> **Keep it simple and stupid ! Simple ? Not simple !** + * **生态多** —— 集成社区库最多的框架之一, 并自行实现了一些网络协议生态. -> **大道至简, 返璞归真. 简单? 不简单!** + * **Multi-ecology** —— Integrate most community third-party libraries and implement many protocols on their own. -### 优势 (Advantage) + * **稳定性好** —— 目前已有在一些企业内部已经开始使用, 使用者也正在逐渐增多. - * **High efficiency** - Efficient static language and efficient virtual machine to achieve excellent runtime framework. + * **Good stability** —— At present, it has been used within some enterprises, and users are gradually increasing. * **高效率** —— 高效的静态语言与高效的虚拟机实现优秀的运行时框架. - * **Multi-ecology** —— Integrate most community third-party libraries and implement many protocols on their own - - * **生态多** —— 集成社区库最多的框架之一, 并自行实现了一些网络协议生态. - - * **High readability** —— Readability is always the standard for maintaining the framework, and painless getting started is worth promoting. + * **High efficiency** - Efficient static language and efficient virtual machine to achieve excellent runtime framework. * **可读性高** —— 可读性始终是维护框架的标准, 无痛入门才是值得提倡的. - * **High readability** —— At present, it has been used within some enterprises, and users are gradually increasing. - - * **稳定性好** —— 目前已有在一些企业内部已经开始使用, 使用者也正在逐渐增多. + * **High readability** —— Readability is always the standard for maintaining the framework, and painless getting started is worth promoting. ### 文档 (Document) @@ -64,11 +63,13 @@ ### 支持 (support) - **如果您觉得cf还不错, 可以支持一杯咖啡予以鼓励.** + * **如果您觉得cf还不错, 可以支持一杯咖啡予以鼓励.** + + * **Encourage the price of a cup of coffee.** -

                  - - +

                  + +

                  ### 授权协议 (License agreement) From e9f6352ef9576bd55e9a65aa86f193ffa77f1fda Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 14 Aug 2019 20:09:00 +0800 Subject: [PATCH 287/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d4c66867..8ad43efa 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# +

                  @@ -17,17 +17,17 @@ * **Multi-ecology** —— Integrate most community third-party libraries and implement many protocols on their own. - * **稳定性好** —— 目前已有在一些企业内部已经开始使用, 使用者也正在逐渐增多. + * **稳定性好** —— 许多领域的企业已经开始使用,并且用户数量也在逐渐增加. - * **Good stability** —— At present, it has been used within some enterprises, and users are gradually increasing. + * **Good stability** —— Enterprises in many fields have begun to use, and users are gradually increasing. * **高效率** —— 高效的静态语言与高效的虚拟机实现优秀的运行时框架. * **High efficiency** - Efficient static language and efficient virtual machine to achieve excellent runtime framework. - * **可读性高** —— 可读性始终是维护框架的标准, 无痛入门才是值得提倡的. + * **高可维护性** —— 通俗易懂的框架编写方式可以让开发者快速适应并且上手. - * **High readability** —— Readability is always the standard for maintaining the framework, and painless getting started is worth promoting. + * **High readability** —— The easy-to-understand framework writing method allows developers to quickly adapt and get started. ### 文档 (Document) @@ -63,7 +63,7 @@ ### 支持 (support) - * **如果您觉得cf还不错, 可以支持一杯咖啡予以鼓励.** + * **如果您使用的还算顺手, 可以支持一杯咖啡予以鼓励.** * **Encourage the price of a cup of coffee.** From 54b715f02546122134987efd022048f58e8c08b4 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 14 Aug 2019 20:11:21 +0800 Subject: [PATCH 288/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BF=BD=E7=95=A5?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 018e4295..64af1fd8 100644 --- a/.gitignore +++ b/.gitignore @@ -50,7 +50,7 @@ .vscode *.la *.lai -ev*.h +src/ev*.h src/l*.h cfadmin From 4a78381cca88107d3ed9f1d99f9616b6d7e2c500 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 19 Aug 2019 10:32:24 +0800 Subject: [PATCH 289/956] =?UTF-8?q?=E4=B8=BAadmin=E5=BA=93=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E6=9D=A1=E4=BB=B6=E6=9F=A5=E6=89=BE=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/db/user.lua | 44 +++++++++++++++++++ lualib/admin/html/system/user/user.html | 58 ++++++++++++++++--------- lualib/admin/http/system/user.lua | 13 +++++- lualib/admin/locales/EN-US.lua | 8 ++++ lualib/admin/locales/ZH-CN.lua | 8 ++++ 5 files changed, 110 insertions(+), 21 deletions(-) diff --git a/lualib/admin/db/user.lua b/lualib/admin/db/user.lua index f7d10a64..e8910f06 100644 --- a/lualib/admin/db/user.lua +++ b/lualib/admin/db/user.lua @@ -27,6 +27,50 @@ function user.user_list (db, opt) ]], limit * (page - 1), limit)) end +-- 模糊查找用户列表 +function user.find_by_username (db, opt) + local limit = toint(opt.limit) or 10 + local page = toint(opt.page) or 1 + local condition + if opt.condition == 'id' or opt.condition == 'email' or opt.condition == 'phone' then + condition = fmt("`cfadmin_users`.`%s` = '%s'", opt.condition, opt.value) + else + condition = fmt("`cfadmin_users`.`%s` LIKE '%%%s%%'", opt.condition, opt.value) + end + print(fmt([[ + SELECT + `cfadmin_users`.id, + `cfadmin_users`.name, + `cfadmin_users`.username, + `cfadmin_roles`.name AS role_name, + `cfadmin_users`.email, + `cfadmin_users`.phone, + `cfadmin_users`.create_at, + `cfadmin_users`.update_at, + `cfadmin_users`.active + FROM cfadmin_users, cfadmin_roles + WHERE + `cfadmin_roles`.id = `cfadmin_users`.role AND `cfadmin_users`.active = 1 AND %s + ORDER BY `cfadmin_users`.id LIMIT %s, %s + ]], condition, limit * (page - 1), limit)) + return db:query(fmt([[ + SELECT + `cfadmin_users`.id, + `cfadmin_users`.name, + `cfadmin_users`.username, + `cfadmin_roles`.name AS role_name, + `cfadmin_users`.email, + `cfadmin_users`.phone, + `cfadmin_users`.create_at, + `cfadmin_users`.update_at, + `cfadmin_users`.active + FROM cfadmin_users, cfadmin_roles + WHERE + `cfadmin_roles`.id = `cfadmin_users`.role AND `cfadmin_users`.active = 1 AND %s + ORDER BY `cfadmin_users`.id LIMIT %s, %s + ]], condition, limit * (page - 1), limit)) +end + -- 用户总数 function user.user_count (db) return db:query([[SELECT count(id) AS count FROM cfadmin_users WHERE active = '1']])[1]['count'] diff --git a/lualib/admin/html/system/user/user.html b/lualib/admin/html/system/user/user.html index aa501e6d..bfcd5eee 100644 --- a/lualib/admin/html/system/user/user.html +++ b/lualib/admin/html/system/user/user.html @@ -39,15 +39,31 @@ diff --git a/lualib/admin/http/system/user.lua b/lualib/admin/http/system/user.lua index b42547bf..ab0a01e9 100644 --- a/lualib/admin/http/system/user.lua +++ b/lualib/admin/http/system/user.lua @@ -131,8 +131,19 @@ function system.user_response (content) if not user_info or user_info.is_admin == 0 then return json_encode({code = 400, data = null, msg = '4. 用户权限不足'}) end - -- 添加用户 local action = args.action + -- 查找用户(模糊) + if action == 'findUser' then + if not args.value or not args.condition then + return json_encode({code = 400, data = null, msg = '1. 无效的参数'}) + end + local users = user.find_by_username(db, args) + if not users then + return json_encode({code = 400, data = null, msg = '2. 无效的参数'}) + end + return json_encode({code = 0, data = users }) + end + -- 添加用户 if action == 'add' then if not args.name or not args.username or not args.password then return json_encode({code = 400, data = null, msg = '1. 用户信息不完善'}) diff --git a/lualib/admin/locales/EN-US.lua b/lualib/admin/locales/EN-US.lua index 5d1e0072..e0b10d86 100644 --- a/lualib/admin/locales/EN-US.lua +++ b/lualib/admin/locales/EN-US.lua @@ -67,6 +67,14 @@ return { ['dashboard.menu.user_manage.table.options.delete.confirm'] = 'confirm Delete it?', ['dashboard.menu.user_manage.table.options.delete.confirm.success'] = 'Delet Success.', + ['dashboard.menu.user_manage.table.search'] = "Search", + ['dashboard.menu.user_manage.table.search.condition'] = "Condition", + ['dashboard.menu.user_manage.table.search.condition.userid'] = "User ID", + ['dashboard.menu.user_manage.table.search.condition.username'] = "User Name", + ['dashboard.menu.user_manage.table.search.condition.useraccount'] = "User Account", + ['dashboard.menu.user_manage.table.search.condition.email'] = "User Email", + ['dashboard.menu.user_manage.table.search.condition.phone'] = "User Phone", + -- 用户新增页面 ['dashboard.menu.user_manage.form.user_add.name'] = 'Name', ['dashboard.menu.user_manage.form.user_add.name.notice'] = 'User Name', diff --git a/lualib/admin/locales/ZH-CN.lua b/lualib/admin/locales/ZH-CN.lua index 4e34c427..a15b80b6 100644 --- a/lualib/admin/locales/ZH-CN.lua +++ b/lualib/admin/locales/ZH-CN.lua @@ -69,6 +69,14 @@ return { ['dashboard.menu.user_manage.table.options.delete.confirm'] = '确认要删除么?', ['dashboard.menu.user_manage.table.options.delete.confirm.success'] = '删除成功', + ['dashboard.menu.user_manage.table.search'] = "搜索", + ['dashboard.menu.user_manage.table.search.condition'] = "搜索条件", + ['dashboard.menu.user_manage.table.search.condition.userid'] = "用户ID", + ['dashboard.menu.user_manage.table.search.condition.username'] = "用户名称", + ['dashboard.menu.user_manage.table.search.condition.useraccount'] = "用户账户", + ['dashboard.menu.user_manage.table.search.condition.email'] = "用户邮箱", + ['dashboard.menu.user_manage.table.search.condition.phone'] = "用户手机", + -- 用户新增页面 ['dashboard.menu.user_manage.form.user_add.name'] = '用户名称', ['dashboard.menu.user_manage.form.user_add.name.notice'] = '用户显示名称', From 27b09a1e8eb0eec7b69e4736cb7811653df91512 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 19 Aug 2019 20:21:10 +0800 Subject: [PATCH 290/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0admin=E5=BA=93?= =?UTF-8?q?=E9=83=BD=E4=B8=80=E4=BA=9B=E9=A1=B5=E9=9D=A2=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=E4=B8=8E=E6=8E=92=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/html/system/menu/menu.html | 140 ++++++++++++------------ lualib/admin/html/system/user/user.html | 4 +- 2 files changed, 72 insertions(+), 72 deletions(-) diff --git a/lualib/admin/html/system/menu/menu.html b/lualib/admin/html/system/menu/menu.html index f9e5e72d..767a7060 100644 --- a/lualib/admin/html/system/menu/menu.html +++ b/lualib/admin/html/system/menu/menu.html @@ -20,85 +20,85 @@ -

                  -
                  - - -
                  -
                  -
                • #
                  - - - - - - - - - - {% if menus and type(menus) == 'table' then%} - {% for index, menu in ipairs(menus) do %} - - - - - - +
                  +
                  + + +
                  +
                  +
                  {*locale['dashboard.menu.menu_manage.table.id']*}{*locale['dashboard.menu.menu_manage.table.icon']*}{*locale['dashboard.menu.menu_manage.table.name']*}{*locale['dashboard.menu.menu_manage.table.url']*}{*locale['dashboard.menu.menu_manage.table.options']*}
                  {*menu.id*}{*menu.icon*} - {% if type(menu.list) == 'table' then %} - - {% end %}{{locale[menu.name] or menu.name}} - {{ menu.url or '-' }} - - - -
                  + + + + + + + + + + {% if menus and type(menus) == 'table' then%} + {% for index, menu in ipairs(menus) do %} + + + + + + + + {% if type(menu.list) == 'table' then%} + {% for index, sub in ipairs(menu.list) do%} + + + + + + - {% if type(menu.list) == 'table' then%} - {% for index, sub in ipairs(menu.list) do%} - - - + {% if type(sub.list) == 'table' then %} + {% for index, it in ipairs(sub.list) do %} + + + - + - {% if type(sub.list) == 'table' then %} - {% for index, it in ipairs(sub.list) do %} - - - - - - - - {% end %} - {% end%} {% end %} - {% end %} - {% end %} + {% end%} {% end %} - -
                  {*locale['dashboard.menu.menu_manage.table.id']*}{*locale['dashboard.menu.menu_manage.table.icon']*}{*locale['dashboard.menu.menu_manage.table.name']*}{*locale['dashboard.menu.menu_manage.table.url']*}{*locale['dashboard.menu.menu_manage.table.options']*}
                  {*menu.id*}{*menu.icon*} + {% if type(menu.list) == 'table' then %} + + {% end %}{{locale[menu.name] or menu.name}} + {{ menu.url or '-' }} + + + +
                  {*sub.id*}{*sub.icon*} + |-------> + {% if type(sub.list) == 'table' then %} + + {% end %} + {{locale[sub.name] or sub.name}} + {{ sub.url or '-' }} + + + +
                  {*sub.id*}{*sub.icon*}
                  {*it.id*}{*it.icon*} - |-------> - {% if type(sub.list) == 'table' then %} - - {% end %} - {{locale[sub.name] or sub.name}} +         +         + |-------> {{locale[it.name] or it.name}} {{ sub.url or '-' }}{*it.url*} - - - + +
                  {*it.id*}{*it.icon*} -         -         - |-------> {{locale[it.name] or it.name}} - {*it.url*} - - -
                  - - + {% end %} + {% end %} + {% end %} + + + + - + From f48be492a2c8e9d6f13b33c32f80006f86d91cd1 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 20 Aug 2019 09:04:19 +0800 Subject: [PATCH 296/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0admin=E5=BA=93?= =?UTF-8?q?=E9=83=BD=E5=B8=83=E5=B1=80=E4=B8=BA=E8=87=AA=E9=80=82=E5=BA=94?= =?UTF-8?q?=E5=AE=BD=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/html/system/header/header.html | 56 ++++++------- lualib/admin/html/system/role/role.html | 55 ++++++------- lualib/admin/html/system/user/user.html | 88 +++++++++++---------- 3 files changed, 102 insertions(+), 97 deletions(-) diff --git a/lualib/admin/html/system/header/header.html b/lualib/admin/html/system/header/header.html index ecac20cf..9eb81373 100644 --- a/lualib/admin/html/system/header/header.html +++ b/lualib/admin/html/system/header/header.html @@ -16,33 +16,35 @@ - - - - - - - - - - - - -
                  #{* locale['dashboard.menu.header_manage.table.name'] *}{* locale['dashboard.menu.header_manage.table.url'] *}{* locale['dashboard.menu.header_manage.table.create_time'] *}{* locale['dashboard.menu.header_manage.table.update_time'] *}{* locale['dashboard.menu.header_manage.table.options'] *}
                  - - + +
                  + + + + + + + + + + + +
                  #{* locale['dashboard.menu.header_manage.table.name'] *}{* locale['dashboard.menu.header_manage.table.url'] *}{* locale['dashboard.menu.header_manage.table.create_time'] *}{* locale['dashboard.menu.header_manage.table.update_time'] *}{* locale['dashboard.menu.header_manage.table.options'] *}
                  + + +
                  - - - - - - - - - - - -
                  #{* locale['dashboard.menu.role_manage.table.name'] *}{* locale['dashboard.menu.role_manage.table.create_time'] *}{* locale['dashboard.menu.role_manage.table.update_time'] *}{* locale['dashboard.menu.role_manage.table.options'] *}
                  - - + +
                  + + + + + + + + + + +
                  #{* locale['dashboard.menu.role_manage.table.name'] *}{* locale['dashboard.menu.role_manage.table.create_time'] *}{* locale['dashboard.menu.role_manage.table.update_time'] *}{* locale['dashboard.menu.role_manage.table.options'] *}
                  + + +
                  - +
                  + + + + + + + + + + + + + + +
                  #{* locale['dashboard.menu.user_manage.table.name'] *}{* locale['dashboard.menu.user_manage.table.username'] *}{* locale['dashboard.menu.user_manage.table.permission_name'] *}{* locale['dashboard.menu.user_manage.table.email'] *}{* locale['dashboard.menu.user_manage.table.phone'] *}{* locale['dashboard.menu.user_manage.table.create_time'] *}{* locale['dashboard.menu.user_manage.table.update_time'] *}{* locale['dashboard.menu.user_manage.table.options'] *}
                  + + +
                  diff --git a/lualib/admin/html/profile/base.html b/lualib/admin/html/profile/base.html index 2b795352..1b431248 100644 --- a/lualib/admin/html/profile/base.html +++ b/lualib/admin/html/profile/base.html @@ -1,176 +1,174 @@ - - - - profile - - - - - - - - - - - -
                  -
                  -
                  {*locale['dashboard.header.profile.update_userinfo']*}
                  -
                  - -
                  - -
                  - -
                  -
                  - *{*locale['dashboard.header.profile.form.name.notice']*} -
                  + + + profile + + + + + + + + + + + +
                  +
                  +
                  {*locale['dashboard.header.profile.update_userinfo']*}
                  +
                  + +
                  + +
                  +
                  -
                  - -
                  - -
                  -
                  - *{*locale['dashboard.header.profile.form.username.notice']*} -
                  +
                  + *{*locale['dashboard.header.profile.form.name.notice']*}
                  -
                  - -
                  - -
                  +
                  +
                  + +
                  +
                  -
                  - -
                  - -
                  +
                  + *{*locale['dashboard.header.profile.form.username.notice']*}
                  -
                  -
                  - -
                  +
                  +
                  + +
                  +
                  -
                  -
                  - -
                  +
                  +
                  + +
                  +
                  -
                  - - +
                  +
                  +
                  +
                  - -
                  -
                  -
                  {*locale['dashboard.header.profile.update_password']*}
                  -
                  -
                  -
                  - -
                  - -
                  -
                  -
                  - -
                  - -
                  -
                  -
                  -
                  - -
                  -
                  -
                  -
                  - -
                  -
                  -
                  - - -
                  -
                  +
                  +
                  + +
                  +
                  +
                  + + +
                  +
                  - - - + }); + + From 9bab1eb7444822f1d11e218219594774761dd4c1 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 25 Aug 2019 23:48:35 +0800 Subject: [PATCH 310/956] =?UTF-8?q?=E6=96=B0=E5=A2=9ETCP.sendfile=E6=96=B9?= =?UTF-8?q?=E6=B3=95,=20=E7=94=A8=E6=9D=A5=E8=A7=A3=E5=86=B3httpd.static?= =?UTF-8?q?=E8=AF=BB=E5=8F=96=E5=A4=A7=E6=96=87=E4=BB=B6=E5=86=85=E5=AD=98?= =?UTF-8?q?=E5=8D=A0=E7=94=A8=E7=9A=84=E9=97=AE=E9=A2=98.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 122 ++++++++++++++++++++++++++++++--------- lualib/httpd/Router.lua | 11 ++-- lualib/internal/TCP.lua | 33 +++++++++-- lualib/protocol/http.lua | 65 ++++++++++----------- 4 files changed, 158 insertions(+), 73 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index d1a00e79..44855e9a 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -245,6 +245,71 @@ IO_ACCEPT(CORE_P_ core_io *io, int revents){ } } +struct io_sendfile { + uint32_t offset; + uint32_t fd; + lua_State *L; +}; + +static void +IO_SENDFILE(CORE_P_ core_io *io, int revents){ + if (revents & EV_WRITE){ + errno = 0; + struct io_sendfile *sf = core_get_watcher_userdata(io); + char buf[sf->offset]; + for (;;) { + memset(buf, 0x0, sf->offset); + int rBytes = read(sf->fd, buf, sf->offset); + // printf("rBytes = %d, offset = %d\n", rBytes, sf->offset); + if (0 == rBytes) { lua_pushboolean(sf->L, 1); break; } // 所有数据写入发送完毕. + int wBytes = write(io->fd, buf, rBytes); + if (wBytes <= 0) { + /* 如果写入失败后需要重试, 则需要先恢复到上次写入位置*/ + lseek(sf->fd, lseek(sf->fd, 0, SEEK_CUR) - rBytes, SEEK_SET); + if (errno == EINTR) continue ; + if (errno == EWOULDBLOCK) return; + LOG("DEBUG", strerror(errno)); + lua_pushboolean(sf->L, 0); + break; + } + // LOG("DEBUG", "写入成功."); + // 如果文件发送字符数量小于读取数量, 就需要重新设置读写位置。 + if (rBytes > wBytes) lseek(sf->fd, lseek(sf->fd, 0, SEEK_CUR) - (rBytes - wBytes), SEEK_SET); + } + // LOG("DEBUG", "写入完毕."); + core_set_watcher_userdata(io, NULL); + int status = CO_RESUME(sf->L, NULL, lua_status(sf->L) == LUA_YIELD ? lua_gettop(sf->L) : lua_gettop(sf->L) - 1); + if (status != LUA_YIELD && status != LUA_OK) { + LOG("ERROR", lua_tostring(sf->L, -1)); + LOG("ERROR", "Error Lua SENDFILE Method"); + } + close(sf->fd); xfree(sf); + } +} + +static int +tcp_sendfile(lua_State *L){ + // LOG("DEBUG", "开始注册."); + core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); + lua_State *t = lua_tothread(L, 2); + const char* path = luaL_checkstring(L, 3); + lua_Integer iofd = luaL_checkinteger(L, 4); + lua_Integer offset = luaL_checkinteger(L, 5); + + int fd = open(path, O_RDONLY); + if (0 > fd) return luaL_error(L, strerror(errno)); + + struct io_sendfile *sf = xmalloc(sizeof(struct io_sendfile)); + sf->fd = fd; + sf->offset = offset; + sf->L = t; + core_set_watcher_userdata(io, sf); + core_io_init(io, IO_SENDFILE, iofd, EV_WRITE); + core_io_start(CORE_LOOP_ io); + // LOG("DEBUG", "注册完成."); + return 1; +} + static int tcp_read(lua_State *L){ @@ -567,40 +632,41 @@ tcp_close(lua_State *L){ LUAMOD_API int luaopen_tcp(lua_State *L){ - luaL_checkversion(L); - /* 添加SSL支持 */ + luaL_checkversion(L); + /* 添加SSL支持 */ SSL_library_init(); SSL_load_error_strings(); // ERR_load_crypto_strings(); // CRYPTO_set_mem_functions(xmalloc, xrealloc, xfree); // OpenSSL_add_ssl_algorithms(); - /* 添加SSL支持 */ - luaL_newmetatable(L, "__TCP__"); - lua_pushstring (L, "__index"); - lua_pushvalue(L, -2); - lua_rawset(L, -3); + /* 添加SSL支持 */ + luaL_newmetatable(L, "__TCP__"); + lua_pushstring (L, "__index"); + lua_pushvalue(L, -2); + lua_rawset(L, -3); lua_pushliteral(L, "__mode"); lua_pushliteral(L, "kv"); lua_rawset(L, -3); - luaL_Reg tcp_libs[] = { - {"read", tcp_read}, - {"write", tcp_write}, - {"ssl_read", tcp_sslread}, - {"ssl_write", tcp_sslwrite}, - {"stop", tcp_stop}, - {"start", tcp_start}, - {"close", tcp_close}, - {"listen", tcp_listen}, - {"connect", tcp_connect}, - {"ssl_connect", tcp_sslconnect}, - {"new", tcp_new}, - {"new_ssl", ssl_new}, - {"free_ssl", ssl_free}, - {"new_server_fd", new_server_fd}, - {"new_client_fd", new_client_fd}, - {NULL, NULL} - }; - luaL_setfuncs(L, tcp_libs, 0); - luaL_newlib(L, tcp_libs); - return 1; + luaL_Reg tcp_libs[] = { + {"read", tcp_read}, + {"write", tcp_write}, + {"ssl_read", tcp_sslread}, + {"ssl_write", tcp_sslwrite}, + {"stop", tcp_stop}, + {"start", tcp_start}, + {"close", tcp_close}, + {"listen", tcp_listen}, + {"connect", tcp_connect}, + {"ssl_connect", tcp_sslconnect}, + {"new", tcp_new}, + {"new_ssl", ssl_new}, + {"free_ssl", ssl_free}, + {"new_server_fd", new_server_fd}, + {"new_client_fd", new_client_fd}, + {"sendfile", tcp_sendfile}, + {NULL, NULL} + }; + luaL_setfuncs(L, tcp_libs, 0); + luaL_newlib(L, tcp_libs); + return 1; } diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index 98e87ae8..e0f69d95 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -105,8 +105,8 @@ local function find_route (method, path) if not prefix and not type then return end - -- 非GET方法不查找静态文件 - if method ~= 'GET' then + -- 非GET/HEAD方法不查找静态文件 + if method ~= 'GET' and method ~= 'HEAD' then return end -- 凡是找到'../'并且检查路径回退已经超出静态文件根目录返回404 @@ -114,13 +114,14 @@ local function find_route (method, path) return end load_file = load_file or function (path) - local f, error = io_open(prefix..path, 'rb') + local filepath = prefix..path + local f, error = io_open(filepath, 'rb') if not f then return end - local file = f:read('*a') + local body_len = f:seek("end") f:close() - return file, match(path, '.+%.([%a]+)') + return body_len, filepath, match(path, '.+%.([%a]+)') end return load_file, type end diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index eff909bb..180c6be6 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -33,6 +33,7 @@ local tcp_sslread = tcp.ssl_read local tcp_write = tcp.write local tcp_ssl_write = tcp.ssl_write local tcp_listen = tcp.listen +local tcp_sendfile = tcp.sendfile local tcp_new_client_fd = tcp.new_client_fd local tcp_new_server_fd = tcp.new_server_fd @@ -43,10 +44,10 @@ local EVENT_WRITE = 0x02 local POOL = {} local function tcp_pop() - if #POOL > 0 then - return remove(POOL) - end - return tcp_new() + if #POOL > 0 then + return remove(POOL) + end + return tcp_new() end local function tcp_push(tcp) @@ -83,6 +84,24 @@ function TCP:set_backlog(backlog) return self end +function TCP:sendfile (filename, offset) + if type(filename) == 'string' and filename ~= '' then + local co = co_self() + self.SEND_IO = tcp_pop() + self.sendfile_current_co = co_self() + self.sendfile_co = co_new(function (ok) + tcp_stop(self.SEND_IO) + tcp_push(self.SEND_IO) + self.SEND_IO = nil + self.sendfile_co = nil + self.sendfile_current_co = nil + return co_wakeup(co, ok) + end) + tcp_sendfile(self.SEND_IO, self.sendfile_co, filename, self.fd, offset or 4096) + return co_wait() + end +end + function TCP:send(buf) if self.ssl then Log:ERROR("Please use ssl_send method :)") @@ -378,6 +397,7 @@ function TCP:close() tcp_push(self.SEND_IO) self.SEND_IO = nil self.send_co = nil + self.sendfile_co = nil end if self.CONNECT_IO then @@ -402,6 +422,11 @@ function TCP:close() self.read_current_co = nil end + if self.sendfile_current_co then + co_wakeup(self.sendfile_current_co) + self.sendfile_current_co = nil + end + if self._timeout then self._timeout = nil end diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index af980c3a..fd4e6b69 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -377,12 +377,16 @@ local function Switch_Protocol(http, cls, sock, header, method, version, path, i return wsserver:new({cls = cls, sock = sock}):start() end -local response = {nil, CRLF2, nil} -local function send_response (sock, headers, body) - response[1], response[3] = concat(headers, CRLF), body - local ok = sock:send(concat(response)) - response[1], response[3] = nil, nil - return ok +local function send_header (sock, header) + header[#header+1] = CRLF + return sock:send(concat(header, CRLF)) +end + +local function send_body (sock, body, filepath) + if not body then + return sock:sendfile(filepath, 128) + end + return sock:send(body) end function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) @@ -515,7 +519,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) end local header = { } - local ok, body, static, statucode + local ok, body, body_len, filepath, static, statucode if typ == HTTP_PROTOCOL.API or typ == HTTP_PROTOCOL.USE then -- 如果httpd开启了记录Cookie字段, 则每次尝试是否deCookie @@ -562,14 +566,9 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) if pos then path = split(PATH, 1, pos - 1) end - ok, body, file_type = pcall(cls, path) - if not ok then + body_len, filepath, file_type = cls(path) + if not body_len then Log:ERROR(body) - statucode = 500 - sock:send(ERROR_RESPONSE(http, statucode, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) - return sock:close() - end - if not body then statucode = 404 sock:send(ERROR_RESPONSE(http, statucode, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) return sock:close() @@ -595,41 +594,35 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) Connection = 'Connection: close' end header[#header+1] = Connection - if body then - if type(body) == 'string' then - header[#header+1] = 'Content-Length: '.. #body - else - Log:WARN('response body not a string type.'..'('..tostring(body)..')') - header[#header+1] = 'Content-Length: 0' - body = '' - end - else - header[#header+1] = 'Content-Length: 0' - body = '' + if Connection == 'Connection: keep-alive' then + header[#header+1] = "Keep-Alive: timeout="..timeout end - if typ == HTTP_PROTOCOL.API then - if #body > 0 then + if typ == HTTP_PROTOCOL.API or typ == HTTP_PROTOCOL.USE then + if typ == HTTP_PROTOCOL.API then header[#header+1] = 'Content-Type: '..REQUEST_MIME_RESPONSE('json') - end - header[#header+1] = 'Cache-Control: no-cache, no-store, must-revalidate' - header[#header+1] = 'Cache-Control: no-cache' - elseif typ == HTTP_PROTOCOL.USE then - if #body > 0 then + else header[#header+1] = concat({'Content-Type: ', REQUEST_MIME_RESPONSE('html'), ';charset=utf-8'}) end + if type(body) == 'string' and #body >= 0 then + header[#header+1] = 'Content-Length: '.. #body + else + Log:WARN("response body can't be a string type.") + return sock:close() + end header[#header+1] = 'Cache-Control: no-cache, no-store, must-revalidate' header[#header+1] = 'Cache-Control: no-cache' else if ttl then header[#header+1] = HTTP_EXPIRES(time() + ttl) end + if body_len then + header[#header+1] = 'Content-Length: '.. body_len + end header[#header+1] = static end - if Connection == 'Connection: keep-alive' then - header[#header+1] = "Keep-Alive: timeout="..timeout - end + -- 根据实际情况分块发送 + local ok = send_header(sock, header) and send_body(sock, body, filepath) or false http:tolog(statucode, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, now() - start) - local ok = send_response(sock, header, body) if not ok then return sock:close() end From 677efea599d029d22d7d197b004d85276ac50e97 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 26 Aug 2019 01:58:53 +0800 Subject: [PATCH 311/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0Apple/BSD=E7=9A=84sen?= =?UTF-8?q?dfile=E8=B0=83=E7=94=A8=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 44855e9a..9f7d04f2 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -248,6 +248,7 @@ IO_ACCEPT(CORE_P_ core_io *io, int revents){ struct io_sendfile { uint32_t offset; uint32_t fd; + uint64_t pos; lua_State *L; }; @@ -256,11 +257,48 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ if (revents & EV_WRITE){ errno = 0; struct io_sendfile *sf = core_get_watcher_userdata(io); + +#ifdef EV_USE_KQUEUE + int tag = 0; off_t nBytes = 0; + for (;;) { +#if defined(__APPLE__) + tag = sendfile(sf->fd, io->fd, sf->pos, &nBytes, NULL, 0); +#else + tag = sendfile(sf->fd, io->fd, sf->pos, &nBytes, NULL, NULL, 0); +#endif + sf->pos += nBytes; + if (0 > tag) { + if (errno == EINTR) continue; + if (errno == EWOULDBLOCK) return; + lua_pushboolean(sf->L, 0); + break; + } + lua_pushboolean(sf->L, 1); + break; + } +#endif + +#ifdef EV_USE_EPOLL + int tag = 0; off_t nBytes = 0; + for (;;) { + wBytes = sendfile(io->fd, sf->fd, sf->pos, sf->offset); + if (0 > tag) { + if (errno == EINTR) continue; + if (errno == EWOULDBLOCK) return; + lua_pushboolean(sf->L, 0); + break; + } + sf->pos += wBytes; + lua_pushboolean(sf->L, 1); + break; + } +#endif + +#ifdef EV_USE_SELECT char buf[sf->offset]; for (;;) { memset(buf, 0x0, sf->offset); int rBytes = read(sf->fd, buf, sf->offset); - // printf("rBytes = %d, offset = %d\n", rBytes, sf->offset); if (0 == rBytes) { lua_pushboolean(sf->L, 1); break; } // 所有数据写入发送完毕. int wBytes = write(io->fd, buf, rBytes); if (wBytes <= 0) { @@ -272,11 +310,10 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ lua_pushboolean(sf->L, 0); break; } - // LOG("DEBUG", "写入成功."); // 如果文件发送字符数量小于读取数量, 就需要重新设置读写位置。 if (rBytes > wBytes) lseek(sf->fd, lseek(sf->fd, 0, SEEK_CUR) - (rBytes - wBytes), SEEK_SET); } - // LOG("DEBUG", "写入完毕."); +#endif core_set_watcher_userdata(io, NULL); int status = CO_RESUME(sf->L, NULL, lua_status(sf->L) == LUA_YIELD ? lua_gettop(sf->L) : lua_gettop(sf->L) - 1); if (status != LUA_YIELD && status != LUA_OK) { @@ -284,7 +321,7 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ LOG("ERROR", "Error Lua SENDFILE Method"); } close(sf->fd); xfree(sf); - } + } } static int @@ -300,6 +337,7 @@ tcp_sendfile(lua_State *L){ if (0 > fd) return luaL_error(L, strerror(errno)); struct io_sendfile *sf = xmalloc(sizeof(struct io_sendfile)); + sf->pos = 0; sf->fd = fd; sf->offset = offset; sf->L = t; From 06b52c2de4f7823263513562e3297db73dd086cf Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 26 Aug 2019 03:48:29 +0800 Subject: [PATCH 312/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0sendfile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 9f7d04f2..720cfe34 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -264,34 +264,38 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ #if defined(__APPLE__) tag = sendfile(sf->fd, io->fd, sf->pos, &nBytes, NULL, 0); #else - tag = sendfile(sf->fd, io->fd, sf->pos, &nBytes, NULL, NULL, 0); + tag = sendfile(sf->fd, io->fd, sf->pos, 0, NULL, &nBytes, SF_NODISKIO | SF_NOCACHE); #endif sf->pos += nBytes; + // printf("tag = %d, pos = %lu, nBytes = %ld, errno = %d, err = %s\n", tag, sf->pos, nBytes, errno, strerror(errno)); if (0 > tag) { if (errno == EINTR) continue; if (errno == EWOULDBLOCK) return; lua_pushboolean(sf->L, 0); break; } - lua_pushboolean(sf->L, 1); - break; + if ( !nBytes ){ + lua_pushboolean(sf->L, 1); + break; + } } #endif #ifdef EV_USE_EPOLL - int tag = 0; off_t nBytes = 0; + int tag = 0; for (;;) { - wBytes = sendfile(io->fd, sf->fd, sf->pos, sf->offset); + tag = sendfile(io->fd, sf->fd, &sf->pos, sf->offset); + sf->pos += tag; if (0 > tag) { if (errno == EINTR) continue; if (errno == EWOULDBLOCK) return; lua_pushboolean(sf->L, 0); break; } - sf->pos += wBytes; - lua_pushboolean(sf->L, 1); - break; - } + if ( !tag ){ + lua_pushboolean(sf->L, 1); + break; + } #endif #ifdef EV_USE_SELECT @@ -306,7 +310,7 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ lseek(sf->fd, lseek(sf->fd, 0, SEEK_CUR) - rBytes, SEEK_SET); if (errno == EINTR) continue ; if (errno == EWOULDBLOCK) return; - LOG("DEBUG", strerror(errno)); + // LOG("DEBUG", strerror(errno)); lua_pushboolean(sf->L, 0); break; } From 2e31c5c0a15ab070e18132d9adf5664342c88d6e Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 26 Aug 2019 03:53:03 +0800 Subject: [PATCH 313/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0sendfile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 720cfe34..8b51c742 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -282,10 +282,8 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ #endif #ifdef EV_USE_EPOLL - int tag = 0; for (;;) { - tag = sendfile(io->fd, sf->fd, &sf->pos, sf->offset); - sf->pos += tag; + int tag = sendfile(io->fd, sf->fd, &sf->pos, sf->offset); if (0 > tag) { if (errno == EINTR) continue; if (errno == EWOULDBLOCK) return; @@ -296,6 +294,8 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ lua_pushboolean(sf->L, 1); break; } + sf->pos += tag; + } #endif #ifdef EV_USE_SELECT From eb7484417a7c421dcebd7eed87671bf2ee13c4ab Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 26 Aug 2019 04:13:33 +0800 Subject: [PATCH 314/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0sendfile=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 53 ++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 8b51c742..2ae1ccb4 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -258,32 +258,33 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ errno = 0; struct io_sendfile *sf = core_get_watcher_userdata(io); -#ifdef EV_USE_KQUEUE - int tag = 0; off_t nBytes = 0; - for (;;) { -#if defined(__APPLE__) - tag = sendfile(sf->fd, io->fd, sf->pos, &nBytes, NULL, 0); -#else - tag = sendfile(sf->fd, io->fd, sf->pos, 0, NULL, &nBytes, SF_NODISKIO | SF_NOCACHE); -#endif - sf->pos += nBytes; - // printf("tag = %d, pos = %lu, nBytes = %ld, errno = %d, err = %s\n", tag, sf->pos, nBytes, errno, strerror(errno)); - if (0 > tag) { - if (errno == EINTR) continue; - if (errno == EWOULDBLOCK) return; - lua_pushboolean(sf->L, 0); - break; - } - if ( !nBytes ){ - lua_pushboolean(sf->L, 1); - break; - } - } -#endif +// #ifdef EV_USE_KQUEUE +// int tag = 0; off_t nBytes = 0; +// for (;;) { +// #if defined(__APPLE__) +// tag = sendfile(sf->fd, io->fd, sf->pos, &nBytes, NULL, 0); +// #else +// tag = sendfile(sf->fd, io->fd, sf->pos, 0, NULL, &nBytes, SF_NODISKIO | SF_NOCACHE); +// #endif +// sf->pos += nBytes; +// // printf("tag = %d, pos = %lu, nBytes = %ld, errno = %d, err = %s\n", tag, sf->pos, nBytes, errno, strerror(errno)); +// if (0 > tag) { +// if (errno == EINTR) continue; +// if (errno == EWOULDBLOCK) return; +// lua_pushboolean(sf->L, 0); +// break; +// } +// if ( !nBytes ){ +// lua_pushboolean(sf->L, 1); +// break; +// } +// } +// #endif #ifdef EV_USE_EPOLL + #include for (;;) { - int tag = sendfile(io->fd, sf->fd, &sf->pos, sf->offset); + int tag = sendfile(io->fd, sf->fd, (off_t *)&sf->pos, sf->offset); if (0 > tag) { if (errno == EINTR) continue; if (errno == EWOULDBLOCK) return; @@ -296,9 +297,7 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ } sf->pos += tag; } -#endif - -#ifdef EV_USE_SELECT +#else char buf[sf->offset]; for (;;) { memset(buf, 0x0, sf->offset); @@ -330,7 +329,6 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ static int tcp_sendfile(lua_State *L){ - // LOG("DEBUG", "开始注册."); core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); lua_State *t = lua_tothread(L, 2); const char* path = luaL_checkstring(L, 3); @@ -348,7 +346,6 @@ tcp_sendfile(lua_State *L){ core_set_watcher_userdata(io, sf); core_io_init(io, IO_SENDFILE, iofd, EV_WRITE); core_io_start(CORE_LOOP_ io); - // LOG("DEBUG", "注册完成."); return 1; } From be82991dfb94a8ce86e0033a48eb8722670c89ef Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 26 Aug 2019 04:21:08 +0800 Subject: [PATCH 315/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0ltcp=E7=9A=84makefile?= =?UTF-8?q?,=20=E9=80=82=E9=85=8D=E4=B8=8D=E5=90=8C=E5=B9=B3=E5=8F=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 47 +++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 2ae1ccb4..e410e745 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -258,28 +258,27 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ errno = 0; struct io_sendfile *sf = core_get_watcher_userdata(io); -// #ifdef EV_USE_KQUEUE -// int tag = 0; off_t nBytes = 0; -// for (;;) { -// #if defined(__APPLE__) -// tag = sendfile(sf->fd, io->fd, sf->pos, &nBytes, NULL, 0); -// #else -// tag = sendfile(sf->fd, io->fd, sf->pos, 0, NULL, &nBytes, SF_NODISKIO | SF_NOCACHE); -// #endif -// sf->pos += nBytes; -// // printf("tag = %d, pos = %lu, nBytes = %ld, errno = %d, err = %s\n", tag, sf->pos, nBytes, errno, strerror(errno)); -// if (0 > tag) { -// if (errno == EINTR) continue; -// if (errno == EWOULDBLOCK) return; -// lua_pushboolean(sf->L, 0); -// break; -// } -// if ( !nBytes ){ -// lua_pushboolean(sf->L, 1); -// break; -// } -// } -// #endif +#ifdef EV_USE_KQUEUE + int tag = 0; off_t nBytes = 0; + for (;;) { +#if defined(__APPLE__) + tag = sendfile(sf->fd, io->fd, sf->pos, &nBytes, NULL, 0); +#else + tag = sendfile(sf->fd, io->fd, sf->pos, 0, NULL, &nBytes, SF_NODISKIO | SF_NOCACHE); +#endif + sf->pos += nBytes; + if (0 > tag) { + if (errno == EINTR) continue; + if (errno == EWOULDBLOCK) return; + lua_pushboolean(sf->L, 0); + break; + } + if ( !nBytes ){ + lua_pushboolean(sf->L, 1); + break; + } + } +#endif #ifdef EV_USE_EPOLL #include @@ -297,7 +296,9 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ } sf->pos += tag; } -#else +#endif + +#ifdef EV_USE_SELECT char buf[sf->offset]; for (;;) { memset(buf, 0x0, sf->offset); From 66727a203d826848e4e956833cf1f86f3be15fe0 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 26 Aug 2019 12:34:54 +0800 Subject: [PATCH 316/956] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E6=97=A0=E6=84=8F?= =?UTF-8?q?=E4=B9=89=E7=9A=84log=E6=89=93=E5=8D=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index fd4e6b69..0e936e37 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -568,7 +568,6 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) end body_len, filepath, file_type = cls(path) if not body_len then - Log:ERROR(body) statucode = 404 sock:send(ERROR_RESPONSE(http, statucode, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) return sock:close() From 0d729cd7fc0b63ce108f89a6dabdaca1b91110e6 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 26 Aug 2019 13:21:58 +0800 Subject: [PATCH 317/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0sendfile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 44 ++++++++++++++++++++-------------------- lualib/protocol/http.lua | 2 +- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index e410e745..69b43838 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -278,27 +278,27 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ break; } } -#endif - -#ifdef EV_USE_EPOLL - #include - for (;;) { - int tag = sendfile(io->fd, sf->fd, (off_t *)&sf->pos, sf->offset); - if (0 > tag) { - if (errno == EINTR) continue; - if (errno == EWOULDBLOCK) return; - lua_pushboolean(sf->L, 0); - break; - } - if ( !tag ){ - lua_pushboolean(sf->L, 1); - break; - } - sf->pos += tag; - } -#endif - -#ifdef EV_USE_SELECT +// #endif + +// #ifdef EV_USE_EPOLL +// #include +// for (;;) { +// int tag = sendfile(io->fd, sf->fd, (off_t *)&sf->pos, sf->offset); +// if (0 > tag) { +// if (errno == EINTR) continue; +// if (errno == EWOULDBLOCK) return; +// lua_pushboolean(sf->L, 0); +// break; +// } +// if ( !tag ){ +// lua_pushboolean(sf->L, 1); +// break; +// } +// sf->pos += tag; +// } +// #endif +#else +// #ifdef EV_USE_SELECT char buf[sf->offset]; for (;;) { memset(buf, 0x0, sf->offset); @@ -310,7 +310,6 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ lseek(sf->fd, lseek(sf->fd, 0, SEEK_CUR) - rBytes, SEEK_SET); if (errno == EINTR) continue ; if (errno == EWOULDBLOCK) return; - // LOG("DEBUG", strerror(errno)); lua_pushboolean(sf->L, 0); break; } @@ -344,6 +343,7 @@ tcp_sendfile(lua_State *L){ sf->fd = fd; sf->offset = offset; sf->L = t; + core_set_watcher_userdata(io, sf); core_io_init(io, IO_SENDFILE, iofd, EV_WRITE); core_io_start(CORE_LOOP_ io); diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index 0e936e37..cf809b34 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -384,7 +384,7 @@ end local function send_body (sock, body, filepath) if not body then - return sock:sendfile(filepath, 128) + return sock:sendfile(filepath, 65535) end return sock:send(body) end From 0f07116bd3b6b424f64449c41d2e3284398ff734 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 26 Aug 2019 17:33:46 +0800 Subject: [PATCH 318/956] =?UTF-8?q?=E5=AE=8C=E5=96=84senfile=E5=B9=B6?= =?UTF-8?q?=E7=AE=80=E5=8C=96=E5=AE=9E=E7=8E=B0=E4=B8=8E=E5=8E=BB=E9=99=A4?= =?UTF-8?q?=E8=B0=83=E8=AF=95=E4=BB=A3=E7=A0=81.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 69b43838..8be4b92d 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -273,32 +273,26 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ lua_pushboolean(sf->L, 0); break; } - if ( !nBytes ){ - lua_pushboolean(sf->L, 1); + // 当nBytes与tag同时为0时说明发送成功, 其它情况下都当做发送失败. + if (0 == nBytes){ lua_pushboolean(sf->L, 1); break; } + } +#endif + +#ifdef EV_USE_EPOLL + #include + for (;;) { + int tag = sendfile(io->fd, sf->fd, NULL, sf->offset); + if (0 => tag) { + if (!tag){ lua_pushboolean(sf->L, 1); break; } + if (errno == EINTR) continue; + if (errno == EWOULDBLOCK) return; + lua_pushboolean(sf->L, 0); break; } } -// #endif - -// #ifdef EV_USE_EPOLL -// #include -// for (;;) { -// int tag = sendfile(io->fd, sf->fd, (off_t *)&sf->pos, sf->offset); -// if (0 > tag) { -// if (errno == EINTR) continue; -// if (errno == EWOULDBLOCK) return; -// lua_pushboolean(sf->L, 0); -// break; -// } -// if ( !tag ){ -// lua_pushboolean(sf->L, 1); -// break; -// } -// sf->pos += tag; -// } -// #endif -#else -// #ifdef EV_USE_SELECT +#endif + +#ifdef EV_USE_SELECT char buf[sf->offset]; for (;;) { memset(buf, 0x0, sf->offset); From 34d4bbe12de2914ab8daf8fad38bcdd085d8cd41 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 26 Aug 2019 17:34:55 +0800 Subject: [PATCH 319/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 8be4b92d..dee004d2 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -282,7 +282,7 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ #include for (;;) { int tag = sendfile(io->fd, sf->fd, NULL, sf->offset); - if (0 => tag) { + if (0 >= tag) { if (!tag){ lua_pushboolean(sf->L, 1); break; } if (errno == EINTR) continue; if (errno == EWOULDBLOCK) return; From 0a2873dc6ada978ea444de98c92bf314c58af480 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 27 Aug 2019 10:53:57 +0800 Subject: [PATCH 320/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0http=20protocol?= =?UTF-8?q?=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index cf809b34..ac317d79 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -546,9 +546,8 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) clCookie() end if not ok then - Log:ERROR(body) - statucode = 500 - sock:send(ERROR_RESPONSE(http, statucode, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) + Log:ERROR(body or "empty response.") + sock:send(ERROR_RESPONSE(http, 500, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) return sock:close() end statucode = 200 @@ -602,12 +601,12 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) else header[#header+1] = concat({'Content-Type: ', REQUEST_MIME_RESPONSE('html'), ';charset=utf-8'}) end - if type(body) == 'string' and #body >= 0 then - header[#header+1] = 'Content-Length: '.. #body - else - Log:WARN("response body can't be a string type.") + if type(body) ~= 'string' or body == '' then + Log:ERROR("Response Error ["..(split(PATH , 1, (find(PATH, '?') or 0 ) - 1)).."]: response must be a string and not empty.") + sock:send(ERROR_RESPONSE(http, 500, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) return sock:close() end + header[#header+1] = 'Content-Length: '.. #body header[#header+1] = 'Cache-Control: no-cache, no-store, must-revalidate' header[#header+1] = 'Cache-Control: no-cache' else @@ -619,9 +618,10 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) end header[#header+1] = static end + -- 不计算数据传输时间, 仅计算实际回调处理所用时间. + http:tolog(statucode, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, now() - start) -- 根据实际情况分块发送 local ok = send_header(sock, header) and send_body(sock, body, filepath) or false - http:tolog(statucode, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, now() - start) if not ok then return sock:close() end From f0bbcb491c7c2cf136e132c6fd8916a4a54cbddb Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 28 Aug 2019 07:56:35 +0800 Subject: [PATCH 321/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0layui=E5=88=B02.5?= =?UTF-8?q?=E7=89=88=E6=9C=AC,=20=E4=B8=8B=E8=BD=BDjquery=E4=B8=8Eechart?= =?UTF-8?q?=E5=88=B0static=E6=9C=AC=E5=9C=B0=E4=BF=AE=E5=A4=8D=E5=9C=A8?= =?UTF-8?q?=E6=96=AD=E7=BD=91=E7=8E=AF=E5=A2=83=E4=B8=8B=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E7=9A=84=E9=97=AE=E9=A2=98.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/html/login/head.html | 2 +- static/js/echarts.min.js | 1 + static/lib/layui/css/layui.css | 4 ++-- static/lib/layui/css/layui.mobile.css | 2 +- static/lib/layui/css/modules/code.css | 2 +- .../css/modules/laydate/default/laydate.css | 2 +- .../layui/css/modules/layer/default/layer.css | 2 +- static/lib/layui/font/iconfont.eot | Bin 40844 -> 41712 bytes static/lib/layui/font/iconfont.svg | 12 ++++++++++++ static/lib/layui/font/iconfont.ttf | Bin 40668 -> 41536 bytes static/lib/layui/font/iconfont.woff | Bin 26744 -> 27256 bytes static/lib/layui/font/iconfont.woff2 | Bin 0 -> 23140 bytes static/lib/layui/lay/modules/carousel.js | 4 ++-- static/lib/layui/lay/modules/code.js | 2 +- static/lib/layui/lay/modules/colorpicker.js | 2 +- static/lib/layui/lay/modules/element.js | 2 +- static/lib/layui/lay/modules/flow.js | 2 +- static/lib/layui/lay/modules/form.js | 4 ++-- static/lib/layui/lay/modules/jquery.js | 2 +- static/lib/layui/lay/modules/laydate.js | 2 +- static/lib/layui/lay/modules/layedit.js | 2 +- static/lib/layui/lay/modules/layer.js | 2 +- static/lib/layui/lay/modules/laypage.js | 2 +- static/lib/layui/lay/modules/laytpl.js | 2 +- static/lib/layui/lay/modules/mobile.js | 2 +- static/lib/layui/lay/modules/rate.js | 2 +- static/lib/layui/lay/modules/slider.js | 2 +- static/lib/layui/lay/modules/table.js | 4 ++-- static/lib/layui/lay/modules/transfer.js | 2 ++ static/lib/layui/lay/modules/tree.js | 4 ++-- static/lib/layui/lay/modules/upload.js | 4 ++-- static/lib/layui/lay/modules/util.js | 4 ++-- static/lib/layui/layui.js | 4 ++-- static/welcome.html | 2 +- 34 files changed, 50 insertions(+), 35 deletions(-) create mode 100644 static/js/echarts.min.js create mode 100755 static/lib/layui/font/iconfont.woff2 create mode 100755 static/lib/layui/lay/modules/transfer.js diff --git a/lualib/admin/html/login/head.html b/lualib/admin/html/login/head.html index 9bc7ce66..46871eb1 100644 --- a/lualib/admin/html/login/head.html +++ b/lualib/admin/html/login/head.html @@ -9,7 +9,7 @@ - + - - -
                  -
                  + + + 欢迎页面-X-admin2.2 + + + + + + + + + + +
                  + +
                  +
                  +
                  欢迎管理员: + admin!当前时间: +
                  +
                  +
                  +
                  +
                  -
                  -
                  欢迎管理员: - admin!当前时间:2019-05-10 00:00:03 -
                  +
                  最新一周新增用户
                  +
                  +
                  +
                  -
                  -
                  -
                  最新一周新增用户
                  -
                  -
                  - -
                  -
                  -
                  -
                  -
                  -
                  最新一周PV/UV量
                  -
                  -
                  - -
                  -
                  -
                  -
                  -
                  -
                  用户来源
                  -
                  -
                  - -
                  -
                  -
                  -
                  -
                  -
                  硬盘使用量
                  -
                  -
                  - -
                  -
                  -
                  -
                  -
                  -
                  +
                  +
                  +
                  +
                  最新一周PV/UV量
                  +
                  +
                  + +
                  +
                  +
                  +
                  +
                  +
                  用户来源
                  +
                  +
                  - - +
                  +
                  +
                  +
                  +
                  +
                  硬盘使用量
                  +
                  +
                  + +
                  +
                  +
                  +
                  +
                  + + + From d0b0e98a2dec1bd6729c0c9c118875cc25c32ad8 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 28 Aug 2019 11:43:00 +0800 Subject: [PATCH 324/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E9=A1=B5=E9=9D=A2=E7=9A=84=E5=88=86=E9=A1=B5?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E6=95=88=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/db/user.lua | 39 +++++++++++++++++-------------- lualib/admin/http/system/user.lua | 6 ++--- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/lualib/admin/db/user.lua b/lualib/admin/db/user.lua index f07a524e..68ba7c1c 100644 --- a/lualib/admin/db/user.lua +++ b/lualib/admin/db/user.lua @@ -28,7 +28,7 @@ function user.user_list (db, opt) end -- 模糊查找用户列表 -function user.find_by_username (db, opt) +function user.find_user (db, opt) local limit = toint(opt.limit) or 10 local page = toint(opt.page) or 1 local condition @@ -37,22 +37,27 @@ function user.find_by_username (db, opt) else condition = fmt("`cfadmin_users`.`%s` LIKE '%%%s%%'", opt.condition, opt.value) end - return db:query(fmt([[ - SELECT - `cfadmin_users`.id, - `cfadmin_users`.name, - `cfadmin_users`.username, - `cfadmin_roles`.name AS role_name, - `cfadmin_users`.email, - `cfadmin_users`.phone, - `cfadmin_users`.create_at, - `cfadmin_users`.update_at, - `cfadmin_users`.active - FROM cfadmin_users, cfadmin_roles - WHERE - `cfadmin_roles`.id = `cfadmin_users`.role AND `cfadmin_users`.active = 1 AND %s - ORDER BY `cfadmin_users`.id LIMIT %s, %s - ]], condition, limit * (page - 1), limit)) + local counts = db:query(fmt([[SELECT COUNT(id) as count FROM cfadmin_users WHERE active = 1 AND %s ]], condition)) + if not counts or not counts[1] then + return + end + local data_sql = fmt([[ + SELECT + `cfadmin_users`.id, + `cfadmin_users`.name, + `cfadmin_users`.username, + `cfadmin_roles`.name AS role_name, + `cfadmin_users`.email, + `cfadmin_users`.phone, + `cfadmin_users`.create_at, + `cfadmin_users`.update_at, + `cfadmin_users`.active + FROM cfadmin_users, cfadmin_roles + WHERE + `cfadmin_roles`.id = `cfadmin_users`.role AND `cfadmin_users`.active = 1 AND %s + ORDER BY `cfadmin_users`.id LIMIT %s, %s + ]], condition, limit * (page - 1), limit) + return db:query(data_sql), counts[1]['count'] end -- 用户总数 diff --git a/lualib/admin/http/system/user.lua b/lualib/admin/http/system/user.lua index 29d49dcf..16233db1 100644 --- a/lualib/admin/http/system/user.lua +++ b/lualib/admin/http/system/user.lua @@ -137,11 +137,11 @@ function system.user_response (content) if not args.value or not args.condition then return json_encode({code = 400, data = null, msg = '1. 无效的参数'}) end - local users = user.find_by_username(db, args) - if not users then + local users, count = user.find_user(db, args) + if not users or not count then return json_encode({code = 400, data = null, msg = '2. 无效的参数'}) end - return json_encode({code = 0, data = users }) + return json_encode({code = 0, data = users, count = count}) end -- 添加用户 if action == 'add' then From 9b78a029854e0cfd7a2d5e8524f36916c8d5deb9 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 28 Aug 2019 12:35:09 +0800 Subject: [PATCH 325/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E6=8E=92=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/html/redirect.html | 2 +- lualib/admin/html/system/user/user.html | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lualib/admin/html/redirect.html b/lualib/admin/html/redirect.html index c38b38a3..16696a4d 100644 --- a/lualib/admin/html/redirect.html +++ b/lualib/admin/html/redirect.html @@ -1,5 +1,5 @@ - + diff --git a/lualib/admin/html/system/user/user.html b/lualib/admin/html/system/user/user.html index af751276..faf75e2b 100644 --- a/lualib/admin/html/system/user/user.html +++ b/lualib/admin/html/system/user/user.html @@ -22,13 +22,13 @@ # - {* locale['dashboard.menu.user_manage.table.name'] *} - {* locale['dashboard.menu.user_manage.table.username'] *} - {* locale['dashboard.menu.user_manage.table.permission_name'] *} - {* locale['dashboard.menu.user_manage.table.email'] *} - {* locale['dashboard.menu.user_manage.table.phone'] *} - {* locale['dashboard.menu.user_manage.table.create_time'] *} - {* locale['dashboard.menu.user_manage.table.update_time'] *} + {* locale['dashboard.menu.user_manage.table.name'] *} + {* locale['dashboard.menu.user_manage.table.username'] *} + {* locale['dashboard.menu.user_manage.table.permission_name'] *} + {* locale['dashboard.menu.user_manage.table.email'] *} + {* locale['dashboard.menu.user_manage.table.phone'] *} + {* locale['dashboard.menu.user_manage.table.create_time'] *} + {* locale['dashboard.menu.user_manage.table.update_time'] *} {* locale['dashboard.menu.user_manage.table.options'] *} From 7647fdfaffb13e856543dcef581e3cac5c20be88 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 28 Aug 2019 22:49:51 +0800 Subject: [PATCH 326/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dpos=20json=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/httpc/init.lua b/lualib/httpc/init.lua index 87314e5e..cecc4421 100644 --- a/lualib/httpc/init.lua +++ b/lualib/httpc/init.lua @@ -117,7 +117,7 @@ local function json(domain, headers, json, timeout) opt.headers = headers opt.server = SERVER - local REQ = build_post_req(opt) + local REQ = build_json_req(opt) local sock = sock_new():timeout(timeout or __TIMEOUT__) local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) From 80a2c43148ec8c79353c1a23a6383508ad7f86d8 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 28 Aug 2019 23:25:23 +0800 Subject: [PATCH 327/956] =?UTF-8?q?=E4=BD=BFhttpc=E5=85=BC=E5=AE=B9?= =?UTF-8?q?=E6=9F=90=E4=BA=9B=E7=89=B9=E6=AE=8A=E6=83=85=E5=86=B5=E4=B8=8B?= =?UTF-8?q?=E7=9A=84=E7=BC=96=E7=A0=81=E9=97=AE=E9=A2=98.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/protocol.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua index 16f761de..a28a7558 100644 --- a/lualib/httpc/protocol.lua +++ b/lualib/httpc/protocol.lua @@ -1,5 +1,8 @@ local tcp = require "internal.TCP" +local url = require "url" +local url_encode = url.encode + local HTTP = require "protocol.http" local FILEMIME = HTTP.FILEMIME local PARSER_HTTP_RESPONSE = HTTP.PARSER_HTTP_RESPONSE @@ -218,8 +221,8 @@ local function build_get_req (opt) if type(opt.args) == "table" then local args = {} for _, arg in ipairs(opt.args) do - assert(#arg == 2, "args need key[1]->value[2] (2 values)") - insert(args, arg[1]..'='..arg[2]) + assert(#arg == 2, "args need key[1]->value[2] (2 values and must be string)") + insert(args, url_encode(arg[1])..'='..url_encode(arg[2])) end request[1] = fmt("GET %s HTTP/1.1", opt.path..'?'..concat(args, "&")) end @@ -252,12 +255,11 @@ local function build_post_req (opt) end end insert(request, CRLF) - if type(opt.body) == "table" then local body = {} for _, b in ipairs(opt.body) do assert(#b == 2, "if BODY is TABLE, BODY need key[1]->value[2] (2 values)") - insert(body, fmt("%s=%s", b[1], b[2])) + insert(body, url_encode(b[1])..'='..url_encode(b[2])) end insert(request, concat(body, "&")) insert(request, #request - 2, fmt("Content-length: %s\r\n", #request[#request])) From 3b4376f94030e36fbe4b7be412c6e13bc3742d66 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 29 Aug 2019 23:13:06 +0800 Subject: [PATCH 328/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0webhook=E5=BA=93,=20?= =?UTF-8?q?=E5=86=85=E7=BD=AEdingtalk=E7=9A=84=E6=9C=BA=E5=99=A8=E4=BA=BA4?= =?UTF-8?q?=E7=B1=BB=E6=B6=88=E6=81=AF=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/webhook/dingtalk.lua | 111 ++++++++++++++++++++++++++++++++++++ script/dingtalk.lua | 62 ++++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 lualib/webhook/dingtalk.lua create mode 100644 script/dingtalk.lua diff --git a/lualib/webhook/dingtalk.lua b/lualib/webhook/dingtalk.lua new file mode 100644 index 00000000..8b24036a --- /dev/null +++ b/lualib/webhook/dingtalk.lua @@ -0,0 +1,111 @@ +local httpc = require "httpc" +local json = require "json" + +local class = require "class" + +local type = type +local ipairs = ipairs + +local function check_error (code, response) + if code ~= 200 then + return nil, "请求失败." .. (response or "") + end + local r = json.decode(response) + if type(r) ~= 'table' then + return nil, "未知的回应." + end + if r.errcode ~= 0 then + return nil, r.errmsg + end + return true +end + +local dingtalk = { __VERSION__ = 0.1, robot = "https://oapi.dingtalk.com/robot/send?access_token=" } + +-- 发送text消息 +function dingtalk.send_text (opt) + assert(type(opt) == 'table', "dingtalk error: 无效的参数.") + assert(type(opt.token) == 'string' and opt.token ~= '', "dingtalk error: 无效的Token参数.") + assert(type(opt.content) == 'string' and opt.content ~= '', "dingtalk error: 无效的content参数.") + local allmobiles = {} + if type(opt.mobiles) == 'table' then + for _, phone in ipairs(opt.mobiles) do + allmobiles[#allmobiles+1] = tostring(phone) + end + end + local code, response = httpc.json(dingtalk.robot..opt.token, nil, json.encode({ msgtype = "text", text = { content = opt.content }, at = { atMobiles = allmobiles, isAtAll = opt.atall == true} })) + return check_error(code, response) +end + +-- 发送link消息 +function dingtalk.send_link (opt) + assert(type(opt) == 'table', "dingtalk error: 无效的参数.") + assert(type(opt.token) == 'string' and opt.token ~= '', "dingtalk error: 无效的Token参数.") + assert(type(opt.msg_link) == 'string' and opt.msg_link ~= '', "dingtalk error: 无效的msg_link(必须为非空字符串).") + assert(type(opt.msg_title) == 'string' and opt.msg_title ~= '', "dingtalk error: 无效的msg_title(必须为非空字符串).") + assert(type(opt.msg_describe) == 'string' and opt.msg_describe ~= '', "dingtalk error: 无效的msg_describe(必须为非空字符串).") + local code, response = httpc.json(dingtalk.robot..opt.token, nil, json.encode({ msgtype = "link", link = { title = opt.msg_title, text = opt.msg_describe, messageUrl = opt.msg_link, picUrl = opt.msg_pic } })) + return check_error(code, response) +end + +-- 发送markdown消息 +function dingtalk.send_markdown (opt) + assert(type(opt) == 'table', "dingtalk error: 无效的参数.") + assert(type(opt.token) == 'string' and opt.token ~= '', "dingtalk error: 无效的Token参数.") + assert(type(opt.msg_title) == 'string' and opt.msg_title ~= '', "dingtalk error: 无效的msg_title(必须为非空字符串).") + assert(type(opt.msg_content) == 'string' and opt.msg_content ~= '', "dingtalk error: 无效的msg_content(必须为非空字符串).") + + local allmobiles = {} + if type(opt.mobiles) == 'table' then + for _, phone in ipairs(opt.mobiles) do + allmobiles[#allmobiles+1] = tostring(phone) + end + end + local code, response = httpc.json(dingtalk.robot..opt.token, nil, json.encode({ + msgtype = "markdown", markdown = { title = opt.msg_title, text = opt.msg_content }, at = { atMobiles = opt.mobiles, isAtAll = opt.atall == true } + })) + return check_error(code, response) +end + +-- 发送actionCard消息 +function dingtalk.send_actioncard (opt) + assert(type(opt) == 'table', "dingtalk error: 无效的参数.") + assert(type(opt.token) == 'string' and opt.token ~= '', "dingtalk error: 无效的Token参数.") + assert(type(opt.msg_title) == 'string' and opt.msg_title ~= '', "dingtalk error: 无效的msg_title(必须为非空字符串).") + assert(type(opt.msg_describe) == 'string' and opt.msg_describe ~= '', "dingtalk error: 无效的msg_describe(必须为非空字符串).") + + local btns, single_title, single_url = nil, nil, nil + if type(opt.single) == 'table' and (type(opt.single.title) == 'string' and opt.single.title ~= '') and (type(opt.single.url) == 'string' and opt.single.url ~= '') then + single_title, single_url = opt.single.title, opt.single.url + end + + if not single_title and not single_url and type(opt.btns) == 'table' and #opt.btns >= 1 then + btns = {} + for index, btn in ipairs(opt.btns) do + assert(type(btn.title) == 'string' and btn.title ~= '', 'dingtalk error: btns第'..index..'个参数title无效.') + assert(type(btn.url) == 'string' and btn.url ~= '', 'dingtalk error: btns第'..index..'个参数url无效.') + btns[#btns+1] = {title = btn.title, actionURL = btn.url} + end + end + + local code, response = httpc.json(dingtalk.robot..opt.token, nil, json.encode({ msgtype = "actionCard", actionCard = { title = opt.msg_title, text = opt.msg_describe, singleURL = single_url, singleTitle = single_title, btns = btns, hideAvatar = opt.hide_avatar and '1' or '0', btnOrientation = opt.btn_orientation and '1' or '0' }})) + return check_error(code, response) +end + +-- 发送FeedCard消息 +function dingtalk.send_feedcard (opt) + assert(type(opt) == 'table', "dingtalk error: 无效的参数.") + assert(type(opt.token) == 'string' and opt.token ~= '', "dingtalk error: 无效的Token参数.") + assert(type(opt.msg_links) == 'table' and #opt.msg_links >= 1, "dingtalk error: 无效的msg_links参数(内部至少有一条消息).") + local links = {} + for index, link in ipairs(opt.msg_links) do + assert(type(link) == 'table', "dingtalk error: 无效的消息"..index) + assert(type(link.msg_title) == 'string' and link.msg_title ~= '', "dingtalk error: 第"..index..'个消息的msg_title无效.') + assert(type(link.msg_link) == 'string' and link.msg_link ~= '', "dingtalk error: 第"..index..'个消息的msg_link无效.') + links[#links+1] = { title = link.msg_title, messageURL = link.msg_link, picURL = link.msg_pic } + end + local code, response = httpc.json(dingtalk.robot..opt.token, nil, json.encode({ msgtype = "feedCard", feedCard = { links = links } })) + return check_error(code, response) +end + +return dingtalk diff --git a/script/dingtalk.lua b/script/dingtalk.lua new file mode 100644 index 00000000..4a8e204b --- /dev/null +++ b/script/dingtalk.lua @@ -0,0 +1,62 @@ +local wb = require "webhook.dingtalk" + +local token = "just_your_token_not_url_and_token" -- such as "de2b0b8a3c4b8d454f47584354a794a12657aa9ff7ccf36b521368d566949e7f" + +print(wb.send_text({ + token = token, + content = "一条测试消息哦.", + -- ignore mobiles if atall equal true. + -- mobiles = {18680684684, 13000000000}, + -- atall = true, +})) + +print(wb.send_link { + token = token, + msg_title = "这是一条测试公告", + msg_link = "https://github.com/candymi", + -- optional + msg_pic = "https://avatars2.githubusercontent.com/u/13453599", + msg_describe = "这是测试公告的描述信息, 它描述了这条公告的一些外链关键内容.", +}) + +print(wb.send_actioncard{ + token = token, + msg_title = "## 消息头部", + msg_describe = "## 消息内容", + single = { + title = "阅读全文", + url = "https://www.baidu.com" + } +}) + +print(wb.send_actioncard{ + token = token, + -- 头部 + msg_title = "## 消息头部", + -- 描述 + msg_describe = "## 消息内容", + -- 隐藏机器人头像 + hide_avatar = true, + -- 设置按钮 + btns = { + { title = "按钮1", url = "https://www.qq.com" }, + { title = "按钮2", url = "https://www.baidu.com" }, + { title = "点赞支持", url = "https://www.163.com" }, + { title = "残忍关闭", url = "https://www.taobao.com" } + } +}) + +print(wb.send_feedcard { + token = token, + msg_links = { + { + msg_title = "第1个公告", msg_link = "https://www.baidu.com", + -- msg_pic = "https://avatars2.githubusercontent.com/u/13453599" -- optional + }, + { + msg_title = "第2个公告", msg_link = "https://www.qq.com", + -- optional + -- msg_pic = "https://avatars2.githubusercontent.com/u/13453599" + } + } +}) From f8d728e54634946a794da44a3878046b47790c99 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 29 Aug 2019 23:21:49 +0800 Subject: [PATCH 329/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0script/dingtalk.lua?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E8=84=9A=E6=9C=AC=E5=B9=B6=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/dingtalk.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/script/dingtalk.lua b/script/dingtalk.lua index 4a8e204b..a5133f0e 100644 --- a/script/dingtalk.lua +++ b/script/dingtalk.lua @@ -1,5 +1,7 @@ local wb = require "webhook.dingtalk" +-- dingtalk API文档: https://ding-doc.dingtalk.com/doc#/serverapi2/qf2nxq + local token = "just_your_token_not_url_and_token" -- such as "de2b0b8a3c4b8d454f47584354a794a12657aa9ff7ccf36b521368d566949e7f" print(wb.send_text({ From beb9622471ddc3b8c60a05925eb66718cfe2295f Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 29 Aug 2019 23:22:52 +0800 Subject: [PATCH 330/956] =?UTF-8?q?=E9=87=8D=E5=91=BD=E5=90=8Dscript/dingt?= =?UTF-8?q?alk.lua=E4=B8=BAscript/test=5Fdingtalk.lua?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/{dingtalk.lua => test_dingtalk.lua} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename script/{dingtalk.lua => test_dingtalk.lua} (100%) diff --git a/script/dingtalk.lua b/script/test_dingtalk.lua similarity index 100% rename from script/dingtalk.lua rename to script/test_dingtalk.lua From 0f0016bf6c473c6e06166b6ca79468264eb2a192 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 30 Aug 2019 01:26:12 +0800 Subject: [PATCH 331/956] =?UTF-8?q?httpd.Form=E5=A2=9E=E5=8A=A0=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E5=A4=96=E9=83=A8=E8=8E=B7=E5=8F=96url=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E7=9A=84=E6=96=B9=E6=B3=95.=E7=94=A8=E4=BA=8E?= =?UTF-8?q?=E9=80=82=E9=85=8D=E4=B8=80=E4=BA=9B=E7=89=B9=E6=AE=8A=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E5=AE=9E=E7=8E=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Form.lua | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lualib/httpd/Form.lua b/lualib/httpd/Form.lua index d167b668..0bb1fefa 100644 --- a/lualib/httpd/Form.lua +++ b/lualib/httpd/Form.lua @@ -3,9 +3,13 @@ local urlencode = url.encode local urldecode = url.decode local type = type + local string = string -local table = table +local sub = string.sub +local find = string.find local splite = string.gmatch + +local table = table local insert = table.insert -- require "utils" @@ -14,6 +18,17 @@ local form = { ARGS = 1, } +function form.get_args (path) + if type(path) ~= 'string' or path == '' then + return + end + local s, e = find(path, '?'), #path + if not s or e - s < 3 then + return + end + return form.urlencode(sub(path, s + 1, e)) +end + -- 将body解析为x-www-form-urlencoded function form.urlencode(body) if type(body) ~= 'string' then From 056d0ecf39f243e516ba35a8ae29e78bd160e2bc Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 30 Aug 2019 14:21:57 +0800 Subject: [PATCH 332/956] fix issue #11 --- lualib/httpd/Router.lua | 5 ++++- lualib/protocol/http.lua | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index 61c5515a..c8915d6c 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -1,6 +1,9 @@ local log = require "logging" local Log = log:new({dump = true, path = 'httpd-Router'}) +local url = require "url" +local url_decode = url.decode + local new_tab = require("sys").new_tab local math = math @@ -114,7 +117,7 @@ local function find_route (method, path) return end load_file = load_file or function (path) - local filepath = prefix..path + local filepath = prefix..url_decode(path) -- 使用r+测试是否可读可写; 如果filepath是目录则无法被打开, 但单独的r模式可以. local f, error = io_open(filepath, 'r+') if not f then diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index ac317d79..836b79fc 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -578,7 +578,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) header[#header+1] = 'Content-Disposition: attachment' -- 确保浏览器提示需要下载 static = fmt('Content-Type: %s', 'application/octet-stream') else - static = fmt('Content-Type: %s', conten_type) + static = fmt('Content-Type: %s', conten_type..'; charset=utf-8') end -- 如果是静态文件, 增加默认跨域访问支持 header[#header+1] = "Access-Control-Allow-Origin: *" From 6f6131fc29d3e6992247959e43468534fd123b49 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 31 Aug 2019 03:25:26 +0800 Subject: [PATCH 333/956] =?UTF-8?q?=E8=A1=A5=E5=85=A8=E5=88=A4=E6=96=AD?= =?UTF-8?q?=E4=B8=8D=E5=AE=8C=E5=85=A8=E5=BE=97=E5=AE=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_ev.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core_ev.h b/src/core_ev.h index 1396e78d..7f6c43bb 100644 --- a/src/core_ev.h +++ b/src/core_ev.h @@ -39,7 +39,11 @@ #endif #if defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) - #define EV_USE_KQUEUE 1 + #define EV_USE_KQUEUE 1 +#endif + +#if !defined(EV_USE_KQUEUE) && !defined(EV_USE_EPOLL) + #define EV_USE_SELECT 1 #endif #include "ev.h" From b60c517672e94664253bcda6ce4bd4a9c70e52ee Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 1 Sep 2019 06:39:59 +0800 Subject: [PATCH 334/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E5=91=BD=E5=90=8D=E9=94=99=E8=AF=AF=E7=9A=84=E6=AD=A7=E4=B9=89?= =?UTF-8?q?=E6=80=A7=E9=97=AE=E9=A2=98=E4=B8=8E=E4=BF=AE=E5=A4=8D=E6=9C=AA?= =?UTF-8?q?=E5=8A=A0local=E4=BF=AE=E9=A5=B0=E7=AC=A6upvalue=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/http/login.lua | 3 ++- lualib/httpd/Cookie.lua | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lualib/admin/http/login.lua b/lualib/admin/http/login.lua index f346d399..6e885298 100644 --- a/lualib/admin/http/login.lua +++ b/lualib/admin/http/login.lua @@ -11,9 +11,10 @@ local json = require "json" local json_decode = json.decode local json_encode = json.encode +local type = type local get_locale = utils.get_locale -template_path = 'lualib/admin/html/login/base.html' +local template_path = 'lualib/admin/html/login/base.html' local login = {} diff --git a/lualib/httpd/Cookie.lua b/lualib/httpd/Cookie.lua index c7a296b7..2ea60263 100644 --- a/lualib/httpd/Cookie.lua +++ b/lualib/httpd/Cookie.lua @@ -2,7 +2,7 @@ local Co = require "internal.Co" local co_self = Co.self local crypt = require "crypt" -local xore_str = crypt.xor_str +local xor_str = crypt.xor_str local hexencode = crypt.hexencode local hexdecode = crypt.hexdecode @@ -19,12 +19,12 @@ local secure = 'http://github.com/candymi/core_framework' -- 加密Cookie Value local function encode_value (value) - return hexencode(xore_str(value, secure)):upper() + return hexencode(xor_str(value, secure)):upper() end -- 解密Cookie Value local function decode_value (value) - return xore_str(hexdecode(value:lower()), secure) + return xor_str(hexdecode(value:lower()), secure) end -- 当前协程注册的cookie From 5b78564c978573d00f4ff1d630b0ae1a763ad499 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 1 Sep 2019 09:01:03 +0800 Subject: [PATCH 335/956] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E7=9A=84=E9=82=AE=E7=AE=B1=E4=B8=8E=E6=94=B6=E9=9B=86=E5=8F=AF?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E8=B0=83=E7=94=A8=E6=9C=AC=E5=9C=B0=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F=E7=9A=84=E8=83=BD=E5=8A=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/html/system/user/user.html | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lualib/admin/html/system/user/user.html b/lualib/admin/html/system/user/user.html index faf75e2b..d64ff873 100644 --- a/lualib/admin/html/system/user/user.html +++ b/lualib/admin/html/system/user/user.html @@ -23,10 +23,10 @@ # {* locale['dashboard.menu.user_manage.table.name'] *} - {* locale['dashboard.menu.user_manage.table.username'] *} - {* locale['dashboard.menu.user_manage.table.permission_name'] *} - {* locale['dashboard.menu.user_manage.table.email'] *} - {* locale['dashboard.menu.user_manage.table.phone'] *} + {* locale['dashboard.menu.user_manage.table.username'] *} + {* locale['dashboard.menu.user_manage.table.permission_name'] *} + {* locale['dashboard.menu.user_manage.table.email'] *} + {* locale['dashboard.menu.user_manage.table.phone'] *} {* locale['dashboard.menu.user_manage.table.create_time'] *} {* locale['dashboard.menu.user_manage.table.update_time'] *} {* locale['dashboard.menu.user_manage.table.options'] *} @@ -62,6 +62,11 @@
                  + {-raw-} + + + + {-raw-}
                  + {-raw-} + + + + {-raw-} + diff --git a/lualib/admin/html/dashboard/aside.html b/lualib/admin/html/dashboard/aside.html index 95127b95..b09fad7b 100644 --- a/lualib/admin/html/dashboard/aside.html +++ b/lualib/admin/html/dashboard/aside.html @@ -11,7 +11,7 @@ {% if is_admin then %}
                • - + {* locale['dashboard.menu.system'] *} @@ -48,7 +48,7 @@ {% elseif type(menu[2]) == 'table' then %} - {*menu[3]*} + {*menu[3]*} {{locale[menu[1]] or menu[1]}} diff --git a/lualib/admin/html/dashboard/content.html b/lualib/admin/html/dashboard/content.html index c97a937a..b0dabab5 100644 --- a/lualib/admin/html/dashboard/content.html +++ b/lualib/admin/html/dashboard/content.html @@ -1,23 +1,19 @@
                  -
                  -
                    -
                  • - {{ locale['dashboard.crumbs.home'] }} -
                  • -
                  -
                  -
                  -
                  {{ locale['dashboard.crumbs.close_this'] }}
                  -
                  {{ locale['dashboard.crumbs.close_other'] }}
                  -
                  {{ locale['dashboard.crumbs.close_all'] }}
                  -
                  -
                  -
                  -
                  - -
                  -
                  -
                  +
                  +
                    +
                  • {{ locale['dashboard.crumbs.home'] }}
                  • +
                  +
                  +
                  +
                  {{ locale['dashboard.crumbs.close_this'] }}
                  +
                  {{ locale['dashboard.crumbs.close_other'] }}
                  +
                  {{ locale['dashboard.crumbs.close_all'] }}
                  +
                  +
                  +
                  +
                  +
                  +
                  diff --git a/lualib/admin/html/system/header/header.html b/lualib/admin/html/system/header/header.html index 9eb81373..28712876 100644 --- a/lualib/admin/html/system/header/header.html +++ b/lualib/admin/html/system/header/header.html @@ -23,7 +23,7 @@ # {* locale['dashboard.menu.header_manage.table.name'] *} - {* locale['dashboard.menu.header_manage.table.url'] *} + {* locale['dashboard.menu.header_manage.table.url'] *} {* locale['dashboard.menu.header_manage.table.create_time'] *} {* locale['dashboard.menu.header_manage.table.update_time'] *} {* locale['dashboard.menu.header_manage.table.options'] *} @@ -44,6 +44,10 @@
                  + {-raw-} + + + {-raw-}
                • From 9257aa15e73bb16f2d700089f90cfba2aa6c0e16 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 4 Sep 2019 21:49:06 +0800 Subject: [PATCH 345/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E4=BF=AE=E6=94=B9=E7=94=A8=E6=88=B7=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E7=9A=84=E8=B4=A6=E6=88=B7/=E5=AF=86=E7=A0=81?= =?UTF-8?q?=E6=95=B0=E9=87=8F=E5=AD=97=E7=AC=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/http/login.lua | 2 +- lualib/admin/http/system/user.lua | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lualib/admin/http/login.lua b/lualib/admin/http/login.lua index 6e885298..64e4f0b1 100644 --- a/lualib/admin/http/login.lua +++ b/lualib/admin/http/login.lua @@ -45,7 +45,7 @@ function login.response (content) end -- 获取登录信息 local user_info = user.user_exists(db, username) - if not user_info or crypt.sha1(password, true) ~= user_info.password then + if not user_info or username ~= user_info.username or crypt.sha1(password, true) ~= user_info.password then return json_encode({code = 403, msg = "3. 用户不存在或者密码错误"}) end local uid, name = user_info.id, user_info.name diff --git a/lualib/admin/http/system/user.lua b/lualib/admin/http/system/user.lua index f91973a2..4aea1c99 100644 --- a/lualib/admin/http/system/user.lua +++ b/lualib/admin/http/system/user.lua @@ -207,15 +207,19 @@ function system.user_response (content) if not args.username or not args.password then return json_encode({code = 400, data = null, msg = "3. 未知的账户与密码"}) end + if #args.username < 6 or #args.username > 20 or #args.password < 6 or #args.password > 20 then + return json_encode({code = 401, msg = "4. 账户/密码应该为6-20个字符."}) + end + args.phone = toint(args.phone) + args.password = crypt.sha1(args.password, true) args.username = utils.escape_script(url.decode(args.username)) if not args.phone or not args.email then - return json_encode({code = 400, data = null, msg = "4. 邮箱与手机号"}) + return json_encode({code = 400, data = null, msg = "5. 无效的邮箱与手机号"}) end args.email = utils.escape_script(url.decode(args.email)) - args.password = crypt.sha1(args.password, true) local ok = user.user_update(db, args) if not ok then - return json_encode({code = 401, msg = "5. 更新用户信息失败"}) + return json_encode({code = 401, msg = "6. 更新用户信息失败"}) end user_token.token_delete(db, args.id) -- 清除Token return json_encode({code = 0, msg = "SUCCESS"}) From bcda3572f1f4b6eb0f64916fea4297711e9a0905 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 5 Sep 2019 00:44:03 +0800 Subject: [PATCH 346/956] =?UTF-8?q?=E4=BC=98=E5=8C=96login=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E5=88=A4=E6=96=AD,=20=E8=A7=A3=E5=86=B3=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E9=80=A0=E6=88=90=E7=9A=84=E4=B8=80=E4=BA=9B=E9=9A=90?= =?UTF-8?q?=E8=97=8F=E9=97=AE=E9=A2=98.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/html/login/action.html | 41 +++++++++++++---------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/lualib/admin/html/login/action.html b/lualib/admin/html/login/action.html index e6d3ec57..d71156e5 100644 --- a/lualib/admin/html/login/action.html +++ b/lualib/admin/html/login/action.html @@ -3,31 +3,28 @@ var form = layui.form; //监听提交 form.on('submit(login)', function(data){ - $('#sub').hide(); - $.ajax({ - url: "{*login_api*}", - type: "POST", - data: data.field, - success: function (res) { - if (res.code != 0) { - return layer.msg(res.msg, { time: 2000 }, function () { - $('#sub').show(); - }); - } - return layer.msg(res.msg, { time: 1000 }, function () { - window.location.href = res.url + '?token=' + res.token - }); - }, - error: function (res) { - return layer.msg("请求失败", { time: 2000}, function () { - $('#sub').show(); - }); - } - }) + // 判断登录按钮是否隐藏, 隐藏之后不允许重复提交登录. + if (!$('#sub').is(":hidden")) { + $('#sub').hide(); + $.ajax({ + url: "{*login_api*}", + type: "POST", + data: data.field, + success: function (res) { + if (res.code != 0) + // 登录失败 + return layer.msg(res.msg, {time: 2000}, function(){ return $('#sub').show(); }); + // 登录成功 + return layer.msg(res.msg, {time: 1000}, function(){ return window.location.href = res.url + '?token=' + res.token; }); + }, + error: function(res) { // 网络请求失败 + return layer.msg("请求失败", {time: 2000}, () => $('#sub').show()); + }, + }) + } return false; }); }); - // console.log(window.self === window.top); if (self && top && self != top) { parent.window.location.reload(); } From e80ee19c2254a2a0b96703b9b3070d7c5123884e Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 5 Sep 2019 15:21:09 +0800 Subject: [PATCH 347/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E4=BD=93=E9=AA=8C=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/html/system/header/header-add.html | 39 ++++---- .../admin/html/system/header/header-edit.html | 36 ++++---- lualib/admin/html/system/menu/menu-add.html | 38 ++++---- lualib/admin/html/system/menu/menu-edit.html | 40 +++++---- lualib/admin/html/system/role/role-add.html | 80 +++++++++-------- lualib/admin/html/system/role/role-edit.html | 90 ++++++++++--------- lualib/admin/html/system/user/user-add.html | 5 +- lualib/admin/html/system/user/user-edit.html | 38 ++++---- 8 files changed, 190 insertions(+), 176 deletions(-) diff --git a/lualib/admin/html/system/header/header-add.html b/lualib/admin/html/system/header/header-add.html index 86f75f94..5a0dce66 100644 --- a/lualib/admin/html/system/header/header-add.html +++ b/lualib/admin/html/system/header/header-add.html @@ -61,31 +61,32 @@
                  diff --git a/lualib/admin/html/system/user/user-edit.html b/lualib/admin/html/system/user/user-edit.html index 0e84aaeb..c8ce8ec9 100644 --- a/lualib/admin/html/system/user/user-edit.html +++ b/lualib/admin/html/system/user/user-edit.html @@ -117,26 +117,28 @@ var {form, layer} = layui; //监听提交 form.on('submit(edit)', function(data) { - $('#submit').hide(); - $.ajax({ - url: "{*api_url*}", type: "POST", headers: {token:"{*token*}"}, data: data.field, - error: function (res) { - return layer.msg('请求失败', {time:2000}, function () { - $('#submit').show(); - }); - }, - success: function (res) { - if (res.code != 0) { - return layer.msg(res.msg, {time:2000}, function () { + if (!$('#submit').is(":hidden")) { + $('#submit').hide(); + $.ajax({ + url: "{*api_url*}", type: "POST", headers: {token:"{*token*}"}, data: data.field, + error: function (res) { + return layer.msg('请求失败', {time:2000}, function () { $('#submit').show(); }); - } - return layer.msg(res.msg, {time:1000}, function () { - xadmin.close(); - xadmin.father_reload(); - }); - }, - }) + }, + success: function (res) { + if (res.code != 0) { + return layer.msg(res.msg, {time:2000}, function () { + $('#submit').show(); + }); + } + return layer.msg(res.msg, {time:1000}, function () { + xadmin.close(); + xadmin.father_reload(); + }); + }, + }) + } return false; }); From bec1e6b934da554b41f66037d30332f7f31259ee Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 5 Sep 2019 20:29:09 +0800 Subject: [PATCH 348/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E7=9A=84=E5=AD=97=E6=AE=B5=E9=95=BF=E5=BA=A6?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/db/database.sql | 2 +- lualib/admin/db/database.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/db/database.sql b/docker/db/database.sql index 98487cb9..e53f8a54 100644 --- a/docker/db/database.sql +++ b/docker/db/database.sql @@ -26,7 +26,7 @@ CREATE TABLE IF NOT EXISTS `cfadmin_headers` ( `name` varchar(255) NOT NULL COMMENT '头部名称', `url` varchar(255) NOT NULL COMMENT '头部URL', `create_at` int(11) unsigned NOT NULL COMMENT '创建时间', - `update_at` int(10) unsigned NOT NULL COMMENT '修改时间', + `update_at` int(11) unsigned NOT NULL COMMENT '修改时间', `active` tinyint(4) unsigned NOT NULL COMMENT '删除标志', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/lualib/admin/db/database.sql b/lualib/admin/db/database.sql index 98487cb9..e53f8a54 100644 --- a/lualib/admin/db/database.sql +++ b/lualib/admin/db/database.sql @@ -26,7 +26,7 @@ CREATE TABLE IF NOT EXISTS `cfadmin_headers` ( `name` varchar(255) NOT NULL COMMENT '头部名称', `url` varchar(255) NOT NULL COMMENT '头部URL', `create_at` int(11) unsigned NOT NULL COMMENT '创建时间', - `update_at` int(10) unsigned NOT NULL COMMENT '修改时间', + `update_at` int(11) unsigned NOT NULL COMMENT '修改时间', `active` tinyint(4) unsigned NOT NULL COMMENT '删除标志', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; From 7120c9faf83f3715afd550e6444781ac14e06d19 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 8 Sep 2019 09:40:17 +0800 Subject: [PATCH 349/956] =?UTF-8?q?=E8=A1=A5=E5=85=85=E4=B8=80=E4=BA=9Bhtt?= =?UTF-8?q?p=E5=8D=8F=E8=AE=AE=E7=9A=84=E5=B8=B8=E8=A7=81MIME?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http.lua | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index 836b79fc..f1493a7c 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -152,16 +152,45 @@ local MIME = { -- 音频 ['wav'] = 'audio/wav', ['acc'] = 'audio/aac', + ['mp3'] = 'audio/mpeg', + ['ogg'] = 'audio/ogg', -- 视频 ['avi'] = 'video/x-msvideo', + ['mpa'] = 'video/mpeg', + ['mpe'] = 'video/mpeg', + ['mp2'] = 'video/mpeg', + ['mpe'] = 'video/mpeg', ['mpeg'] = 'video/mpeg', + ['mp4'] = 'audio/mp4', + ['qt'] = 'video/mpeg', + ['mov'] = 'video/mpeg', + ['webm'] = 'video/webm', + ['flv'] = 'video/x-flv', + ['m4v'] = 'video/x-m4v', + ['3gp'] = 'video/3gpp', + ['3gpp'] = 'video/3gpp', + ['ts'] = 'video/mp2t', -- 文档 - ['csv'] = 'text/csv', + ['dot'] = 'application/msword', + ['doc'] = 'application/msword', ['xls'] = 'application/vnd.ms-excel', + ['ppt'] = 'application/vnd.ms-powerpoint', + ['csv'] = 'text/csv', ['xlsx'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - ['docx'] = 'application/msword', - ['doc'] = 'application/msword', + ['docx'] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + ['pptx'] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation', ['pdf'] = 'application/pdf', + -- 密匙 + ['der'] = 'application/x-x509-ca-cert', + ['pem'] = 'application/x-x509-ca-cert', + ['crt'] = 'application/x-x509-ca-cert', + -- 压缩文件 + ['7z'] = 'application/x-7z-compressed', + ['gz'] = 'application/x-gzip', + ['tar'] = 'application/x-tar', + ['zip'] = 'application/zip', + ['tgz'] = 'application/x-compressed', + ['rar'] = 'application/x-rar-compressed', -- TODO } From c56108e6a7afd13b159d0106ec2d3063274bd413 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 8 Sep 2019 10:41:42 +0800 Subject: [PATCH 350/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=B0=E5=AF=8C?= =?UTF-8?q?=E7=9A=84httpd=E5=86=85=E7=BD=AEmime?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http.lua | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index f1493a7c..30f4513d 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -135,11 +135,13 @@ local MIME = { ['xml'] = 'application/xml', ['htm'] = 'text/html', ['html'] = 'text/html', + ['shtml'] = 'text/html', ['xhtml'] = 'application/xhtml+xml', ['txt'] = 'text/plain', ['css'] = 'text/css', - ['js'] = 'application/javascript', ['json'] = 'application/json', + ['rtf'] = 'application/rtf', + ['ogx'] = 'application/ogg', -- 图片格式 ['bmp'] = 'image/bmp', ['png'] = 'image/png', @@ -149,11 +151,13 @@ local MIME = { ['ico'] = 'image/x-icon', ['tif'] = 'image/tiff', ['tiff'] = 'image/tiff', + ['svg'] = 'image/svg+xml', -- 音频 + ['au'] = 'audio/basic', ['wav'] = 'audio/wav', ['acc'] = 'audio/aac', ['mp3'] = 'audio/mpeg', - ['ogg'] = 'audio/ogg', + ['oga'] = 'audio/ogg', -- 视频 ['avi'] = 'video/x-msvideo', ['mpa'] = 'video/mpeg', @@ -170,27 +174,51 @@ local MIME = { ['3gp'] = 'video/3gpp', ['3gpp'] = 'video/3gpp', ['ts'] = 'video/mp2t', + ['ogv'] = 'video/ogg', -- 文档 + ['csv'] = 'text/csv', ['dot'] = 'application/msword', ['doc'] = 'application/msword', + ['mdb'] = 'application/x-msaccess', ['xls'] = 'application/vnd.ms-excel', ['ppt'] = 'application/vnd.ms-powerpoint', - ['csv'] = 'text/csv', + ['chm'] = 'application/vnd.ms-htmlhelp', ['xlsx'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', ['docx'] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', ['pptx'] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation', ['pdf'] = 'application/pdf', -- 密匙 ['der'] = 'application/x-x509-ca-cert', + ['cer'] = 'application/x-x509-ca-cert', ['pem'] = 'application/x-x509-ca-cert', ['crt'] = 'application/x-x509-ca-cert', + ['p10'] = 'application/pkcs10', + ['p12'] = 'application/x-pkcs12', + ['pfx'] = 'application/x-pkcs12', + ['p7c'] = 'application/x-pkcs7-mime', + ['p7m'] = 'application/x-pkcs7-mime', + ['p7b'] = 'application/x-pkcs7-certificates', + ['spc'] = 'application/x-pkcs7-certificates', + ['p7r'] = 'application/x-pkcs7-certreqresp', + ['p7s'] = 'application/x-pkcs7-signature', -- 压缩文件 ['7z'] = 'application/x-7z-compressed', + ['zip'] = 'application/zip', ['gz'] = 'application/x-gzip', ['tar'] = 'application/x-tar', - ['zip'] = 'application/zip', + ['gtar'] = 'application/x-gtar', ['tgz'] = 'application/x-compressed', ['rar'] = 'application/x-rar-compressed', + -- 源文件 + ['c'] = 'text/plain', + ['cxx'] = 'text/plain', + ['cpp'] = 'text/plain', + ['h'] = 'text/plain', + ['hpp'] = 'text/plain', + ['py'] = 'text/plain', + ['cs'] = 'text/plain', + ['lua'] = 'text/plain', + ['js'] = 'application/javascript', -- TODO } From c5c29d7ef7371de98d429891fbc77eea7c7418ac Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 18 Sep 2019 07:02:12 +0800 Subject: [PATCH 351/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0User-Agent=E4=B8=8EHo?= =?UTF-8?q?st?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/protocol.lua | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua index a28a7558..7e03d0f1 100644 --- a/lualib/httpc/protocol.lua +++ b/lualib/httpc/protocol.lua @@ -212,11 +212,11 @@ end local function build_get_req (opt) local request = { fmt("GET %s HTTP/1.1", opt.path), - fmt("Host: %s", opt.domain..':'..opt.port), + fmt("Host: %s", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port), 'Accept: */*', - 'Accept-encoding: identity', + 'Accept-Encoding: identity', fmt("Connection: keep-alive"), - fmt("User-agent: %s", opt.server), + fmt("User-Agent: %s", opt.server), } if type(opt.args) == "table" then local args = {} @@ -240,11 +240,11 @@ end local function build_post_req (opt) local request = { fmt("POST %s HTTP/1.1\r\n", opt.path), - fmt("Host: %s\r\n", opt.domain..':'..opt.port), + fmt("Host: %s", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port), 'Accept: */*\r\n', - 'Accept-encoding: identity\r\n', + 'Accept-Encoding: identity\r\n', 'Connection: keep-alive\r\n', - fmt("User-agent: %s\r\n", opt.server), + fmt("User-Agent: %s\r\n", opt.server), 'Content-Type: application/x-www-form-urlencoded\r\n', } if type(opt.headers) == "table" then @@ -274,11 +274,11 @@ end local function build_json_req (opt) local request = { fmt("POST %s HTTP/1.1", opt.path), - fmt("Host: %s", opt.domain..':'..opt.port), + fmt("Host: %s", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port), 'Accept: */*', - 'Accept-encoding: identity', + 'Accept-Encoding: identity', "Connection: keep-alive", - fmt("User-agent: %s", opt.server), + fmt("User-Agent: %s", opt.server), fmt("Content-length: %s", #opt.json), 'Content-Type: application/json', } @@ -296,7 +296,7 @@ end local function build_file_req (opt) local request = { fmt("POST %s HTTP/1.1\r\n", opt.path), - fmt("Host: %s\r\n", opt.domain..':'..opt.port), + fmt("Host: %s", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port), 'Accept: */*\r\n', 'Accept-Encoding: identity\r\n', fmt("Connection: keep-alive\r\n"), From d457fa15d52d121043258fee1da8c2d0c9f9857b Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 18 Sep 2019 09:38:18 +0800 Subject: [PATCH 352/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0httpc=E5=BA=93?= =?UTF-8?q?=E7=9A=84DELETE=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/class.lua | 61 +++++++++++++++++++++++++++++++++++++++ lualib/httpc/init.lua | 32 ++++++++++++++++++++ lualib/httpc/protocol.lua | 39 ++++++++++++++++++++----- 3 files changed, 124 insertions(+), 8 deletions(-) diff --git a/lualib/httpc/class.lua b/lualib/httpc/class.lua index 4f41628f..ab9d6351 100644 --- a/lualib/httpc/class.lua +++ b/lualib/httpc/class.lua @@ -14,6 +14,7 @@ local build_get_req = protocol.build_get_req local build_post_req = protocol.build_post_req local build_json_req = protocol.build_json_req local build_file_req = protocol.build_file_req +local build_delete_req = protocol.build_delete_req local type = type local assert = assert @@ -166,6 +167,66 @@ function httpc:post (domain, headers, body, timeout) return code, msg end +-- delete 请求 +function httpc:delete (domain, headers, body, timeout) + local opt, err = splite_protocol(domain) + if not opt then + return nil, err + end + + if self.domain and self.domain ~= opt.domain then + return nil, "1. 不同的域名不可使用httpc对象来请求" + end + + if self.port and self.port ~= opt.port then + return nil, "2. 不同的域名不可使用httpc对象来请求" + end + + opt.body = body + opt.headers = headers + opt.server = self.server + + self.domain = opt.domain + self.port = opt.port + + local REQ = build_delete_req(opt) + + if not self.sock then + local sock = sock_new():timeout(self.timeout) + local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) + if not ok then + sock:close() + return ok, err + end + self.sock = sock + end + + local ok, err = sock_send(self.sock, opt.protocol, REQ) + if not ok then + self.sock:close() + self.sock = nil + local sock = sock_new():timeout(self.timeout) + local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) + if not ok then + sock:close() + return ok, err + end + ok, err = sock_send(sock, opt.protocol, REQ) + if not ok then + sock:close() + return nil, err + end + self.sock = sock + end + + local code, msg = httpc_response(self.sock, opt.protocol) + if not code then + self.sock:close() + self.sock = nil + end + return code, msg +end + -- json 请求 function httpc:json (domain, headers, json, timeout) diff --git a/lualib/httpc/init.lua b/lualib/httpc/init.lua index cecc4421..89af511c 100644 --- a/lualib/httpc/init.lua +++ b/lualib/httpc/init.lua @@ -19,6 +19,7 @@ local build_get_req = protocol.build_get_req local build_post_req = protocol.build_post_req local build_json_req = protocol.build_json_req local build_file_req = protocol.build_file_req +local build_delete_req = protocol.build_delete_req local type = type local assert = assert @@ -104,6 +105,36 @@ local function post(domain, headers, body, timeout) return code, msg end +-- HTTP DELETE +local function delete(domain, headers, body, timeout) + + local opt, err = splite_protocol(domain) + if not opt then + return nil, err + end + + opt.body = body + opt.headers = headers + opt.server = SERVER + + local REQ = build_delete_req(opt) + + local sock = sock_new():timeout(timeout or __TIMEOUT__) + local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) + if not ok then + sock:close() + return ok, err + end + local ok, err = sock_send(sock, opt.protocol, REQ) + if not ok then + sock:close() + return ok, err + end + local code, msg = httpc_response(sock, opt.protocol) + sock:close() + return code, msg +end + local function json(domain, headers, json, timeout) local opt, err = splite_protocol(domain) @@ -215,6 +246,7 @@ end return { get = get, post = post, + delete = delete, json = json, file = file, multi_request = multi_request, diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua index 7e03d0f1..53d03c83 100644 --- a/lualib/httpc/protocol.lua +++ b/lualib/httpc/protocol.lua @@ -228,7 +228,7 @@ local function build_get_req (opt) end if type(opt.headers) == "table" then for _, header in ipairs(opt.headers) do - assert(lower(header[1]) ~= 'content-length', "please don't give Content-Length") + assert(lower(header[1]) ~= 'Content-Length', "please don't give Content-Length") assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") insert(request, header[1]..': '..header[2]) end @@ -249,7 +249,7 @@ local function build_post_req (opt) } if type(opt.headers) == "table" then for _, header in ipairs(opt.headers) do - assert(lower(header[1]) ~= 'content-length', "please don't give Content-Length") + assert(lower(header[1]) ~= 'Content-Length', "please don't give Content-Length") assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") insert(request, header[1]..': '..header[2]..CRLF) end @@ -262,15 +262,37 @@ local function build_post_req (opt) insert(body, url_encode(b[1])..'='..url_encode(b[2])) end insert(request, concat(body, "&")) - insert(request, #request - 2, fmt("Content-length: %s\r\n", #request[#request])) + insert(request, #request - 2, fmt("Content-Length: %s\r\n", #request[#request])) end if type(opt.body) == "string" then - insert(request, #request, fmt("Content-length: %s\r\n", #opt.body)) + insert(request, #request, fmt("Content-Length: %s\r\n", #opt.body)) insert(request, opt.body) end return concat(request) end +local function build_delete_req (opt) + local request = { + fmt("DELETE %s HTTP/1.1", opt.path), + fmt("Host: %s", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port), + fmt("User-Agent: %s", opt.server), + 'Accept: */*', + 'Accept-Encoding: identity', + "Connection: keep-alive", + } + if type(opt.headers) == "table" then + for _, header in ipairs(opt.headers) do + assert(lower(header[1]) ~= 'Content-Length', "please don't give Content-Length") + assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") + insert(request, header[1]..': '..header[2]) + end + if opt.body then + insert(request, fmt("Content-Length: %s", #opt.body)) + end + end + return concat(request, CRLF) .. CRLF2 .. ( opt.body or '') +end + local function build_json_req (opt) local request = { fmt("POST %s HTTP/1.1", opt.path), @@ -279,12 +301,12 @@ local function build_json_req (opt) 'Accept-Encoding: identity', "Connection: keep-alive", fmt("User-Agent: %s", opt.server), - fmt("Content-length: %s", #opt.json), + fmt("Content-Length: %s", #opt.json), 'Content-Type: application/json', } if type(opt.headers) == "table" then for _, header in ipairs(opt.headers) do - assert(lower(header[1]) ~= 'content-length', "please don't give Content-Length") + assert(lower(header[1]) ~= 'Content-Length', "please don't give Content-Length") assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") insert(request, header[1]..': '..header[2]) end @@ -305,7 +327,7 @@ local function build_file_req (opt) if type(opt.headers) == "table" then for _, header in ipairs(opt.headers) do - assert(lower(header[1]) ~= 'content-length', "please don't give Content-Length") + assert(lower(header[1]) ~= 'Content-Length', "please don't give Content-Length") assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") insert(request, header[1]..': '..header[2]..'\r\n') end @@ -328,7 +350,7 @@ local function build_file_req (opt) insert(body, file.file) end body = concat(body, CRLF) - insert(request, fmt("Content-length: %s\r\n", #body + 2 + #boundary_end)) + insert(request, fmt("Content-Length: %s\r\n", #body + 2 + #boundary_end)) insert(request, CRLF) insert(request, body) insert(request, CRLF) @@ -348,4 +370,5 @@ return { build_post_req = build_post_req, build_json_req = build_json_req, build_file_req = build_file_req, + build_delete_req = build_delete_req, } From 2d0d7a12e7793840e1dc50c08e36ed71c0bea76c Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 18 Sep 2019 11:24:23 +0800 Subject: [PATCH 353/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=83=E7=89=9B?= =?UTF-8?q?=E4=BA=91=E7=9A=84=E7=9F=AD=E4=BF=A1=E6=9C=8D=E5=8A=A1=E4=B8=8E?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1=E5=AD=98=E5=82=A8=E6=9C=8D=E5=8A=A1=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/cloud/qiniu/oss.lua | 66 +++++++++++ lualib/cloud/qiniu/sms.lua | 227 +++++++++++++++++++++++++++++++++++++ 2 files changed, 293 insertions(+) create mode 100644 lualib/cloud/qiniu/oss.lua create mode 100644 lualib/cloud/qiniu/sms.lua diff --git a/lualib/cloud/qiniu/oss.lua b/lualib/cloud/qiniu/oss.lua new file mode 100644 index 00000000..a9fcd2b1 --- /dev/null +++ b/lualib/cloud/qiniu/oss.lua @@ -0,0 +1,66 @@ +local crypt = require "crypt" +local json = require "json" + +local type = type + +local os_time = os.time +local find = string.find +local toint = math.tointeger + + +local oss = { __Version__ = 0.1 } +--[[ +此为七牛云对象存储服务的上传与下载Token生成库的原生lua实现. +此库实现了服务端根据指定算法生成临时上传/下载的授权Token后交由客户端上传文件, 服务端不负责具体业务. +具体使用方法请参考: https://developer.qiniu.com/kodo/manual/1644/security +]] + +-- 检查url是否合法 +local function check_domain (domain) + if type(domain) == 'string' and find(domain, "http[s]?://([%w]+)") then + return domain + end +end + +function oss.getUploadToken (AccessKey, SecretKey, roles) + if type(AccessKey) ~= 'string' or AccessKey == '' then + return nil, "invaild AccessKey." + end + if type(SecretKey) ~= 'string' or SecretKey == '' then + return nil, "invaild SecretKey." + end + if type(roles) ~= 'table' or not next(roles) then + return nil, "invaild roles." + end + if type(roles.bucket) ~= 'string' or roles.bucket == '' then + return nil, "invaild roles.bucket." + end + local Policy = { + scope = table.concat({roles.bucket, roles.prefix}, ':'), -- 上传可自行是否加入上传前缀 + deadline = toint(roles.deadline) or os_time() + 180, -- 默认超时时间为3分钟 + callbackUrl = check_domain(roles.callbackurl), -- 回调地址 + callbackBody = check_domain(roles.callbackbody), -- 回调地址内容 + insertOnly = toint(roles.insertonly) == 1 and 1 or 0, -- 是否可以写入覆盖 + fsizeMin = toint(roles.fsizemin), -- 限定上传文件大小最小值(单位Byte) + fsizeLimit = toint(roles.fsizeLimit), -- 限定上传文件大小最大值(单位Byte), 超过限制上传文件大小的最大值会返回 413 状态码. + fileType = toint(roles.filetype) ~= 0 and 1 or 0, -- 文件存储类型(0 为普通存储,1 为低频存储) 默认为: 0 + mimeLimit = type(roles.mimeLimit) == 'string' and roles.mimeLimit or nil, -- 限制文件类型 + } + local encodedPutPolicy = crypt.base64encode(json.encode(Policy)) + return AccessKey .. ':' .. crypt.base64encode(crypt.hmac_sha1(SecretKey, encodedPutPolicy)) .. ':' .. encodedPutPolicy +end + +function oss.getDownloadToken (AccessKey, SecretKey, opt) + if type(AccessKey) ~= 'string' or AccessKey == '' then + return nil, "invaild AccessKey." + end + if type(SecretKey) ~= 'string' or SecretKey == '' then + return nil, "invaild SecretKey." + end + if type(opt) ~= 'table' or not opt.url or not toint(opt.expires) then + return nil, "invaild roles." + end + return table.concat({ opt.url .. '?'..'e='.. opt.expires, 'token='.. AccessKey .. ':' .. crypt.base64encode(crypt.hmac_sha1(SecretKey, opt.url)) }, '&') +end + +return oss diff --git a/lualib/cloud/qiniu/sms.lua b/lualib/cloud/qiniu/sms.lua new file mode 100644 index 00000000..866ec5d9 --- /dev/null +++ b/lualib/cloud/qiniu/sms.lua @@ -0,0 +1,227 @@ +local httpc = require "httpc" +local crypt = require "crypt" +local json = require "json" + +local type = type + +local sms = { __Version__ = 0.1 } +--[[ +此为七牛云短信服务的lua版实现. +提供了包含短信的发送/记录查询/获取模板/删除模板/获取签名/删除签名/ +]] + +-- 生成管理凭证 +function sms.newAuthorization (AccessKey, SecretKey, opt) + local auth = opt.method .. ' ' .. opt.path + if opt.query then + auth = auth .. opt.query + end + auth = auth .. '\nHost: ' .. opt.host + if opt.body then + auth = auth .. '\nContent-Type: application/json\n\n' .. opt.body + else + auth = auth .. '\n\n' + end + return 'Qiniu'.. ' ' .. AccessKey .. ':' .. crypt.base64encode(crypt.hmac_sha1(SecretKey, auth)) +end + +-- 获取短信模板列表 +function sms.getTemplates (AccessKey, SecretKey, opt) + if type(AccessKey) ~= 'string' or AccessKey == '' then + return nil, "invaild AccessKey." + end + if type(SecretKey) ~= 'string' or SecretKey == '' then + return nil, "invaild SecretKey." + end + local args + if opt then + args = {} + if opt.page then + args[#args+1] = 'page=' .. opt.page + end + if opt.page_size then + args[#args+1] = 'page_size=' .. opt.page_size + end + if #args == 0 then + args = nil + end + end + local Authorization = sms.newAuthorization(AccessKey, SecretKey, { method = 'GET', host = 'sms.qiniuapi.com', path = '/v1/template', query = args and '?' .. table.concat(args, '&') or nil }) + local code, ret = httpc.get('https://sms.qiniuapi.com/v1/template'..(args and '?'..table.concat(args, '&') or ''), {{ 'Authorization', Authorization }}) + if code ~= 200 then + return nil, ret + end + return code, json.decode(ret) +end + +-- 删除短信模板列表 +function sms.detTemplate (AccessKey, SecretKey, template_id) + if type(AccessKey) ~= 'string' or AccessKey == '' then + return nil, "invaild AccessKey." + end + if type(SecretKey) ~= 'string' or SecretKey == '' then + return nil, "invaild SecretKey." + end + if type(template_id) ~= 'string' or template_id == '' then + return nil, "invaild template_id." + end + local Authorization = sms.newAuthorization(AccessKey, SecretKey, { method = 'DELETE', host = 'sms.qiniuapi.com', path = '/v1/template/' .. template_id }) + local code, ret = httpc.delete("https://sms.qiniuapi.com" .. '/v1/template/' .. template_id, {{'Authorization', Authorization}}) + if code ~= 200 then + return nil, ret + end + return code, ret +end + +-- 获取短信签名列表 +function sms.getSignatures (AccessKey, SecretKey, opt) + if type(AccessKey) ~= 'string' or AccessKey == '' then + return nil, "invaild AccessKey." + end + if type(SecretKey) ~= 'string' or SecretKey == '' then + return nil, "invaild SecretKey." + end + local args + if opt then + args = {} + if opt.page then + args[#args+1] = 'page=' .. opt.page + end + if opt.page_size then + args[#args+1] = 'page_size=' .. opt.page_size + end + if #args == 0 then + args = nil + end + end + local Authorization = sms.newAuthorization(AccessKey, SecretKey, { method = 'GET', host = 'sms.qiniuapi.com', path = '/v1/signature', query = args and '?' .. table.concat(args, '&') or nil }) + local code, ret = httpc.get('https://sms.qiniuapi.com/v1/signature'..(args and '?'..table.concat(args, '&') or ''), {{ 'Authorization', Authorization }}) + if code ~= 200 then + return nil, ret + end + return code, json.decode(ret) +end + +-- 删除短信签名列表 +function sms.delSignature (AccessKey, SecretKey, signature_id) + if type(AccessKey) ~= 'string' or AccessKey == '' then + return nil, "invaild AccessKey." + end + if type(SecretKey) ~= 'string' or SecretKey == '' then + return nil, "invaild SecretKey." + end + if type(signature_id) ~= 'string' or signature_id == '' then + return nil, "invaild signature_id." + end + local Authorization = sms.newAuthorization(AccessKey, SecretKey, { method = 'DELETE', host = 'sms.qiniuapi.com', path = '/v1/signature/' .. signature_id }) + local code, ret = httpc.delete("https://sms.qiniuapi.com" .. '/v1/signature/' .. signature_id, {{'Authorization', Authorization}}) + if code ~= 200 then + return nil, ret + end + return code, json.decode(ret) +end + +-- 获取短信发送记录 +function sms.getSMSRecord (AccessKey, SecretKey, opt) + if type(AccessKey) ~= 'string' or AccessKey == '' then + return nil, "invaild AccessKey." + end + if type(SecretKey) ~= 'string' or SecretKey == '' then + return nil, "invaild SecretKey." + end + local args + if opt then + args = {} + if opt.page then + args[#args+1] = 'page=' .. opt.page + end + if opt.page_size then + args[#args+1] = 'page_size=' .. opt.page_size + end + if opt.mobile then + args[#args+1] = 'mobile=' .. opt.mobile + end + if opt.status then + args[#args+1] = 'status=' .. opt.status + end + if opt.start then + args[#args+1] = 'start=' .. opt.start + end + if opt['end'] then + args[#args+1] = 'end=' .. opt['end'] + end + if opt.type then + args[#args+1] = 'type=' .. opt.type + end + if opt.job_id then + args[#args+1] = 'job_id=' .. opt.job_id + end + if opt.message_id then + args[#args+1] = 'message_id=' .. opt.message_id + end + if opt.template_id then + args[#args+1] = 'template_id=' .. opt.template_id + end + if #args == 0 then + args = nil + end + end + local Authorization = sms.newAuthorization(AccessKey, SecretKey, { method = 'GET', host = 'sms.qiniuapi.com', path = '/v1/messages', query = args and '?' .. table.concat(args, '&') or nil }) + local code, ret = httpc.get('https://sms.qiniuapi.com/v1/messages'..(args and '?'..table.concat(args, '&') or ''), {{ 'Authorization', Authorization}}) + if code ~= 200 then + return nil, ret + end + return code, json.decode(ret) +end + +-- 发送国内短信 +function sms.sendSMS (AccessKey, SecretKey, opt) + if type(AccessKey) ~= 'string' or AccessKey == '' then + return nil, "invaild AccessKey." + end + if type(SecretKey) ~= 'string' or SecretKey == '' then + return nil, "invaild SecretKey." + end + if type(opt) ~= 'table' or not next(opt) then + return nil, "invaild opt arguments." + end + if type(opt.template_id) ~= 'string' or type(opt.mobiles) ~= 'table' then + return nil, "invaild template_id or mobiles." + end + local body = json.encode({ mobiles = opt.mobiles, template_id = opt.template_id, parameters = opt.parameters }) + local Authorization = sms.newAuthorization(AccessKey, SecretKey, { + method = 'POST', host = 'sms.qiniuapi.com', path = '/v1/message', body = body, + }) + local code, ret = httpc.json('https://sms.qiniuapi.com/v1/message', { { 'Authorization', Authorization } }, body) + if code ~= 200 then + return nil, ret + end + return code, json.decode(ret) +end + +-- 发送国际短信 +function sms.sendOverseaSMS (AccessKey, SecretKey, opt) + if type(AccessKey) ~= 'string' or AccessKey == '' then + return nil, "invaild AccessKey." + end + if type(SecretKey) ~= 'string' or SecretKey == '' then + return nil, "invaild SecretKey." + end + if type(opt) ~= 'table' or not next(opt) then + return nil, "invaild opt arguments." + end + if type(opt.template_id) ~= 'string' or type(opt.mobile) ~= 'string' then + return nil, "invaild template_id or mobiles." + end + local body = json.encode({ mobile = opt.mobile, template_id = opt.template_id, parameters = opt.parameters }) + local Authorization = sms.newAuthorization(AccessKey, SecretKey, { + method = 'POST', host = 'sms.qiniuapi.com', path = '/v1/message/oversea', body = body, + }) + local code, ret = httpc.json('https://sms.qiniuapi.com/v1/message/oversea', { { 'Authorization', Authorization } }, body) + if code ~= 200 then + return nil, ret + end + return code, json.decode(ret) +end + +return sms From 99526aaabb7c9a0545315ee7fd77402205d6abc8 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 18 Sep 2019 11:32:10 +0800 Subject: [PATCH 354/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=83=E7=89=9B?= =?UTF-8?q?=E4=BA=91API=E5=BA=93=E7=9A=84=E6=B5=8B=E8=AF=95=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_qiniu.lua | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 script/test_qiniu.lua diff --git a/script/test_qiniu.lua b/script/test_qiniu.lua new file mode 100644 index 00000000..5ac05f99 --- /dev/null +++ b/script/test_qiniu.lua @@ -0,0 +1,31 @@ +local sms = require "cloud.qiniu.sms" +local oss = require "cloud.qiniu.oss" + +local AccessKey = 'Your_Real_AccessKey' +local SecretKey = 'Your_Real_SecretKey' + + +-- local ok, err = sms.sendSMS(AccessKey, SecretKey, { template_id = '1174104085482180608', mobiles = {'+8613000000000'}, parameters = { code = tostring(math.random(1, 1024)) }}) +-- require"logging":DEBUG(ok, err) +-- +-- local ok, err = sms.getTemplates(AccessKey, SecretKey, { page = 1, page_size = 100 }) +-- require"logging":DEBUG(ok, err) +-- +-- local ok, err = sms.getSignatures(AccessKey, SecretKey, { page = 1, page_size = 90 }) +-- require"logging":DEBUG(ok, err) +-- +-- local ok, err = sms.getSMSRecord(AccessKey, SecretKey, { start = '1568131200', ['end'] = '1568822399'}) +-- require"logging":DEBUG(ok, err) +-- +-- local ok, err = sms.detTemplate(AccessKey, SecretKey, '1174104085482180608') +-- require"logging":DEBUG(ok, err) +-- +-- local ok, err = sms.delSignature(AccessKey, SecretKey, '1174104085482180608') +-- require"logging":DEBUG(ok, err) + + +-- local upToken = oss.getUploadToken(AccessKey, SecretKey, { bucket = 'candymi' }) +-- require"logging":DEBUG(upToken) +-- +-- local upToken = oss.getDownloadToken(AccessKey, SecretKey, { url = 'https://www.gitub.com/a.img', expires = os.time() + 180 }) +-- require"logging":DEBUG(upToken) From ef72b9b1bd4bc2345a4a2d278cfe2de758c4e69f Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 18 Sep 2019 12:32:41 +0800 Subject: [PATCH 355/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=B8=80=E4=BA=9Bhtt?= =?UTF-8?q?pc=E6=96=B9=E6=B3=95=E5=AF=B9=E5=A4=B4=E9=83=A8=E4=BF=A1?= =?UTF-8?q?=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/class.lua | 61 +++++++++++++++++++++++++++++++++++++++ lualib/httpc/init.lua | 32 ++++++++++++++++++++ lualib/httpc/protocol.lua | 27 +++++++++++++++-- 3 files changed, 117 insertions(+), 3 deletions(-) diff --git a/lualib/httpc/class.lua b/lualib/httpc/class.lua index ab9d6351..5f15a13a 100644 --- a/lualib/httpc/class.lua +++ b/lualib/httpc/class.lua @@ -14,6 +14,7 @@ local build_get_req = protocol.build_get_req local build_post_req = protocol.build_post_req local build_json_req = protocol.build_json_req local build_file_req = protocol.build_file_req +local build_put_req = protocol.build_put_req local build_delete_req = protocol.build_delete_req local type = type @@ -227,6 +228,66 @@ function httpc:delete (domain, headers, body, timeout) return code, msg end +-- put 请求 +function httpc:put (domain, headers, body, timeout) + local opt, err = splite_protocol(domain) + if not opt then + return nil, err + end + + if self.domain and self.domain ~= opt.domain then + return nil, "1. 不同的域名不可使用httpc对象来请求" + end + + if self.port and self.port ~= opt.port then + return nil, "2. 不同的域名不可使用httpc对象来请求" + end + + opt.body = body + opt.headers = headers + opt.server = self.server + + self.domain = opt.domain + self.port = opt.port + + local REQ = build_put_req(opt) + + if not self.sock then + local sock = sock_new():timeout(self.timeout) + local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) + if not ok then + sock:close() + return ok, err + end + self.sock = sock + end + + local ok, err = sock_send(self.sock, opt.protocol, REQ) + if not ok then + self.sock:close() + self.sock = nil + local sock = sock_new():timeout(self.timeout) + local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) + if not ok then + sock:close() + return ok, err + end + ok, err = sock_send(sock, opt.protocol, REQ) + if not ok then + sock:close() + return nil, err + end + self.sock = sock + end + + local code, msg = httpc_response(self.sock, opt.protocol) + if not code then + self.sock:close() + self.sock = nil + end + return code, msg +end + -- json 请求 function httpc:json (domain, headers, json, timeout) diff --git a/lualib/httpc/init.lua b/lualib/httpc/init.lua index 89af511c..d01ab372 100644 --- a/lualib/httpc/init.lua +++ b/lualib/httpc/init.lua @@ -19,6 +19,7 @@ local build_get_req = protocol.build_get_req local build_post_req = protocol.build_post_req local build_json_req = protocol.build_json_req local build_file_req = protocol.build_file_req +local build_put_req = protocol.build_put_req local build_delete_req = protocol.build_delete_req local type = type @@ -135,6 +136,36 @@ local function delete(domain, headers, body, timeout) return code, msg end +-- HTTP PUT +local function put(domain, headers, body, timeout) + + local opt, err = splite_protocol(domain) + if not opt then + return nil, err + end + + opt.body = body + opt.headers = headers + opt.server = SERVER + + local REQ = build_put_req(opt) + + local sock = sock_new():timeout(timeout or __TIMEOUT__) + local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) + if not ok then + sock:close() + return ok, err + end + local ok, err = sock_send(sock, opt.protocol, REQ) + if not ok then + sock:close() + return ok, err + end + local code, msg = httpc_response(sock, opt.protocol) + sock:close() + return code, msg +end + local function json(domain, headers, json, timeout) local opt, err = splite_protocol(domain) @@ -249,5 +280,6 @@ return { delete = delete, json = json, file = file, + put = put, multi_request = multi_request, } diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua index 53d03c83..d0fd3e66 100644 --- a/lualib/httpc/protocol.lua +++ b/lualib/httpc/protocol.lua @@ -240,7 +240,7 @@ end local function build_post_req (opt) local request = { fmt("POST %s HTTP/1.1\r\n", opt.path), - fmt("Host: %s", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port), + fmt("Host: %s\r\n", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port), 'Accept: */*\r\n', 'Accept-Encoding: identity\r\n', 'Connection: keep-alive\r\n', @@ -290,7 +290,7 @@ local function build_delete_req (opt) insert(request, fmt("Content-Length: %s", #opt.body)) end end - return concat(request, CRLF) .. CRLF2 .. ( opt.body or '') + return concat(request, CRLF) .. CRLF2 .. ( opt.body or '' ) end local function build_json_req (opt) @@ -318,7 +318,7 @@ end local function build_file_req (opt) local request = { fmt("POST %s HTTP/1.1\r\n", opt.path), - fmt("Host: %s", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port), + fmt("Host: %s\r\n", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port), 'Accept: */*\r\n', 'Accept-Encoding: identity\r\n', fmt("Connection: keep-alive\r\n"), @@ -359,6 +359,26 @@ local function build_file_req (opt) return concat(request) end +local function build_put_req (opt) + local request = { + fmt("PUT %s HTTP/1.1", opt.path), + fmt("Host: %s", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port), + 'Accept: */*', + 'Accept-Encoding: identity', + fmt("Connection: keep-alive"), + fmt("Content-Length: %s", #opt.body), + fmt("User-Agent: %s", opt.server), + } + if type(opt.headers) == "table" then + for _, header in ipairs(opt.headers) do + assert(lower(header[1]) ~= 'Content-Length', "please don't give Content-Length") + assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") + insert(request, header[1]..': '..header[2]) + end + end + return concat(request, CRLF) .. CRLF2 .. ( opt.body or '' ) +end + return { sock_new = sock_new, sock_recv = sock_recv, @@ -370,5 +390,6 @@ return { build_post_req = build_post_req, build_json_req = build_json_req, build_file_req = build_file_req, + build_put_req = build_put_req, build_delete_req = build_delete_req, } From cea007b6fa445702ca29f43538e99572c8d85472 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 18 Sep 2019 12:33:54 +0800 Subject: [PATCH 356/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_httpc.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/script/test_httpc.lua b/script/test_httpc.lua index fcb8ad62..66c1a097 100644 --- a/script/test_httpc.lua +++ b/script/test_httpc.lua @@ -39,8 +39,8 @@ cf.timeout(3, function ( ... ) -- http 上传文件示例 local code, body = httpc.file(domain..'/api', nil, { - {name='1', filename='1.jpg', file='1', type='abc'}, - {name='2', filename='2.jpg', file='2', type='abc'}, + {name='1', filename='1.jpg', file='1', type='jpg'}, + {name='2', filename='2.jpg', file='2', type='jpg'}, }) print(code, body) @@ -110,8 +110,8 @@ cf.timeout(5, function () method = "file", headers = {{"Auth", "admin"}}, files = { - {name='1', filename='1.jpg', file='1', type='abc'}, - {name='2', filename='2.jpg', file='2', type='abc'}, + {name='1', filename='1.jpg', file='1', type='jpg'}, + {name='2', filename='2.jpg', file='2', type='jpg'}, } } } From 2acd19dade695613487b261ae4d00d6ec92a8aaa Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 19 Sep 2019 09:07:48 +0800 Subject: [PATCH 357/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/protocol.lua | 100 ++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 52 deletions(-) diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua index d0fd3e66..49353c44 100644 --- a/lualib/httpc/protocol.lua +++ b/lualib/httpc/protocol.lua @@ -228,7 +228,6 @@ local function build_get_req (opt) end if type(opt.headers) == "table" then for _, header in ipairs(opt.headers) do - assert(lower(header[1]) ~= 'Content-Length', "please don't give Content-Length") assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") insert(request, header[1]..': '..header[2]) end @@ -245,29 +244,26 @@ local function build_post_req (opt) 'Accept-Encoding: identity\r\n', 'Connection: keep-alive\r\n', fmt("User-Agent: %s\r\n", opt.server), - 'Content-Type: application/x-www-form-urlencoded\r\n', } - if type(opt.headers) == "table" then - for _, header in ipairs(opt.headers) do - assert(lower(header[1]) ~= 'Content-Length', "please don't give Content-Length") - assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") - insert(request, header[1]..': '..header[2]..CRLF) - end - end - insert(request, CRLF) - if type(opt.body) == "table" then - local body = {} - for _, b in ipairs(opt.body) do - assert(#b == 2, "if BODY is TABLE, BODY need key[1]->value[2] (2 values)") - insert(body, url_encode(b[1])..'='..url_encode(b[2])) - end - insert(request, concat(body, "&")) - insert(request, #request - 2, fmt("Content-Length: %s\r\n", #request[#request])) - end - if type(opt.body) == "string" then - insert(request, #request, fmt("Content-Length: %s\r\n", #opt.body)) - insert(request, opt.body) - end + if type(opt.headers) == "table" then + for _, header in ipairs(opt.headers) do + assert(lower(header[1]) ~= 'content-length', "please don't give Content-Length") + assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") + insert(request, header[1] .. ': ' .. header[2] .. CRLF) + end + end + insert(request, CRLF) + if type(opt.body) == "table" then + local body = {} + for _, item in ipairs(opt.body) do + assert(#item == 2, "if BODY is TABLE, BODY need key[1]->value[2] (2 values)") + insert(body, url_encode(item[1])..'='..url_encode(item[2])) + end + local Body = concat(body, "&") + insert(request, #request, fmt('Content-Length: %s\r\n', #Body)) + insert(request, #request, 'Content-Type: application/x-www-form-urlencoded\r\n') + insert(request, Body) + end return concat(request) end @@ -281,38 +277,36 @@ local function build_delete_req (opt) "Connection: keep-alive", } if type(opt.headers) == "table" then - for _, header in ipairs(opt.headers) do - assert(lower(header[1]) ~= 'Content-Length', "please don't give Content-Length") - assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") - insert(request, header[1]..': '..header[2]) - end - if opt.body then - insert(request, fmt("Content-Length: %s", #opt.body)) + for _, header in ipairs(opt.headers) do + assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") + insert(request, header[1]..': '..header[2]) end - end - return concat(request, CRLF) .. CRLF2 .. ( opt.body or '' ) + end + if type(opt.body) == "string" then + insert(request, fmt("Content-Length: %s", #opt.body)) + end + return concat(request, CRLF) .. CRLF2 .. ( type(opt.body) == "string" and opt.body or '' ) end local function build_json_req (opt) local request = { - fmt("POST %s HTTP/1.1", opt.path), - fmt("Host: %s", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port), - 'Accept: */*', - 'Accept-Encoding: identity', - "Connection: keep-alive", - fmt("User-Agent: %s", opt.server), - fmt("Content-Length: %s", #opt.json), - 'Content-Type: application/json', + fmt("POST %s HTTP/1.1", opt.path), + fmt("Host: %s", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port), + 'Accept: */*', + 'Accept-Encoding: identity', + "Connection: keep-alive", + fmt("User-Agent: %s", opt.server), + fmt("Content-Length: %s", #opt.json), + 'Content-Type: application/json', } if type(opt.headers) == "table" then - for _, header in ipairs(opt.headers) do - assert(lower(header[1]) ~= 'Content-Length', "please don't give Content-Length") - assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") - insert(request, header[1]..': '..header[2]) - end - end - insert(request, CRLF) - return concat(request, CRLF)..opt.json + for _, header in ipairs(opt.headers) do + assert(lower(header[1]) ~= 'content-length', "please don't give Content-Length") + assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") + insert(request, header[1]..': '..header[2]) + end + end + return concat(request, CRLF) .. CRLF2 .. opt.json end local function build_file_req (opt) @@ -327,7 +321,7 @@ local function build_file_req (opt) if type(opt.headers) == "table" then for _, header in ipairs(opt.headers) do - assert(lower(header[1]) ~= 'Content-Length', "please don't give Content-Length") + assert(lower(header[1]) ~= 'content-length', "please don't give Content-Length") assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") insert(request, header[1]..': '..header[2]..'\r\n') end @@ -366,17 +360,19 @@ local function build_put_req (opt) 'Accept: */*', 'Accept-Encoding: identity', fmt("Connection: keep-alive"), - fmt("Content-Length: %s", #opt.body), fmt("User-Agent: %s", opt.server), } if type(opt.headers) == "table" then for _, header in ipairs(opt.headers) do - assert(lower(header[1]) ~= 'Content-Length', "please don't give Content-Length") + assert(lower(header[1]) ~= 'content-length', "please don't give Content-Length") assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") insert(request, header[1]..': '..header[2]) end end - return concat(request, CRLF) .. CRLF2 .. ( opt.body or '' ) + if type(opt.body) == "string" then + insert(request, fmt("Content-Length: %s", #opt.body)) + end + return concat(request, CRLF) .. CRLF2 .. ( type(opt.body) == "string" and opt.body or '' ) end return { From a0c861fa5189cc92143c103349f6552195f5f5e1 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 19 Sep 2019 11:19:16 +0800 Subject: [PATCH 358/956] =?UTF-8?q?=E4=BC=98=E5=8C=96httpc=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/protocol.lua | 44 ++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua index 49353c44..0a0ca150 100644 --- a/lualib/httpc/protocol.lua +++ b/lualib/httpc/protocol.lua @@ -282,7 +282,7 @@ local function build_delete_req (opt) insert(request, header[1]..': '..header[2]) end end - if type(opt.body) == "string" then + if type(opt.body) == "string" and opt.body ~= '' then insert(request, fmt("Content-Length: %s", #opt.body)) end return concat(request, CRLF) .. CRLF2 .. ( type(opt.body) == "string" and opt.body or '' ) @@ -296,8 +296,6 @@ local function build_json_req (opt) 'Accept-Encoding: identity', "Connection: keep-alive", fmt("User-Agent: %s", opt.server), - fmt("Content-Length: %s", #opt.json), - 'Content-Type: application/json', } if type(opt.headers) == "table" then for _, header in ipairs(opt.headers) do @@ -306,6 +304,10 @@ local function build_json_req (opt) insert(request, header[1]..': '..header[2]) end end + if type(opt.json) == 'string' and opt.json ~= '' then + insert(request, 'Content-Type: application/json') + insert(request, fmt("Content-Length: %s", #opt.json)) + end return concat(request, CRLF) .. CRLF2 .. opt.json end @@ -327,28 +329,40 @@ local function build_file_req (opt) end end - if opt.files then + if type(opt.files) == 'table' then local bd = random(1000000000, 9999999999) local boundary_start = fmt("------CFWebService%d", bd) local boundary_end = fmt("------CFWebService%d--", bd) insert(request, fmt('Content-Type: multipart/form-data; boundary=----CFWebService%s\r\n', bd)) local body = {} - local header = "" - for _, file in ipairs(opt.files) do + local cd = 'Content-Disposition: form-data; %s' + local ct = 'Content-Type: %s' + for index, file in ipairs(opt.files) do + assert(file.file, "files index : [" .. index .. "] unknown multipart/form-data content.") insert(body, boundary_start) - if file.name and file.filename then - header = fmt(' name="%s"; filename="%s"', file.name, file.filename) + local name = file.name + local filename = file.filename + if not file.type then + if type(name) ~= 'string' or name == '' then + name = '' + end + insert(body, fmt(cd, fmt('name="%s"', name)) .. CRLF) + else + if type(name) ~= 'string' or name == '' then + name = '' + end + if type(filename) ~= 'string' or filename == '' then + filename = '' + end + insert(body, fmt(cd, fmt('name="%s"', name) .. '; ' .. fmt('filename="%s"', filename))) + insert(body, fmt(ct, FILEMIME(file.type or '') or 'application/octet-stream') .. CRLF) end - insert(body, fmt('Content-Disposition: form-data;%s', header)) - insert(body, fmt('Content-Type: %s\r\n', FILEMIME(file.type or '') or 'application/octet-stream')) insert(body, file.file) end + insert(body, boundary_end) body = concat(body, CRLF) - insert(request, fmt("Content-Length: %s\r\n", #body + 2 + #boundary_end)) - insert(request, CRLF) + insert(request, fmt("Content-Length: %s\r\n\r\n", #body)) insert(request, body) - insert(request, CRLF) - insert(request, boundary_end) end return concat(request) end @@ -369,7 +383,7 @@ local function build_put_req (opt) insert(request, header[1]..': '..header[2]) end end - if type(opt.body) == "string" then + if type(opt.body) == "string" and opt.body ~= '' then insert(request, fmt("Content-Length: %s", #opt.body)) end return concat(request, CRLF) .. CRLF2 .. ( type(opt.body) == "string" and opt.body or '' ) From 8271c8fa1fa48c51d23cd44f37571812297b0900 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 19 Sep 2019 15:17:19 +0800 Subject: [PATCH 359/956] =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E6=96=87=E6=9C=AC?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E9=BB=98=E8=AE=A4=E4=B8=BAutf8=E5=AD=97?= =?UTF-8?q?=E7=AC=A6=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua index 30f4513d..a7b867e7 100644 --- a/lualib/protocol/http.lua +++ b/lualib/protocol/http.lua @@ -654,9 +654,9 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) end if typ == HTTP_PROTOCOL.API or typ == HTTP_PROTOCOL.USE then if typ == HTTP_PROTOCOL.API then - header[#header+1] = 'Content-Type: '..REQUEST_MIME_RESPONSE('json') + header[#header+1] = 'Content-Type: ' .. REQUEST_MIME_RESPONSE('json') .. "; charset=utf-8" else - header[#header+1] = concat({'Content-Type: ', REQUEST_MIME_RESPONSE('html'), ';charset=utf-8'}) + header[#header+1] = 'Content-Type: ' .. REQUEST_MIME_RESPONSE('html') .. "; charset=utf-8" end if type(body) ~= 'string' or body == '' then Log:ERROR("Response Error ["..(split(PATH , 1, (find(PATH, '?') or 0 ) - 1)).."]: response must be a string and not empty.") From 156ceb1e81102380d14ce4d2c0d9e5ea7a18fe62 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 20 Sep 2019 01:10:05 +0800 Subject: [PATCH 360/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lhttpparser/lhttpparser.c | 14 +++++++++----- src/Makefile | 8 ++++---- src/core.c | 7 ++++--- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/luaclib/src/lhttpparser/lhttpparser.c b/luaclib/src/lhttpparser/lhttpparser.c index ca7fd50d..3bf79d11 100644 --- a/luaclib/src/lhttpparser/lhttpparser.c +++ b/luaclib/src/lhttpparser/lhttpparser.c @@ -3,13 +3,18 @@ #include "../../../src/core.h" #include "httpparser.h" -int +#define ALLOCA_MAX 65535 + +static int lparser_response_chunked(lua_State *L){ size_t buf_len; const char* data = luaL_checklstring(L, 1, &buf_len); - - char *buf = (char *)xcalloc(1, buf_len); - strncpy(buf, data, buf_len); + char* buf = xmalloc(buf_len); + if (!buf){ + LOG("ERROR", "Alloca memory falt."); + return 0; + } + memmove(buf, data, buf_len); struct phr_chunked_decoder decoder = { .consume_trailer = 1 }; @@ -25,7 +30,6 @@ lparser_response_chunked(lua_State *L){ return 1; } - static int lparser_http_request(lua_State *L){ size_t buf_len; diff --git a/src/Makefile b/src/Makefile index ad58d562..c970ad63 100644 --- a/src/Makefile +++ b/src/Makefile @@ -13,14 +13,14 @@ LIBS += -L. -L../ -L/usr/local/lib INCLUDES += -I. -L../ -I/usr/local/include # 使用jemalloc内存分配器请启用这段 -# CFLAGS += -Wall -Os -fPIC --shared -DJEMALLOC -fno-strict-aliasing -Wl,-rpath,/usr/local/lib +# CFLAGS += -Wall -O3 -fPIC --shared -DJEMALLOC -fno-strict-aliasing -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib # DLL += -ljemalloc -lev -llua -ldl -# MACRO += -w -O3 -Wl,-rpath,./ -DJEMALLOC +# MACRO += -w -O3 -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib/ -DJEMALLOC # 使用tcmalloc内存分配器请启用这段 -# CFLAGS += -Wall -Os -fPIC --shared -DTCMALLOC -fno-strict-aliasing -Wl,-rpath,/usr/local/lib +# CFLAGS += -Wall -O3 -fPIC --shared -DTCMALLOC -fno-strict-aliasing -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib # DLL += -ltcmalloc -lev -llua -ldl -# MACRO += -w -O3 -Wl,-rpath,./ -DTCMALLOC +# MACRO += -w -O3 -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib -DTCMALLOC # 默认情况下使用系统内存分配器 CFLAGS += -Wall -O3 -fPIC --shared -fno-strict-aliasing -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib diff --git a/src/core.c b/src/core.c index 2a1e9d04..38e614cf 100644 --- a/src/core.c +++ b/src/core.c @@ -48,7 +48,8 @@ SIG_IGNORE(core_loop *loop, core_signal *signal, int revents){ static void // 退出信号 SIG_EXIT(core_loop *loop, core_signal *signal, int revents){ // LOG("ERROR", signum_to_string(signal->signum)); - return exit(-1); + _exit(-1); + return ; } static void @@ -166,13 +167,13 @@ init_main(){ status = luaL_loadfile(L, "script/main.lua"); if (status > 1){ LOG("ERROR", lua_tostring(L, -1)); - return lua_close(L), exit(-1); + return lua_close(L), _exit(-1); } status = CO_RESUME(L, NULL, 0); if (status > 1){ LOG("ERROR", lua_tostring(L, -1)); - return lua_close(L), exit(-1); + return lua_close(L), _exit(-1); } if (status == LUA_YIELD) { signal_init(); From be1e314b9a94b529adbd3a05a3e636781a846d68 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 20 Sep 2019 01:34:28 +0800 Subject: [PATCH 361/956] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E6=9C=AA=E8=A2=AB?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E7=9A=84=E5=AE=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lhttpparser/lhttpparser.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/luaclib/src/lhttpparser/lhttpparser.c b/luaclib/src/lhttpparser/lhttpparser.c index 3bf79d11..9aad968e 100644 --- a/luaclib/src/lhttpparser/lhttpparser.c +++ b/luaclib/src/lhttpparser/lhttpparser.c @@ -3,8 +3,6 @@ #include "../../../src/core.h" #include "httpparser.h" -#define ALLOCA_MAX 65535 - static int lparser_response_chunked(lua_State *L){ size_t buf_len; From d6e95eb1698ff4699eac907430190b876d937d32 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 25 Sep 2019 12:55:41 +0800 Subject: [PATCH 362/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index a1788436..3592e42c 100755 --- a/build.sh +++ b/build.sh @@ -24,7 +24,7 @@ echo "========== build libev ==========" && make && cp e*.h ${current}/src && cd .libs && cp $(printf "%s" "`ls | grep libev | grep -v la`") ${current}/ echo "========== build lua ==========" && - cd ${current}/build/lua && make all MYCFLAGS=-fPIC MYCFLAGS+=-DLUA_USE_POSIX MYCFLAGS+=-DLUA_USE_DLOPEN MYLIBS="-ldl -lreadline" && + cd ${current}/build/lua && make posix MYCFLAGS="-fPIC -DLUA_USE_DLOPEN -DLUA_USE_READLINE" MYLIBS="-ldl -lreadline" && cp lua.h luaconf.h lualib.h lauxlib.h ${current}/src && cp liblua.* ${current}/ echo "========== clean build ==========" && cd ${current} && rm -rf build From 1a204b40fb5d5a0c3dcdb5cb6494efda12903a0a Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 26 Sep 2019 11:44:31 +0800 Subject: [PATCH 363/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_DB.lua | 128 ++++----------------------------------- script/test_dingtalk.lua | 11 ++-- script/test_ffi.lua | 8 +-- script/test_logging.lua | 30 ++++++++- script/test_mysql.lua | 54 ----------------- script/test_qiniu.lua | 22 ++++--- 6 files changed, 64 insertions(+), 189 deletions(-) delete mode 100644 script/test_mysql.lua diff --git a/script/test_DB.lua b/script/test_DB.lua index 4cddc96b..fb5e5045 100644 --- a/script/test_DB.lua +++ b/script/test_DB.lua @@ -1,9 +1,6 @@ -local log = require "logging" +local LOG = require "logging" local cf = require "cf" local DB = require "DB" -local Log = log:new() - -require "utils" local db = DB:new { host = 'localhost', @@ -16,21 +13,18 @@ local db = DB:new { local ok = db:connect() if not ok then - return print("连接mysql失败") + return LOG:DEBUG("连接mysql失败") end -print("连接成功") +LOG:DEBUG("连接成功") --[[ - 复制下面语句到任意管理工具即可导入测试表进行测试 + /* 复制下面语句到任意管理工具即可导入测试表进行测试 */ SET NAMES utf8; SET FOREIGN_KEY_CHECKS = 0; + CREATE DATABASE IF NOT EXISTS `test` DEFAULT CHARSET utf8 COLLATE utf8_general_ci; - -- ---------------------------- - -- Table structure for `user` - -- ---------------------------- - DROP TABLE IF EXISTS `user`; - CREATE TABLE `user` ( + CREATE TABLE IF NOT EXISTS `test`.`user` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(255) CHARACTER SET utf8mb4 NOT NULL, `user` varchar(255) CHARACTER SET utf8mb4 NOT NULL, @@ -39,121 +33,23 @@ print("连接成功") ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; SET FOREIGN_KEY_CHECKS = 1; - --]] --- 插入语句示例 -cf.fork(function ( ... ) - local ret, err = db:insert("user") - :fields({"name", "user", "passwd"}) - :values({ - {"candy", "root", "123456789"}, - {"水果糖", "admin", "123456789"}, - }) - :execute() - - if not ret then - return print(err) - end - - var_dump(ret) -end) - --- 查询语句示例 -cf.fork(function ( ... ) - local ret, err = db:select({"id", "name", "user", "passwd"}) - :from({"user"}) - :where({ - {"id", "!=", "0"}, - "AND", - {"id", ">=", "1"}, - "OR", - {"user", "!=", "admin"}, - "AND", - {"user", "=", "admin"}, - "OR", - {"user", "IS", "NOT", "NULL"}, - "AND", - {"user", "IS", "NULL"}, - "AND", - {"user", "IN", {1, 2, 3, 4, 5}}, - "AND", - {"user", "NOT", "IN", {1, 2, 3, 4, 5}}, - "AND", - {"user", "BETWEEN", {1, 100}}, - "AND", - {"user", "NOT", "BETWEEN", {1, 100}}, - "AND", - {"`user`.id", "=", '`user`.id'}, - }) - :groupby('id') -- groupby({"name", "user"}) - :orderby("id") -- orderby({"name", "user"}) - :asc() -- or desc() - :limit(1) -- limit("1") limit(1, 100) - :execute() -- 所有语句最后必须指定这个方法才会真正执行 - - if not ret then - return print(err) - end - - var_dump(ret) -end) - --- 更新语句示例 -cf.fork(function ( ... ) - local ret, err = db:update("user") - :set({ - {"name", "=", "管理员"}, - {"user", "=", "Administrator"}, - {"passwd", "=", "Administrator"}, - }) - :where({ - {"id", "<=", 1}, - }) - :limit(1) - :execute() - - if not ret then - return print(err) - end - - var_dump(ret) -end) - --- 删除语句示例 -cf.fork(function ( ... ) - local ret, err = db:delete("user") - :where({ - {"id", ">", 1}, - }) - :orderby("id") - :limit(1) - :execute() - - if not ret then - return print(err) - end - - var_dump(ret) -end) - - +-- 测试普通数据库语句 cf.fork(function ( ... ) local ret, err = db:query("show variables like 'wait_timeout'") if not ret then - return print(err) + return LOG:DEBUG(err) end - - var_dump(ret) + LOG:DEBUG(ret) end) - +-- 测试预编译语句 cf.fork(function ( ... ) local rkey = db:prepare([[SELECT version() AS version]]) local ret, err = db:execute(rkey) if not ret then - return print(err) + return LOG:DEBUG(err) end - - var_dump(ret) + LOG:DEBUG(ret) end) diff --git a/script/test_dingtalk.lua b/script/test_dingtalk.lua index a5133f0e..9046682d 100644 --- a/script/test_dingtalk.lua +++ b/script/test_dingtalk.lua @@ -1,10 +1,11 @@ local wb = require "webhook.dingtalk" +local LOG = require "logging" -- dingtalk API文档: https://ding-doc.dingtalk.com/doc#/serverapi2/qf2nxq local token = "just_your_token_not_url_and_token" -- such as "de2b0b8a3c4b8d454f47584354a794a12657aa9ff7ccf36b521368d566949e7f" -print(wb.send_text({ +LOG:DEBUG(wb.send_text({ token = token, content = "一条测试消息哦.", -- ignore mobiles if atall equal true. @@ -12,7 +13,7 @@ print(wb.send_text({ -- atall = true, })) -print(wb.send_link { +LOG:DEBUG(wb.send_link { token = token, msg_title = "这是一条测试公告", msg_link = "https://github.com/candymi", @@ -21,7 +22,7 @@ print(wb.send_link { msg_describe = "这是测试公告的描述信息, 它描述了这条公告的一些外链关键内容.", }) -print(wb.send_actioncard{ +LOG:DEBUG(wb.send_actioncard{ token = token, msg_title = "## 消息头部", msg_describe = "## 消息内容", @@ -31,7 +32,7 @@ print(wb.send_actioncard{ } }) -print(wb.send_actioncard{ +LOG:DEBUG(wb.send_actioncard{ token = token, -- 头部 msg_title = "## 消息头部", @@ -48,7 +49,7 @@ print(wb.send_actioncard{ } }) -print(wb.send_feedcard { +LOG:DEBUG(wb.send_feedcard { token = token, msg_links = { { diff --git a/script/test_ffi.lua b/script/test_ffi.lua index 7d339a01..036b49b1 100644 --- a/script/test_ffi.lua +++ b/script/test_ffi.lua @@ -6,7 +6,7 @@ local ffi = require "lffi" -- Log:DEBUG("uint16_t长度为:"..ffi.sizeof(ffi.new("uint16_t"))) -- Log:DEBUG("uint32_t长度为:"..ffi.sizeof(ffi.new("uint32_t"))) -- Log:DEBUG("uint64_t长度为:"..ffi.sizeof(ffi.new("uint64_t"))) - +-- -- -- 字符串测试 -- local cdata = ffi.new("char [?]", #"admin", "admin") -- Log:DEBUG("将lua字符串转换为cdata:", cdata) @@ -15,13 +15,13 @@ local ffi = require "lffi" -- -- Log:DEBUG("测试cdata字符串类型是否可以索引:", cdata[0], cdata[1], cdata[2], cdata[3], cdata[4]) -- Log:DEBUG("测试cdata字符串类型是否可以转换:", string.char(cdata[0])..string.char(cdata[1])..string.char(cdata[2])..string.char(cdata[3])..string.char(cdata[4])) - +-- -- -- 整型数组测试 -- local array = ffi.new("int[?]", 3, 1, 2, 3) -- 初始化方法 1 -- local array = ffi.new("int[3]", 1, 2, 3) -- 初始化方法 2 -- Log:DEBUG(array[0], array[1], array[2]) - --- 结构体创建测试 +-- +-- -- 结构体创建测试 -- ffi.cdef [[ -- typedef struct cuboid { uint8_t h, w, l; } cuboid_t; -- ]] diff --git a/script/test_logging.lua b/script/test_logging.lua index 22c22eb4..b24a2a7a 100644 --- a/script/test_logging.lua +++ b/script/test_logging.lua @@ -1,10 +1,34 @@ local LOG = require "logging" +--[[ + 日志库分为2种使用方式: 1. 初始化后使用, 2. 直接导入使用; 两者之间的差异根据不同需求不同使用. + + 初始化使用时为了将不同的日志打印并dump到不同的日志文件中, 这中方式在调试开发的时候非常有用. + + 直接导入使用不会将日志持久化到磁盘(输出到stdout), 相对于dump到本地磁盘. 它更是适用于docker这种做为集中式日志收集. + + `path`参数通常是一个日志文件名, 它的前缀为`logs/{your_path}`, 但是你可以根据实际情况加上路径对文件进行分割. + + `dump`参数决定是否将打印内容序列化到磁盘. + + 注意: 如果您的path中包含了目录, 需要先自行创建目录. 否则将会打印错误. +]] + -- 初始化日志 -local log = LOG:new { path = './admin' } +local log = LOG:new { path = 'admin/main' , dump = true } + +print() --- 打印 +-- dump到磁盘 log:INFO('this is INFO LOG', nil, 1, nil) log:DEBUG('this is DEBUG LOG', nil, nil, 1) log:WARN('this is WARN LOG', 1, nil, nil) -log:ERROR('this is ERROR LOG', nil, nil, nil, log) +log:ERROR('this is ERROR LOG', nil, nil, nil) + +print() + +-- 仅输出到stdout +LOG:INFO('this is INFO LOG', nil, 1, nil) +LOG:DEBUG('this is DEBUG LOG', nil, nil, 1) +LOG:WARN('this is WARN LOG', 1, nil, nil) +LOG:ERROR('this is ERROR LOG', nil, nil, nil) diff --git a/script/test_mysql.lua b/script/test_mysql.lua deleted file mode 100644 index 8466ce51..00000000 --- a/script/test_mysql.lua +++ /dev/null @@ -1,54 +0,0 @@ --- 测试MySQL -local mysql = require "protocol.mysql" -local config = { - host = "localhost", - port = 3306, - database = "mysql", - user = "root", - password = "123456789" -} - --- 创建->查询->销毁100次mysql连接 -local times = 100 -for i = 1, times do - local db, err = mysql:new() - if not db then - return - end - local ok, err, errno, sqlstate = db:connect(config) - if not ok then - return print("连接失败.", i, err) - end - local resp, err = db:query("select * from user") - if not resp then - return print('查询失败', err) - end - db:close() -end -print("创建并销毁"..tostring(times).."次MySQL连接成功") - - --- 保存mysql连接池 -> 销毁连接池 -local pool = {} -for i = 1, times do - local db, err = mysql:new() - if not db then - return - end - local ok, err, errno, sqlstate = db:connect(config) - if not ok then - return print("连接失败.", i, err) - end - local resp, err = db:query("select * from user") - if not resp then - return print('查询失败', err) - end - pool[#pool+1] = db -end -print('创建'..tostring(times)..'MySQL连接成功') -for _, db in ipairs(pool) do - db:close() -end -pool = nil -print('销毁MySQL连接') -print("测试完成") \ No newline at end of file diff --git a/script/test_qiniu.lua b/script/test_qiniu.lua index 5ac05f99..dab8f2b1 100644 --- a/script/test_qiniu.lua +++ b/script/test_qiniu.lua @@ -4,28 +4,36 @@ local oss = require "cloud.qiniu.oss" local AccessKey = 'Your_Real_AccessKey' local SecretKey = 'Your_Real_SecretKey' - +-- 发送短信 -- local ok, err = sms.sendSMS(AccessKey, SecretKey, { template_id = '1174104085482180608', mobiles = {'+8613000000000'}, parameters = { code = tostring(math.random(1, 1024)) }}) -- require"logging":DEBUG(ok, err) --- + +-- 获取短信模板列表 -- local ok, err = sms.getTemplates(AccessKey, SecretKey, { page = 1, page_size = 100 }) -- require"logging":DEBUG(ok, err) --- + +-- 获取短信签名列表 -- local ok, err = sms.getSignatures(AccessKey, SecretKey, { page = 1, page_size = 90 }) -- require"logging":DEBUG(ok, err) --- + +-- 获取发送短信记录 -- local ok, err = sms.getSMSRecord(AccessKey, SecretKey, { start = '1568131200', ['end'] = '1568822399'}) -- require"logging":DEBUG(ok, err) --- + +-- 删除模板 -- local ok, err = sms.detTemplate(AccessKey, SecretKey, '1174104085482180608') -- require"logging":DEBUG(ok, err) --- + +-- 删除签名 -- local ok, err = sms.delSignature(AccessKey, SecretKey, '1174104085482180608') -- require"logging":DEBUG(ok, err) +-- *************** +-- 生成上传token -- local upToken = oss.getUploadToken(AccessKey, SecretKey, { bucket = 'candymi' }) -- require"logging":DEBUG(upToken) --- + +-- 生成下载token -- local upToken = oss.getDownloadToken(AccessKey, SecretKey, { url = 'https://www.gitub.com/a.img', expires = os.time() + 180 }) -- require"logging":DEBUG(upToken) From 3a51b2c2fa5fa0628f71a59a13501c53dff07db6 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 28 Sep 2019 09:55:27 +0800 Subject: [PATCH 364/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0Lua=E7=89=88=E7=9A=84?= =?UTF-8?q?paypal=E6=94=AF=E4=BB=98SDK=E4=B8=8Etencent=E7=9A=84=E4=BD=8D?= =?UTF-8?q?=E7=BD=AE=E6=9C=8D=E5=8A=A1SDK?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/cloud/paypal/payment.lua | 62 ++++++++++++++ lualib/cloud/tencent/location.lua | 135 ++++++++++++++++++++++++++++++ script/test_location.lua | 32 +++++++ 3 files changed, 229 insertions(+) create mode 100644 lualib/cloud/paypal/payment.lua create mode 100644 lualib/cloud/tencent/location.lua create mode 100644 script/test_location.lua diff --git a/lualib/cloud/paypal/payment.lua b/lualib/cloud/paypal/payment.lua new file mode 100644 index 00000000..1d35adc2 --- /dev/null +++ b/lualib/cloud/paypal/payment.lua @@ -0,0 +1,62 @@ +local httpc = require "httpc" +local crypt = require "crypt" +local json = require "json" + +--[[ + 1. 申请token + 2. 创建订单 + 3. 用户确认支付 / 取消支付 + 4. 商家确认支付 / 取消支付 + 5. 交易完成 / 取消 +]] + +local Payment = { __Version__ = 0.1 } + +local function basic_auth(username, password) + return "Basic " .. crypt.base64encode(username .. ":" .. password) +end + +local function Bearer_auth (accesstoken) + return "Bearer " .. accesstoken +end + +-- 申请AccessToken +function Payment.getAccessToken (clientid, secret) + return httpc.post("https://api.sandbox.paypal.com/v1/oauth2/token", { + {"Accept", "application/json"}, {"Authorization", basic_auth(clientid, secret)} + }, { {"grant_type", "client_credentials"} }) +end + +-- 创建账单 +function Payment.createPayment (accesstoken, opt) + return httpc.json("https://api.sandbox.paypal.com/v1/payments/payment", { {"Authorization", Bearer_auth(accesstoken)} }, json.encode({ + intent = opt.intent, payer = opt.payer, + transactions = opt.transactions, + not_to_payer = opt.not_to_payer, + redirect_urls = opt.redirect_urls, + })) +end + +-- 确认支付 +function Payment.confirmPayment (accesstoken, payment_id, payer_id) + return httpc.json("https://api.sandbox.paypal.com/v1/payments/payment/".. payment_id .."/execute", { {"Authorization", Bearer_auth(accesstoken)} }, + json.encode({ payer_id = payer_id }) + ) +end + +-- 查询账单 +function Payment.getPaymentDetails (accesstoken, order_id) + return httpc.get("https://api.sandbox.paypal.com/v1/payments/payment/" .. order_id, { {"Authorization", Bearer_auth(accesstoken)} }) +end + +-- 批量查询账单 +function Payment.getPaymentsDetails (accesstoken, opt) + return httpc.get("https://api.sandbox.paypal.com/v1/payments/payment", { {"Authorization", Bearer_auth(accesstoken)} }, { + {"count", opt.count or 10}, -- 分页数量 + {"start_index", opt.start_index or 1}, -- 第几页 + {"sort_by", opt.sort_by or "create_time"}, -- 排序字段, + {"sort_order", opt.sort_order or "desc"}, -- 排序方式, + }) +end + +return Payment diff --git a/lualib/cloud/tencent/location.lua b/lualib/cloud/tencent/location.lua new file mode 100644 index 00000000..fb4898c5 --- /dev/null +++ b/lualib/cloud/tencent/location.lua @@ -0,0 +1,135 @@ +local httpc = require "httpc" +local crypt = require "crypt" + +local type = type +local assert = assert + +--[[ + 文档地址: https://lbs.qq.com/webservice_v1/index.html + 目前所有API接口仅支持SN码签名校验, 请自行在腾讯位置服务端`后台管理`->`key管理`中进行设置获取key并且生成sn码. + 所有接口数据均返回原生http code与json数据, 请开发者自行进行接口判断与json decode. +]] + +local Location = { __Version__ = 0.1 } + +-- IP定位 +--[[ + ip : IP地址 +]] +function Location.getIpLocation (accesskey, sn, ip) + return httpc.get("https://apis.map.qq.com/ws/location/v1/ip", nil, { + {"ip", ip}, + {"key", accesskey}, + {"sig", crypt.md5("/ws/location/v1/ip?" + .. "ip=" .. ip .. "&" .. "key=" .. accesskey + .. sn , true) + } + }) +end + +-- 获取行政规划区列表 +function Location.getDistrictList (accesskey, sn) + return httpc.get("https://apis.map.qq.com/ws/district/v1/list", nil, { + {"key", accesskey}, + {"sig", crypt.md5("/ws/district/v1/list?" + .. "key=" .. accesskey + .. sn , true) + } + }) +end + +-- 获取指定行政规划区 +--[[ + id: 父级行政区划ID,缺省时则返回最顶级行政区划 +]] +function Location.getDistrictChildren (accesskey, sn, id) + return httpc.get("https://apis.map.qq.com/ws/district/v1/getchildren", nil, { + {"key", accesskey}, + {"id", id}, + {"sig", crypt.md5("/ws/district/v1/getchildren?" + .. "id=" .. id .. "&" .. "key=" .. accesskey + .. sn , true) + } + }) +end + +-- 关键词猜测(补全) +--[[ + keyword: 搜索/联想/补全关键词 + region: 范围, 如: 广州 + region_fix : 是否固定范围. + location: 不支持 + page_index: 当前是第几页 + page_size: 每页返回数量 +]] +function Location.searchSuggestion (accesskey, sn, keyword, region, region_fix, page_index, page_size) + return httpc.get("https://apis.map.qq.com/ws/place/v1/suggestion", nil, { + {"key", accesskey}, + {"keyword", keyword}, + {"page_index", page_index or 1}, + {"page_size", page_size or 10}, + {"region", region or '' }, + {"region_fix", region_fix or 1}, + {"sig", crypt.md5("/ws/place/v1/suggestion?" + .. "key=" .. accesskey .. "&" .. "keyword=" .. keyword .. "&" + .. "page_index=" .. (page_index or 1) .. "&" .. "page_size=" .. (page_size or 10) .. "&" + .. "region=" .. (region or '') .. "&" .. "region_fix=" .. (region_fix or 1) + .. sn , true) + } + }) +end + +function Location.searchPlace (accesskey, sn, keyword, boundary) + return httpc.get("https://apis.map.qq.com/ws/place/v1/search", nil, { + {"boundary", boundary}, + {"key", accesskey}, + {"keyword", keyword}, + {"page_index", page_index or 1}, + {"page_size", page_size or 10}, + {"sig", crypt.md5("/ws/place/v1/search?" + .. "boundary=" .. boundary .. "&" .. "key=" .. accesskey .. "&" .. "keyword=" .. keyword .. "&" + .. "page_index=" .. (page_index or 1) .. "&" .. "page_size=" .. (page_size or 10) .. "&" + .. sn , true) + } + }) +end + +-- 关键词搜索行政规划区 +--[[ + keyword: 行政区关键词 +]] +function Location.searchDistrict (accesskey, sn, keyword) + return httpc.get("https://apis.map.qq.com/ws/district/v1/search", nil, { + {"key", accesskey}, + {"keyword", keyword}, + {"sig", crypt.md5("/ws/district/v1/search?" + .. "key=" .. accesskey .. "&" .. "keyword=" .. keyword + .. sn , true) + } + }) +end + +-- 距离计算(一对多) +--[[ + mode: driving 驾车, wakling 步行 + from: 起始经纬度 + to: 目的经纬度 +]] +function Location.getDistance (accesskey, sn, opt) + assert(type(opt) == 'table', "invaild arguments") + assert(opt.from, "invaild current position.") + assert(opt.to, "invaild Destination position.") + assert(opt.mode == 'driving' or opt.mode == 'walking', "invaild mode. [driving | walking]") + return httpc.get("https://apis.map.qq.com/ws/distance/v1/", nil, { + {"from", opt.from}, + {"mode", opt.mode}, + {"key", accesskey}, + {"to", opt.to}, + {"sig", crypt.md5("/ws/distance/v1/?" + .. "from=" .. opt.from .. "&" .. "key=" .. accesskey .. "&" .. "mode=" .. opt.mode .. "&" .. "to=" .. opt.to + .. sn , true) + } + }) +end + +return Location diff --git a/script/test_location.lua b/script/test_location.lua new file mode 100644 index 00000000..1e6cc569 --- /dev/null +++ b/script/test_location.lua @@ -0,0 +1,32 @@ +local location = require "cloud.tencent.location" + +--[[ + 使用详情请参考: lualib/cloud/tencent/location.lua +]] + +local accesskey = "Your_Access_key" + +local sn = "Your_SN_Key" + +-- local code, ret = location.getIpLocation(accesskey, sn, '8.8.8.8') +-- print(code, ret) + +-- local code, ret = location.getDistrictList(accesskey, sn,) +-- print(code, ret) + +-- local code, ret = location.getDistrictChildren(accesskey, sn, 110000) +-- print(code, ret) + +-- local code, ret = location.searchDistrict(accesskey, sn, "香格里拉") +-- print(code, ret) + +-- local code, ret = location.searchSuggestion(accesskey, sn, "盐津铺子", "广州", 1) +-- print(code, ret) + +-- local code, ret = location.getDistance(accesskey, sn, { +-- mode = "walking", from = "39.071510,117.190091", to="39.840177,116.463318" +-- }) +-- print(code, ret) + +-- local code, ret = location.searchPlace(accesskey, sn, "长沙", "region(湖南,0)") +-- print(code, ret) From f7d9b8edd79b3ee1f0a2c1c0cd16a8d00d2e9033 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 29 Sep 2019 14:15:03 +0800 Subject: [PATCH 365/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=99=9A=E6=8B=9F?= =?UTF-8?q?=E6=9C=BA=E5=87=BA=E9=94=99=E5=90=8E=E6=9C=AA=E6=89=93=E5=8D=B0?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_sys.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/core_sys.h b/src/core_sys.h index 6ade2183..83db194f 100644 --- a/src/core_sys.h +++ b/src/core_sys.h @@ -53,9 +53,8 @@ time_t t = time(NULL); struct tm* lt = localtime(&t); \ fprintf(stdout, "[%04d/%02d/%02d][%02d:%02d:%02d][%s][%s][%s:%d] : %s\n", \ lt->tm_year + 1900, 1 + lt->tm_mon, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec, \ - LEVEL, \ - __FILE__, __FUNCTION__, __LINE__, \ - CONTENT); \ + LEVEL, __FILE__, __FUNCTION__, __LINE__, CONTENT); \ + fflush(stdout); \ } /* 微秒级时间戳函数 */ From 6e0aab90e27dee312b64650a015a0eaaa17c2117 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 29 Sep 2019 23:22:02 +0800 Subject: [PATCH 366/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=B8=83=E7=89=9B?= =?UTF-8?q?=E7=9B=B8=E5=85=B3SDK,=20=E5=A2=9E=E5=8A=A0=E7=9B=B4=E6=92=AD?= =?UTF-8?q?=E4=B8=8E=E5=86=85=E5=AE=B9=E5=AE=A1=E6=A0=B8SDK?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/cloud/qiniu/censor.lua | 34 +++++ lualib/cloud/qiniu/oss.lua | 59 +------- lualib/cloud/qiniu/sms.lua | 18 +-- lualib/cloud/qiniu/stream.lua | 245 ++++++++++++++++++++++++++++++++++ lualib/cloud/qiniu/token.lua | 118 ++++++++++++++++ script/test_qiniu.lua | 121 +++++++++++++---- 6 files changed, 496 insertions(+), 99 deletions(-) create mode 100644 lualib/cloud/qiniu/censor.lua create mode 100644 lualib/cloud/qiniu/stream.lua create mode 100644 lualib/cloud/qiniu/token.lua diff --git a/lualib/cloud/qiniu/censor.lua b/lualib/cloud/qiniu/censor.lua new file mode 100644 index 00000000..3e27bdea --- /dev/null +++ b/lualib/cloud/qiniu/censor.lua @@ -0,0 +1,34 @@ +local token = require "cloud.qiniu.token" +local httpc = require "httpc" +local json = require "json" + +local Censor = { __Version__ = 0.1, host = "ai.qiniuapi.com" } + +--[[ + 内容审核: image 图片审核, video 视频审核 +]] + +-- 图片内容审核 +function Censor.newImageCensor (AccessKey, SecretKey, image_uri, params) + local path = "/v3/image/censor" + local body = json.encode({ data = { uri = image_uri or "" }, params = params or { scenes = {"pulp", "terror", "politician"} , detail = true } }) + local Authorization = token.newAuthorization(AccessKey, SecretKey, { method = "POST", path = path, host = Censor.host, body = body}) + return httpc.json("https://" .. Censor.host .. path, { {"Authorization", Authorization} }, body) +end + +-- 视频内容审核 +function Censor.newVideoCensor (AccessKey, SecretKey, video_uri, params) + local path = "/v3/video/censor" + local body = json.encode({ data = { uri = video_uri or "" }, params = params or { scenes = {"pulp", "terror", "politician"} , cut_param = { interval_msecs = 5000 } } }) + local Authorization = token.newAuthorization(AccessKey, SecretKey, { method = "POST", path = path, host = Censor.host, body = body}) + return httpc.json("https://" .. Censor.host .. path, { {"Authorization", Authorization} }, body) +end + +-- 根据job_id检查视频审核结果 +function Censor.getVideoCensorResult (AccessKey, SecretKey, job_id) + local path = "/v3/jobs/video/" .. job_id + local Authorization = token.newAuthorization(AccessKey, SecretKey, { method = "GET", path = path, host = Censor.host }) + return httpc.get("https://" .. Censor.host .. path, { {"Authorization", Authorization} }) +end + +return Censor diff --git a/lualib/cloud/qiniu/oss.lua b/lualib/cloud/qiniu/oss.lua index a9fcd2b1..37d56b55 100644 --- a/lualib/cloud/qiniu/oss.lua +++ b/lualib/cloud/qiniu/oss.lua @@ -1,66 +1,11 @@ -local crypt = require "crypt" -local json = require "json" +local token = require "cloud.qiniu.token" -local type = type +local oss = { __Version__ = 0.1, getUploadToken = token.getUploadToken, getDownloadToken = token.getDownloadToken } -local os_time = os.time -local find = string.find -local toint = math.tointeger - - -local oss = { __Version__ = 0.1 } --[[ 此为七牛云对象存储服务的上传与下载Token生成库的原生lua实现. 此库实现了服务端根据指定算法生成临时上传/下载的授权Token后交由客户端上传文件, 服务端不负责具体业务. 具体使用方法请参考: https://developer.qiniu.com/kodo/manual/1644/security ]] --- 检查url是否合法 -local function check_domain (domain) - if type(domain) == 'string' and find(domain, "http[s]?://([%w]+)") then - return domain - end -end - -function oss.getUploadToken (AccessKey, SecretKey, roles) - if type(AccessKey) ~= 'string' or AccessKey == '' then - return nil, "invaild AccessKey." - end - if type(SecretKey) ~= 'string' or SecretKey == '' then - return nil, "invaild SecretKey." - end - if type(roles) ~= 'table' or not next(roles) then - return nil, "invaild roles." - end - if type(roles.bucket) ~= 'string' or roles.bucket == '' then - return nil, "invaild roles.bucket." - end - local Policy = { - scope = table.concat({roles.bucket, roles.prefix}, ':'), -- 上传可自行是否加入上传前缀 - deadline = toint(roles.deadline) or os_time() + 180, -- 默认超时时间为3分钟 - callbackUrl = check_domain(roles.callbackurl), -- 回调地址 - callbackBody = check_domain(roles.callbackbody), -- 回调地址内容 - insertOnly = toint(roles.insertonly) == 1 and 1 or 0, -- 是否可以写入覆盖 - fsizeMin = toint(roles.fsizemin), -- 限定上传文件大小最小值(单位Byte) - fsizeLimit = toint(roles.fsizeLimit), -- 限定上传文件大小最大值(单位Byte), 超过限制上传文件大小的最大值会返回 413 状态码. - fileType = toint(roles.filetype) ~= 0 and 1 or 0, -- 文件存储类型(0 为普通存储,1 为低频存储) 默认为: 0 - mimeLimit = type(roles.mimeLimit) == 'string' and roles.mimeLimit or nil, -- 限制文件类型 - } - local encodedPutPolicy = crypt.base64encode(json.encode(Policy)) - return AccessKey .. ':' .. crypt.base64encode(crypt.hmac_sha1(SecretKey, encodedPutPolicy)) .. ':' .. encodedPutPolicy -end - -function oss.getDownloadToken (AccessKey, SecretKey, opt) - if type(AccessKey) ~= 'string' or AccessKey == '' then - return nil, "invaild AccessKey." - end - if type(SecretKey) ~= 'string' or SecretKey == '' then - return nil, "invaild SecretKey." - end - if type(opt) ~= 'table' or not opt.url or not toint(opt.expires) then - return nil, "invaild roles." - end - return table.concat({ opt.url .. '?'..'e='.. opt.expires, 'token='.. AccessKey .. ':' .. crypt.base64encode(crypt.hmac_sha1(SecretKey, opt.url)) }, '&') -end - return oss diff --git a/lualib/cloud/qiniu/sms.lua b/lualib/cloud/qiniu/sms.lua index 866ec5d9..d4b10e8a 100644 --- a/lualib/cloud/qiniu/sms.lua +++ b/lualib/cloud/qiniu/sms.lua @@ -1,30 +1,16 @@ +local token = require "cloud.qiniu.token" local httpc = require "httpc" local crypt = require "crypt" local json = require "json" local type = type -local sms = { __Version__ = 0.1 } +local sms = { __Version__ = 0.1, newAuthorization = token.newAuthorization } --[[ 此为七牛云短信服务的lua版实现. 提供了包含短信的发送/记录查询/获取模板/删除模板/获取签名/删除签名/ ]] --- 生成管理凭证 -function sms.newAuthorization (AccessKey, SecretKey, opt) - local auth = opt.method .. ' ' .. opt.path - if opt.query then - auth = auth .. opt.query - end - auth = auth .. '\nHost: ' .. opt.host - if opt.body then - auth = auth .. '\nContent-Type: application/json\n\n' .. opt.body - else - auth = auth .. '\n\n' - end - return 'Qiniu'.. ' ' .. AccessKey .. ':' .. crypt.base64encode(crypt.hmac_sha1(SecretKey, auth)) -end - -- 获取短信模板列表 function sms.getTemplates (AccessKey, SecretKey, opt) if type(AccessKey) ~= 'string' or AccessKey == '' then diff --git a/lualib/cloud/qiniu/stream.lua b/lualib/cloud/qiniu/stream.lua new file mode 100644 index 00000000..1a5c19eb --- /dev/null +++ b/lualib/cloud/qiniu/stream.lua @@ -0,0 +1,245 @@ +local token = require "cloud.qiniu.token" +local httpc = require "httpc" +local json = require "json" + +local crypt = require "crypt" +local hmac_sha1 = crypt.hmac_sha1 +local urlencode = crypt.urlencode +local base64encode = crypt.base64encode + +local type = type +local time = os.time +local concat = table.concat + +local Stream = { __Version__ = 0.1, domain = "https://pili.qiniuapi.com" } + +-- 创建流 +function Stream.createStream (AccessKey, SecretKey, hub, key) + if type(AccessKey) ~= 'string' or AccessKey == '' then + return nil, "invaild AccessKey." + end + if type(SecretKey) ~= 'string' or SecretKey == '' then + return nil, "invaild SecretKey." + end + if type(hub) ~= 'string' or hub == '' then + return nil, "invaild hub." + end + if type(key) ~= 'string' or key == '' then + return nil, "invaild key." + end + local path = "/v2/hubs/" .. hub .. "/streams" + local Authorization, QboxAuthorization = token.getAccessToken(AccessKey, SecretKey, {path = path}) + return httpc.json(Stream.domain .. path, { {"Authorization", QboxAuthorization } }, json.encode({ {"key", key} })) +end + +-- 查询流 +function Stream.queryStream (AccessKey, SecretKey, hub, key) + if type(AccessKey) ~= 'string' or AccessKey == '' then + return nil, "invaild AccessKey." + end + if type(SecretKey) ~= 'string' or SecretKey == '' then + return nil, "invaild SecretKey." + end + if type(hub) ~= 'string' or hub == '' then + return nil, "invaild hub." + end + if type(key) ~= 'string' or key == '' then + return nil, "invaild key." + end + local path = "/v2/hubs/" .. hub .. "/streams/" .. base64encode(key) + local Authorization, QboxAuthorization = token.getAccessToken(AccessKey, SecretKey, { path = path }) + return httpc.get(Stream.domain.. path, { {"Authorization", QboxAuthorization}, {"Content-Type", "application/x-www-form-urlencoded"} }) +end + +-- 查询流列表 +function Stream.queryStreams (AccessKey, SecretKey, hub, opt) + if type(AccessKey) ~= 'string' or AccessKey == '' then + return nil, "invaild AccessKey." + end + if type(SecretKey) ~= 'string' or SecretKey == '' then + return nil, "invaild SecretKey." + end + if type(hub) ~= 'string' or hub == '' then + return nil, "invaild hub." + end + local args, querys = {} + if opt.liveonly then + args[#args+1] = {"liveonly", "true"} + end + if opt.prefix then + args[#args+1] = {"prefix", opt.prefix} + end + if opt.marker then + args[#args+1] = {"marker", opt.marker} + end + if opt.limit then + args[#args+1] = {"limit", opt.limit} + end + if #args > 1 then + local query = {} + for _, item in ipairs(args) do + query[#query+1] = urlencode(item[1]) .. '=' .. urlencode(item[2]) + end + querys = concat(query, "&") + end + local path = "/v2/hubs/" .. hub .. "/streams" .. (querys and '?' .. querys or '') + local Authorization, QboxAuthorization = token.getAccessToken(AccessKey, SecretKey, { path = path, args = args }) + return httpc.get(Stream.domain.. path, { {"Authorization", QboxAuthorization}, {"Content-Type", "application/x-www-form-urlencoded"} }) +end + +-- 禁止推流 +function Stream.disableStream (AccessKey, SecretKey, hub, key) + if type(AccessKey) ~= 'string' or AccessKey == '' then + return nil, "invaild AccessKey." + end + if type(SecretKey) ~= 'string' or SecretKey == '' then + return nil, "invaild SecretKey." + end + if type(hub) ~= 'string' or hub == '' then + return nil, "invaild hub." + end + if type(key) ~= 'string' or key == '' then + return nil, "invaild key." + end + local path = "/v2/hubs/" .. hub .. "/streams/" .. base64encode(key) .. '/disabled' + local Authorization, QboxAuthorization = token.getAccessToken(AccessKey, SecretKey, { path = path }) + return httpc.json(Stream.domain.. path, { {"Authorization", QboxAuthorization} }, json.encode({ {"disabledTill", key} })) +end + +-- 获取指定流相关信息 +function Stream.getStreamLive (AccessKey, SecretKey, hub, key) + if type(AccessKey) ~= 'string' or AccessKey == '' then + return nil, "invaild AccessKey." + end + if type(SecretKey) ~= 'string' or SecretKey == '' then + return nil, "invaild SecretKey." + end + if type(hub) ~= 'string' or hub == '' then + return nil, "invaild hub." + end + if type(key) ~= 'string' or key == '' then + return nil, "invaild key." + end + local path = "/v2/hubs/" .. hub .. "/streams/" .. base64encode(key) .. '/live' + local Authorization, QboxAuthorization = token.getAccessToken(AccessKey, SecretKey, { path = path }) + return httpc.get(Stream.domain.. path, { {"Authorization", QboxAuthorization }, {"Content-Type", "application/x-www-form-urlencoded"} }) +end + +-- 批量获取指定流相关信息 +function Stream.getStreamLives (AccessKey, SecretKey, hub, lives) + if type(AccessKey) ~= 'string' or AccessKey == '' then + return nil, "invaild AccessKey." + end + if type(SecretKey) ~= 'string' or SecretKey == '' then + return nil, "invaild SecretKey." + end + if type(hub) ~= 'string' or hub == '' then + return nil, "invaild hub." + end + if type(lives) ~= 'table' or (#lives <= 0 or #lives > 100) then + return nil, "invaild lives. 0 < lives < 100" + end + local path = "/v2/hubs/" .. hub .. "/livestreams" + local Authorization, QboxAuthorization = token.getAccessToken(AccessKey, SecretKey, { path = path }) + return httpc.json(Stream.domain.. path, { {"Authorization", QboxAuthorization } }, json.encode(lives)) +end + +-- 查询直播流历史记录 +function Stream.getStreamHistory (AccessKey, SecretKey, hub, key, opt) + if type(AccessKey) ~= 'string' or AccessKey == '' then + return nil, "invaild AccessKey." + end + if type(SecretKey) ~= 'string' or SecretKey == '' then + return nil, "invaild SecretKey." + end + if type(hub) ~= 'string' or hub == '' then + return nil, "invaild hub." + end + if type(key) ~= 'string' or key == '' then + return nil, "invaild key." + end + local args = "start=" .. (type(opt) == 'table' and opt['start'] or 0) .. "&" .. "end=" .. (type(opt) == 'table' and opt['end'] or 0) + local path = "/v2/hubs/" .. hub .. "/streams/" .. base64encode(key) .. '/historyactivity?' .. args + local Authorization, QboxAuthorization = token.getAccessToken(AccessKey, SecretKey, { path = path, args = args }) + return httpc.get(Stream.domain.. path, { {"Authorization", QboxAuthorization }, {"Content-Type", "application/x-www-form-urlencoded"} }) +end + +-- 生成rtmp推流地址 +function Stream.rtmpPublishUrl (AccessKey, SecretKey, domain, hub, key, expire) + if type(AccessKey) ~= 'string' or AccessKey == '' then + return nil, "invaild AccessKey." + end + if type(SecretKey) ~= 'string' or SecretKey == '' then + return nil, "invaild SecretKey." + end + if type(domain) ~= 'string' or domain == '' then + return nil, "invaild domain." + end + if type(hub) ~= 'string' or hub == '' then + return nil, "invaild hub." + end + if type(key) ~= 'string' or key == '' then + return nil, "invaild key." + end + local path = '/' .. hub .. '/' .. key .. '?' .. ( expire and 'e='..expire or '' ) + local token = AccessKey .. ':' .. base64encode(hmac_sha1(SecretKey, path)) + return "rtmp://" .. domain .. path .. ( expire and '&token=' .. token or 'token=' .. token ) +end + +-- 获取rtmp观播地址 +function Stream.rtmpSubscribeUrl (domain, hub, key) + if type(domain) ~= 'string' or domain == '' then + return nil, "invaild domain." + end + if type(hub) ~= 'string' or hub == '' then + return nil, "invaild hub." + end + if type(key) ~= 'string' or key == '' then + return nil, "invaild key." + end + return concat({'rtmp://' .. domain, hub, key}, "/") +end + +-- 获取HLS观播地址 +function Stream.hlsSubscribeUrl (domain, hub, key) + if type(domain) ~= 'string' or domain == '' then + return nil, "invaild domain." + end + if type(hub) ~= 'string' or hub == '' then + return nil, "invaild hub." + end + if type(key) ~= 'string' or key == '' then + return nil, "invaild key." + end + return concat({'http://' .. domain, hub, key..".m3u8"}, "/") +end + +-- 获取HDL观播地址 +function Stream.hdlSubscribeUrl (domain, hub, key) + if type(domain) ~= 'string' or domain == '' then + return nil, "invaild domain." + end + if type(hub) ~= 'string' or hub == '' then + return nil, "invaild hub." + end + if type(key) ~= 'string' or key == '' then + return nil, "invaild key." + end + return concat({'http://' .. domain, hub, key..".flv"}, "/") +end + +-- 直播快照地址 +function Stream.getSnapshot (domain, hub, key) + if type(domain) ~= 'string' or domain == '' then + return nil, "invaild domain." + end + if type(hub) ~= 'string' or hub == '' then + return nil, "invaild hub." + end + if type(key) ~= 'string' or key == '' then + return nil, "invaild key." + end + return concat({'http://' .. domain, hub, key..".jpg"}, "/") +end + +return Stream diff --git a/lualib/cloud/qiniu/token.lua b/lualib/cloud/qiniu/token.lua new file mode 100644 index 00000000..93e7b82b --- /dev/null +++ b/lualib/cloud/qiniu/token.lua @@ -0,0 +1,118 @@ +local json = require "json" +local crypt = require "crypt" +local hmac_sha1 = crypt.hmac_sha1 +local urlencode = crypt.urlencode +local base64encode = crypt.base64encode + +local type = type +local next = next +local ipairs = ipairs +local os_time = os.time +local find = string.find +local toint = math.tointeger +local concat = table.concat + +local function build_sign (path, args, body) + local req = path + if type(args) == 'table' then + local query = {} + for _, item in ipairs(args) do + query[#query+1] = urlencode(item[1]) .. '=' .. urlencode(item[2]) + end + if #query > 0 then + req = req .. '?' .. concat(query, "&") + end + end + return req .. '\n' .. (type(body) == 'string' and body or '') +end + +local Token = { __Version__ = 0.1 } + +-- 检查url是否合法 +local function check_domain (domain) + if type(domain) == 'string' and find(domain, "http[s]?://([%w]+)") then + return domain + end +end + +-- 管理凭证 +--[[ + opt.path : 请求路径(必填) + opt.args : 请求参数(选填) + opt.body : 请求体内容(选填) +]] +function Token.getAccessToken (AccessKey, SecretKey, opt) + if type(AccessKey) ~= 'string' or AccessKey == '' then + return nil, "invaild AccessKey." + end + if type(SecretKey) ~= 'string' or SecretKey == '' then + return nil, "invaild SecretKey." + end + if type(opt) ~= 'table' or not next(opt) then + return nil, "invaild opt." + end + local sign_str = build_sign(opt.path, opt.args, opt.body) + local hash_sign = hmac_sha1(SecretKey, sign_str) + local Authorization = AccessKey .. ':' .. base64encode(hash_sign) + return Authorization, "QBox " .. Authorization +end + +-- Auth凭证 +function Token.newAuthorization (AccessKey, SecretKey, opt) + local auth = opt.method .. ' ' .. opt.path + if opt.query then + auth = auth .. opt.query + end + auth = auth .. '\nHost: ' .. opt.host + if opt.body then + auth = auth .. '\nContent-Type: application/json\n\n' .. opt.body + else + auth = auth .. '\n\n' + end + return 'Qiniu'.. ' ' .. AccessKey .. ':' .. crypt.base64encode(crypt.hmac_sha1(SecretKey, auth)) +end + +-- 上传凭证 +function Token.getUploadToken (AccessKey, SecretKey, roles) + if type(AccessKey) ~= 'string' or AccessKey == '' then + return nil, "invaild AccessKey." + end + if type(SecretKey) ~= 'string' or SecretKey == '' then + return nil, "invaild SecretKey." + end + if type(roles) ~= 'table' or not next(roles) then + return nil, "invaild roles." + end + if type(roles.bucket) ~= 'string' or roles.bucket == '' then + return nil, "invaild roles.bucket." + end + local Policy = { + scope = concat({roles.bucket, roles.prefix}, ':'), -- 上传可自行是否加入上传前缀 + deadline = toint(roles.deadline) or os_time() + 180, -- 默认超时时间为3分钟 + callbackUrl = check_domain(roles.callbackurl), -- 回调地址 + callbackBody = check_domain(roles.callbackbody), -- 回调地址内容 + insertOnly = toint(roles.insertonly) == 1 and 1 or 0, -- 是否可以写入覆盖 + fsizeMin = toint(roles.fsizemin), -- 限定上传文件大小最小值(单位Byte) + fsizeLimit = toint(roles.fsizeLimit), -- 限定上传文件大小最大值(单位Byte), 超过限制上传文件大小的最大值会返回 413 状态码. + fileType = toint(roles.filetype) ~= 0 and 1 or 0, -- 文件存储类型(0 为普通存储,1 为低频存储) 默认为: 0 + mimeLimit = type(roles.mimeLimit) == 'string' and roles.mimeLimit or nil, -- 限制文件类型 + } + local encodedPutPolicy = crypt.base64encode(json.encode(Policy)) + return AccessKey .. ':' .. crypt.base64encode(crypt.hmac_sha1(SecretKey, encodedPutPolicy)) .. ':' .. encodedPutPolicy +end + +-- 下载凭证 +function Token.getDownloadToken (AccessKey, SecretKey, opt) + if type(AccessKey) ~= 'string' or AccessKey == '' then + return nil, "invaild AccessKey." + end + if type(SecretKey) ~= 'string' or SecretKey == '' then + return nil, "invaild SecretKey." + end + if type(opt) ~= 'table' or not opt.url or not toint(opt.expires) then + return nil, "invaild roles." + end + return concat({ opt.url .. '?'..'e='.. opt.expires, 'token='.. AccessKey .. ':' .. crypt.base64encode(crypt.hmac_sha1(SecretKey, opt.url)) }, '&') +end + +return Token diff --git a/script/test_qiniu.lua b/script/test_qiniu.lua index dab8f2b1..465795f5 100644 --- a/script/test_qiniu.lua +++ b/script/test_qiniu.lua @@ -1,39 +1,108 @@ +local token = require "cloud.qiniu.token" +local censor = require "cloud.qiniu.censor" +local Stream = require "cloud.qiniu.Stream" local sms = require "cloud.qiniu.sms" local oss = require "cloud.qiniu.oss" +local LOG = require "logging" local AccessKey = 'Your_Real_AccessKey' local SecretKey = 'Your_Real_SecretKey' --- 发送短信 +-- -- *******短信服务******** +-- +-- -- 发送短信 -- local ok, err = sms.sendSMS(AccessKey, SecretKey, { template_id = '1174104085482180608', mobiles = {'+8613000000000'}, parameters = { code = tostring(math.random(1, 1024)) }}) --- require"logging":DEBUG(ok, err) - --- 获取短信模板列表 +-- LOG:DEBUG(ok, err) +-- +-- -- 获取短信模板列表 -- local ok, err = sms.getTemplates(AccessKey, SecretKey, { page = 1, page_size = 100 }) --- require"logging":DEBUG(ok, err) - --- 获取短信签名列表 +-- LOG:DEBUG(ok, err) +-- +-- -- 获取短信签名列表 -- local ok, err = sms.getSignatures(AccessKey, SecretKey, { page = 1, page_size = 90 }) --- require"logging":DEBUG(ok, err) - --- 获取发送短信记录 +-- LOG:DEBUG(ok, err) +-- +-- -- 获取发送短信记录 -- local ok, err = sms.getSMSRecord(AccessKey, SecretKey, { start = '1568131200', ['end'] = '1568822399'}) --- require"logging":DEBUG(ok, err) - --- 删除模板 +-- LOG:DEBUG(ok, err) +-- +-- -- 删除模板 -- local ok, err = sms.detTemplate(AccessKey, SecretKey, '1174104085482180608') --- require"logging":DEBUG(ok, err) - --- 删除签名 +-- LOG:DEBUG(ok, err) +-- +-- -- 删除签名 -- local ok, err = sms.delSignature(AccessKey, SecretKey, '1174104085482180608') --- require"logging":DEBUG(ok, err) - --- *************** - --- 生成上传token +-- LOG:DEBUG(ok, err) +-- +-- -- *******Token与验权******** +-- +-- -- 生成上传token -- local upToken = oss.getUploadToken(AccessKey, SecretKey, { bucket = 'candymi' }) --- require"logging":DEBUG(upToken) - --- 生成下载token --- local upToken = oss.getDownloadToken(AccessKey, SecretKey, { url = 'https://www.gitub.com/a.img', expires = os.time() + 180 }) --- require"logging":DEBUG(upToken) +-- LOG:DEBUG(upToken) +-- +-- -- 生成下载token +-- local upToken, err = oss.getDownloadToken(AccessKey, SecretKey, { url = 'https://www.gitub.com/a.img', expires = os.time() + 180 }) +-- LOG:DEBUG(upToken, err) +-- +-- +-- local url = "http://rs.qiniu.com/move/bmV3ZG9jczpmaW5kX21hbi50eHQ=/bmV3ZG9jczpmaW5kLm1hbi50eHQ=" +-- LOG:DEBUG(token.getAccessToken(AccessKey, SecretKey, { path = "/move/bmV3ZG9jczpmaW5kX21hbi50eHQ=/bmV3ZG9jczpmaW5kLm1hbi50eHQ=" })) +-- +-- -- *******直播服务******** +-- +-- -- 创建推流 +-- local code, ret = Stream.createStream(AccessKey, SecretKey, 'hub', 'candymi') +-- LOG:DEBUG(code, ret) +-- +-- -- 查询推流 +-- local code, ret = Stream.queryStream(AccessKey, SecretKey, 'hub', 'candymi') +-- LOG:DEBUG(code, ret) +-- +-- -- 关闭推流 +-- local code, ret = Stream.disableStream(AccessKey, SecretKey, 'hub', 'candymi') +-- LOG:DEBUG(code, ret) +-- +-- -- 查询推流详情 +-- local code, ret = Stream.getStreamLive(AccessKey, SecretKey, 'hub', 'candymi') +-- LOG:DEBUG(code, ret) +-- +-- -- 批量查询推流详情 +-- local code, ret = Stream.getStreamLives(AccessKey, SecretKey, 'hub', {'candymi', "abc", "def"}) +-- LOG:DEBUG(code, ret) +-- +-- -- 批量查询推流历史记录 +-- local code, ret = Stream.getStreamHistory(AccessKey, SecretKey, 'hub', 'candymi', {["start"] = os.time(), ["end"] = os.time()}) +-- LOG:DEBUG(code, ret) +-- +-- -- rtmp推流地址 +-- local url = Stream.rtmpPublishUrl(AccessKey, SecretKey, "pub-rtmp.test.com", 'hub', 'candymi') +-- LOG:DEBUG(url) +-- +-- -- rtmp观看地址 +-- local url = Stream.rtmpSubscribeUrl("sub-rtmp.test.com", 'hub', 'candymi') +-- LOG:DEBUG(url) +-- +-- -- hls观看地址 +-- local url = Stream.hlsSubscribeUrl("sub-hls.test.com", 'hub', 'candymi') +-- LOG:DEBUG(url) +-- +-- -- hdl观看地址 +-- local url = Stream.hdlSubscribeUrl("sub-hdl.test.com", 'hub', 'candymi') +-- LOG:DEBUG(url) +-- +-- -- 封面快照地址 +-- local url = Stream.getSnapshot("snap-shot.test.com", 'hub', 'candymi') +-- LOG:DEBUG(url) +-- +-- -- *******内容服务******** +-- +-- local code, ret = censor.newImageCensor(AccessKey, SecretKey, "http://img3.redocn.com/20120528/Redocn_2012052817213559.jpg") +-- LOG:DEBUG(code, ret) +-- +-- local code, ret = censor.newVideoCensor(AccessKey, SecretKey, "https://www.w3school.com.cn/i/movie.ogg") +-- LOG:DEBUG(code, ret) +-- +-- local job_id = "5d90cb95093ff70008e32e0a" +-- +-- local code, ret = censor.getVideoCensorResult(AccessKey, SecretKey, job_id) +-- LOG:DEBUG(code, ret) From df85d5a108117349b807cfdefb70528658d026fd Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 30 Sep 2019 06:21:52 +0800 Subject: [PATCH 367/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 25 +++++----- luaclib/Makefile | 83 ++++++++++++++++++++++---------- luaclib/src/lcjson/Makefile | 8 +-- luaclib/src/lcrypt/Makefile | 8 +-- luaclib/src/lffi/makefile | 8 +-- luaclib/src/lfs/Makefile | 8 +-- luaclib/src/lhttpparser/Makefile | 8 +-- luaclib/src/lmsgpack/Makefile | 8 +-- luaclib/src/lpbc/Makefile | 8 +-- luaclib/src/lpeg/makefile | 8 +-- src/Makefile | 20 ++++---- 11 files changed, 113 insertions(+), 79 deletions(-) diff --git a/Makefile b/Makefile index ca8ea44d..9c5a9831 100644 --- a/Makefile +++ b/Makefile @@ -13,18 +13,21 @@ default : # 3. lualib/src/cjson/Makefile build : - cd src && make build - cd luaclib && make build - cd 3rd && make build + + @cd src && make build + @cd luaclib && make build + @cd 3rd && make build rebuild : - rm -rf main cfadmin libcore.so *.pid - cd src && make clean && make rebuild - cd luaclib && make clean && make rebuild - cd 3rd && make clean && make rebuild + + @rm -rf main cfadmin libcore.so *.pid + @cd src && make clean && make rebuild + @cd luaclib && make clean && make rebuild + @cd 3rd && make clean && make rebuild clean : - rm -rf main cfadmin libcore.so *.pid - cd src && make clean - cd luaclib && make clean - cd 3rd && make clean + + @rm -rf main cfadmin libcore.so *.pid + @cd src && make clean + @cd luaclib && make clean + @cd 3rd && make clean diff --git a/luaclib/Makefile b/luaclib/Makefile index dece33d1..fa7343d0 100644 --- a/luaclib/Makefile +++ b/luaclib/Makefile @@ -14,37 +14,68 @@ LIBS += -L./ -L../ -L/usr/local/lib CFLAGS = -Wall -O3 -fPIC --shared build : - $(CC) -o sys.so src/lsys.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua - $(CC) -o udp.so src/ludp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua - $(CC) -o task.so src/ltask.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua - $(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua - $(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore -llua + + ### 以下为内置模块位置 ### + @echo "CC - lsys" + @$(CC) -o sys.so src/lsys.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua + @echo "CC - ludp" + @$(CC) -o udp.so src/ludp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua + @echo "CC - ltask" + @$(CC) -o task.so src/ltask.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua + @echo "CC - ltimer" + @$(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua + @echo "CC - ltcp" + @$(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore -llua ### 以下为内置第三方库编译位置 ### - cd src/lfs && rm -rf *.o *.so && make build # 增加lfs库 - cd src/lffi && rm -rf *.o *.so && make build # 增加lffi库 - cd src/lpbc && rm -rf *.o *.so && make build # 增加lpbc库 - cd src/lpeg && rm -rf *.o *.so && make build # 增加lpeg库 - cd src/lcrypt && rm -rf *.o *.so && make build # 增加lcrypt库 - cd src/lcjson && rm -rf *.o *.so && make build # 增加cjson库 - cd src/lmsgpack && rm -rf *.o *.so && make build # 增加lmsgpack库 - cd src/lhttpparser && rm -rf *.o *.so && make build # 增加PicoHTTPParser库 + @echo "CC - lfs" + @cd src/lfs && rm -rf *.o *.so && make build # 增加lfs库 + @echo "CC - lffi" + @cd src/lffi && rm -rf *.o *.so && make build # 增加lffi库 + @echo "CC - lpbc" + @cd src/lpbc && rm -rf *.o *.so && make build # 增加lpbc库 + @echo "CC - lpeg" + @cd src/lpeg && rm -rf *.o *.so && make build # 增加lpeg库 + @echo "CC - lcrypt" + @cd src/lcrypt && rm -rf *.o *.so && make build # 增加lcrypt库 + @echo "CC - lcjson" + @cd src/lcjson && rm -rf *.o *.so && make build # 增加cjson库 + @echo "CC - lmsgpack" + @cd src/lmsgpack && rm -rf *.o *.so && make build # 增加lmsgpack库 + @echo "CC - lhttpparser" + @cd src/lhttpparser && rm -rf *.o *.so && make build # 增加PicoHTTPParser库 rebuild : - $(CC) -o sys.so src/lsys.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua - $(CC) -o udp.so src/ludp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua - $(CC) -o task.so src/ltask.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua - $(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua - $(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore -llua + + ### 以下为内置模块位置 ### + @echo "CC - lsys" + @$(CC) -o sys.so src/lsys.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua + @echo "CC - ludp" + @$(CC) -o udp.so src/ludp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua + @echo "CC - ltask" + @$(CC) -o task.so src/ltask.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua + @echo "CC - ltimer" + @$(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua + @echo "CC - ltcp" + @$(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore -llua ### 以下为内置第三方库编译位置 ### - cd src/lfs && rm -rf *.o *.so && make build # 增加lfs库 - cd src/lffi && rm -rf *.o *.so && make build # 增加lffi库 - cd src/lpbc && rm -rf *.o *.so && make build # 增加lpbc库 - cd src/lpeg && rm -rf *.o *.so && make build # 增加lpeg库 - cd src/lcrypt && rm -rf *.o *.so && make build # 增加lcrypt库 - cd src/lcjson && rm -rf *.o *.so && make build # 增加cjson库 - cd src/lmsgpack && rm -rf *.o *.so && make build # 增加lmsgpack库 - cd src/lhttpparser && rm -rf *.o *.so && make build # 增加PicoHTTPParser库 + @echo "CC - lfs" + @cd src/lfs && rm -rf *.o *.so && make build # 增加lfs库 + @echo "CC - lffi" + @cd src/lffi && rm -rf *.o *.so && make build # 增加lffi库 + @echo "CC - lpbc" + @cd src/lpbc && rm -rf *.o *.so && make build # 增加lpbc库 + @echo "CC - lpeg" + @cd src/lpeg && rm -rf *.o *.so && make build # 增加lpeg库 + @echo "CC - lcrypt" + @cd src/lcrypt && rm -rf *.o *.so && make build # 增加lcrypt库 + @echo "CC - lcjson" + @cd src/lcjson && rm -rf *.o *.so && make build # 增加cjson库 + @echo "CC - lmsgpack" + @cd src/lmsgpack && rm -rf *.o *.so && make build # 增加lmsgpack库 + @echo "CC - lhttpparser" + @cd src/lhttpparser && rm -rf *.o *.so && make build # 增加PicoHTTPParser库 clean : + rm -rf *.so diff --git a/luaclib/src/lcjson/Makefile b/luaclib/src/lcjson/Makefile index 933860f3..eb2a4219 100644 --- a/luaclib/src/lcjson/Makefile +++ b/luaclib/src/lcjson/Makefile @@ -17,12 +17,12 @@ LIBS = -L../ -L../../../ -L/usr/local/lib DLL = -lcore -llua build: - $(CC) -o cjson.so lua_cjson.c fpconv.c strbuf.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) - mv *.so ../../ + @$(CC) -o cjson.so lua_cjson.c fpconv.c strbuf.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) + @mv *.so ../../ rebuild: $(OBJS) - $(CC) -o cjson.so lua_cjson.c fpconv.c strbuf.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) - mv *.so ../../ + @$(CC) -o cjson.so lua_cjson.c fpconv.c strbuf.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) + @mv *.so ../../ clean: rm -rf *.o *.so diff --git a/luaclib/src/lcrypt/Makefile b/luaclib/src/lcrypt/Makefile index 4c838a69..e5a1eeb7 100644 --- a/luaclib/src/lcrypt/Makefile +++ b/luaclib/src/lcrypt/Makefile @@ -16,12 +16,12 @@ INCLUDES = -I../../../ -I/usr/local/include LIBS = -L../ -L../../../ -L/usr/local/lib build: - $(CC) -o lcrypt.so lcrypt.c crc.c md5.c url.c sha1.c sha2.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) - mv *.so ../../ + @$(CC) -o lcrypt.so lcrypt.c crc.c md5.c url.c sha1.c sha2.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) + @mv *.so ../../ rebuild: - $(CC) -o lcrypt.so lcrypt.c crc.c md5.c url.c sha1.c sha2.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) - mv *.so ../../ + @$(CC) -o lcrypt.so lcrypt.c crc.c md5.c url.c sha1.c sha2.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) + @mv *.so ../../ clean: rm -rf *.o *.so diff --git a/luaclib/src/lffi/makefile b/luaclib/src/lffi/makefile index 25cc0d2f..7bc8daf1 100644 --- a/luaclib/src/lffi/makefile +++ b/luaclib/src/lffi/makefile @@ -16,12 +16,12 @@ CFLAGS = -O3 -Wall -shared -fPIC -Wno-unused-function -fno-strict-aliasing -Wno- DLL = -lcore -llua build: - $(CC) -o lffi.so ffi.c parser.c ctype.c $(CFLAGS) $(INCLUDE) $(LIB) $(DLL) - mv *.so ../../ + @$(CC) -o lffi.so ffi.c parser.c ctype.c $(CFLAGS) $(INCLUDE) $(LIB) $(DLL) + @mv *.so ../../ rebuild: - $(CC) -o lffi.so ffi.c parser.c ctype.c $(CFLAGS) $(INCLUDE) $(LIB) $(DLL) - mv *.so ../../ + @$(CC) -o lffi.so ffi.c parser.c ctype.c $(CFLAGS) $(INCLUDE) $(LIB) $(DLL) + @mv *.so ../../ clean: rm -rf *.o *.so diff --git a/luaclib/src/lfs/Makefile b/luaclib/src/lfs/Makefile index eefe8eba..f44193da 100644 --- a/luaclib/src/lfs/Makefile +++ b/luaclib/src/lfs/Makefile @@ -16,12 +16,12 @@ CFLAGS = -O3 -Wall -shared -fPIC DLL = -lcore -llua build: - $(CC) -o lfs.so lfs.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) - mv *.so ../../ + @$(CC) -o lfs.so lfs.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) + @mv *.so ../../ rebuild: - $(CC) -o lfs.so lfs.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) - mv *.so ../../ + @$(CC) -o lfs.so lfs.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) + @mv *.so ../../ clean: rm -rf *.o *.so diff --git a/luaclib/src/lhttpparser/Makefile b/luaclib/src/lhttpparser/Makefile index 3a97ac9a..f10fab0a 100644 --- a/luaclib/src/lhttpparser/Makefile +++ b/luaclib/src/lhttpparser/Makefile @@ -20,12 +20,12 @@ DLL = -lcore -llua build: - $(CC) -o httpparser.so httpparser.c lhttpparser.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) - mv *.so ../../ + @$(CC) -o httpparser.so httpparser.c lhttpparser.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) + @mv *.so ../../ rebuild: - $(CC) -o httpparser.so httpparser.c lhttpparser.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) - mv *.so ../../ + @$(CC) -o httpparser.so httpparser.c lhttpparser.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) + @mv *.so ../../ clean: rm -rf *.o *.so diff --git a/luaclib/src/lmsgpack/Makefile b/luaclib/src/lmsgpack/Makefile index f1715e7b..028f3f83 100644 --- a/luaclib/src/lmsgpack/Makefile +++ b/luaclib/src/lmsgpack/Makefile @@ -16,12 +16,12 @@ CFLAGS = -O3 -Wall -shared -fPIC DLL = -lcore -llua build: - $(CC) -o lmsgpack.so lmsgpack.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) - mv *.so ../../ + @$(CC) -o lmsgpack.so lmsgpack.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) + @mv *.so ../../ rebuild: - $(CC) -o lmsgpack.so lmsgpack.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) - mv *.so ../../ + @$(CC) -o lmsgpack.so lmsgpack.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) + @mv *.so ../../ clean: rm -rf *.o *.so diff --git a/luaclib/src/lpbc/Makefile b/luaclib/src/lpbc/Makefile index 1394f056..560a289d 100644 --- a/luaclib/src/lpbc/Makefile +++ b/luaclib/src/lpbc/Makefile @@ -16,12 +16,12 @@ CFLAGS = -O3 -Wall -shared -fPIC DLL = -lcore -llua build: - $(CC) -o lprotobuf.so lpb.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) - mv *.so ../../ + @$(CC) -o lprotobuf.so lpb.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) + @mv *.so ../../ rebuild: - $(CC) -o lprotobuf.so lpb.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) - mv *.so ../../ + @$(CC) -o lprotobuf.so lpb.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) + @mv *.so ../../ clean: rm -rf *.o *.so diff --git a/luaclib/src/lpeg/makefile b/luaclib/src/lpeg/makefile index 38465c64..588b6f4c 100644 --- a/luaclib/src/lpeg/makefile +++ b/luaclib/src/lpeg/makefile @@ -14,12 +14,12 @@ CFLAGS = -O3 -shared -fPIC DLL = -lcore -llua build: - $(CC) -o lpeg.so lpcap.c lpcode.c lpprint.c lptree.c lpvm.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) - mv *.so ../../ + @$(CC) -o lpeg.so lpcap.c lpcode.c lpprint.c lptree.c lpvm.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) + @mv *.so ../../ rebuild: - $(CC) -o lpeg.so lpcap.c lpcode.c lpprint.c lptree.c lpvm.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) - mv *.so ../../ + @$(CC) -o lpeg.so lpcap.c lpcode.c lpprint.c lptree.c lpvm.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) + @mv *.so ../../ clean: rm -f *.so *.o diff --git a/src/Makefile b/src/Makefile index c970ad63..7046057d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -29,19 +29,19 @@ MACRO += -w -O3 -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib build : - $(CC) -o libcore.so core.c core_ev.c core_memory.c core_sys.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) - - $(CC) -o cfadmin core_start.c $(MACRO) $(INCLUDES) $(LIBS) -lcore - - mv *.so cfadmin ../ + @$(CC) -o libcore.so core.c core_ev.c core_memory.c core_sys.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) + @echo "CC - libcore" + @$(CC) -o cfadmin core_start.c $(MACRO) $(INCLUDES) $(LIBS) -lcore + @echo "CC - cfadmin" + @mv *.so cfadmin ../ rebuild: - $(CC) -o libcore.so core.c core_ev.c core_memory.c core_sys.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) - - $(CC) -o cfadmin core_start.c $(MACRO) $(INCLUDES) $(LIBS) -lcore - - mv *.so cfadmin ../ + @$(CC) -o libcore.so core.c core_ev.c core_memory.c core_sys.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) + @echo "CC - libcore" + @$(CC) -o cfadmin core_start.c $(MACRO) $(INCLUDES) $(LIBS) -lcore + @echo "CC - cfadmin" + @mv *.so cfadmin ../ clean : From e9543e88322548a695221beb7782c041a2af268b Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 30 Sep 2019 06:51:27 +0800 Subject: [PATCH 368/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0MAKE=E5=91=BD?= =?UTF-8?q?=E4=BB=A4=E4=BD=BF=E7=94=A8=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 21 ++++++++++---------- luaclib/Makefile | 51 ++++++++++++++++++++++++++++-------------------- src/Makefile | 12 ++++-------- 3 files changed, 44 insertions(+), 40 deletions(-) diff --git a/Makefile b/Makefile index 9c5a9831..c059321d 100644 --- a/Makefile +++ b/Makefile @@ -14,20 +14,19 @@ default : build : - @cd src && make build - @cd luaclib && make build - @cd 3rd && make build + ### 编译核心库与可执行文件 ### + @$(MAKE) -C src build + @$(MAKE) -C luaclib build + @$(MAKE) -C 3rd build rebuild : - @rm -rf main cfadmin libcore.so *.pid - @cd src && make clean && make rebuild - @cd luaclib && make clean && make rebuild - @cd 3rd && make clean && make rebuild + ### 编译核心库与可执行文件 ### + @$(MAKE) -C src rebuild + @$(MAKE) -C luaclib rebuild + @$(MAKE) -C 3rd rebuild clean : - @rm -rf main cfadmin libcore.so *.pid - @cd src && make clean - @cd luaclib && make clean - @cd 3rd && make clean + ### 清理所有编译文件与库 ### + rm -rf cfadmin libcore.so luaclib/*.so diff --git a/luaclib/Makefile b/luaclib/Makefile index fa7343d0..3918f09f 100644 --- a/luaclib/Makefile +++ b/luaclib/Makefile @@ -28,21 +28,28 @@ build : @$(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore -llua ### 以下为内置第三方库编译位置 ### @echo "CC - lfs" - @cd src/lfs && rm -rf *.o *.so && make build # 增加lfs库 + @$(MAKE) -C src/lfs build + @echo "CC - lffi" - @cd src/lffi && rm -rf *.o *.so && make build # 增加lffi库 + @$(MAKE) -C src/lffi build + @echo "CC - lpbc" - @cd src/lpbc && rm -rf *.o *.so && make build # 增加lpbc库 + @$(MAKE) -C src/lpbc build + @echo "CC - lpeg" - @cd src/lpeg && rm -rf *.o *.so && make build # 增加lpeg库 + @$(MAKE) -C src/lpeg build + @echo "CC - lcrypt" - @cd src/lcrypt && rm -rf *.o *.so && make build # 增加lcrypt库 + @$(MAKE) -C src/lcrypt build + @echo "CC - lcjson" - @cd src/lcjson && rm -rf *.o *.so && make build # 增加cjson库 + @$(MAKE) -C src/lcjson build + @echo "CC - lmsgpack" - @cd src/lmsgpack && rm -rf *.o *.so && make build # 增加lmsgpack库 + @$(MAKE) -C src/lmsgpack build + @echo "CC - lhttpparser" - @cd src/lhttpparser && rm -rf *.o *.so && make build # 增加PicoHTTPParser库 + @$(MAKE) -C src/lhttpparser build rebuild : @@ -59,23 +66,25 @@ rebuild : @$(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore -llua ### 以下为内置第三方库编译位置 ### @echo "CC - lfs" - @cd src/lfs && rm -rf *.o *.so && make build # 增加lfs库 + @$(MAKE) -C src/lfs build + @echo "CC - lffi" - @cd src/lffi && rm -rf *.o *.so && make build # 增加lffi库 + @$(MAKE) -C src/lffi build + @echo "CC - lpbc" - @cd src/lpbc && rm -rf *.o *.so && make build # 增加lpbc库 + @$(MAKE) -C src/lpbc build + @echo "CC - lpeg" - @cd src/lpeg && rm -rf *.o *.so && make build # 增加lpeg库 + @$(MAKE) -C src/lpeg build + @echo "CC - lcrypt" - @cd src/lcrypt && rm -rf *.o *.so && make build # 增加lcrypt库 - @echo "CC - lcjson" - @cd src/lcjson && rm -rf *.o *.so && make build # 增加cjson库 - @echo "CC - lmsgpack" - @cd src/lmsgpack && rm -rf *.o *.so && make build # 增加lmsgpack库 - @echo "CC - lhttpparser" - @cd src/lhttpparser && rm -rf *.o *.so && make build # 增加PicoHTTPParser库 + @$(MAKE) -C src/lcrypt build + @echo "CC - lcjson" + @$(MAKE) -C src/lcjson build -clean : + @echo "CC - lmsgpack" + @$(MAKE) -C src/lmsgpack build - rm -rf *.so + @echo "CC - lhttpparser" + @$(MAKE) -C src/lhttpparser build diff --git a/src/Makefile b/src/Makefile index 7046057d..7fa94114 100644 --- a/src/Makefile +++ b/src/Makefile @@ -29,20 +29,16 @@ MACRO += -w -O3 -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib build : - @$(CC) -o libcore.so core.c core_ev.c core_memory.c core_sys.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) @echo "CC - libcore" - @$(CC) -o cfadmin core_start.c $(MACRO) $(INCLUDES) $(LIBS) -lcore + @$(CC) -o libcore.so core.c core_ev.c core_memory.c core_sys.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) @echo "CC - cfadmin" + @$(CC) -o cfadmin core_start.c $(MACRO) $(INCLUDES) $(LIBS) -lcore @mv *.so cfadmin ../ rebuild: - @$(CC) -o libcore.so core.c core_ev.c core_memory.c core_sys.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) @echo "CC - libcore" - @$(CC) -o cfadmin core_start.c $(MACRO) $(INCLUDES) $(LIBS) -lcore + @$(CC) -o libcore.so core.c core_ev.c core_memory.c core_sys.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) @echo "CC - cfadmin" + @$(CC) -o cfadmin core_start.c $(MACRO) $(INCLUDES) $(LIBS) -lcore @mv *.so cfadmin ../ - -clean : - - rm -rf *.o *.so From 3af5de997909e04e5c92ba0d2c0f1f8873d05441 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 30 Sep 2019 07:18:53 +0800 Subject: [PATCH 369/956] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E6=97=A0=E7=94=A8?= =?UTF-8?q?=E7=9A=84=E7=BC=96=E8=AF=91=E5=91=BD=E4=BB=A4,=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E5=85=BC=E5=AE=B9=E6=80=A7=E6=9B=B4=E5=A5=BD=E7=9A=84?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E4=B8=8E=E6=9B=B4=E7=BE=8E=E8=A7=82=E6=98=93?= =?UTF-8?q?=E6=8E=92=E9=94=99=E7=9A=84=E7=BC=96=E8=AF=91=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 20 +++++++-------- luaclib/Makefile | 43 +++----------------------------- luaclib/src/lcjson/Makefile | 7 ------ luaclib/src/lcrypt/Makefile | 7 ------ luaclib/src/lffi/makefile | 7 ------ luaclib/src/lfs/Makefile | 7 ------ luaclib/src/lhttpparser/Makefile | 8 ------ luaclib/src/lmsgpack/Makefile | 7 ------ luaclib/src/lpbc/Makefile | 7 ------ luaclib/src/lpeg/makefile | 7 ------ src/Makefile | 9 +------ 11 files changed, 15 insertions(+), 114 deletions(-) diff --git a/Makefile b/Makefile index c059321d..c22ad075 100644 --- a/Makefile +++ b/Makefile @@ -14,19 +14,19 @@ default : build : - ### 编译核心库与可执行文件 ### - @$(MAKE) -C src build - @$(MAKE) -C luaclib build - @$(MAKE) -C 3rd build + @$(MAKE) -s -C src build + @$(MAKE) -s -C luaclib internal + @$(MAKE) -s -C luaclib 3part + @$(MAKE) -s -C 3rd build rebuild : - ### 编译核心库与可执行文件 ### - @$(MAKE) -C src rebuild - @$(MAKE) -C luaclib rebuild - @$(MAKE) -C 3rd rebuild + @$(MAKE) -s -C src build + @$(MAKE) -s -C luaclib internal + @$(MAKE) -s -C luaclib 3part + @$(MAKE) -s -C 3rd build clean : - - ### 清理所有编译文件与库 ### + @echo "********** Clean All Files **********" rm -rf cfadmin libcore.so luaclib/*.so + @$(MAKE) -s -C 3rd clean diff --git a/luaclib/Makefile b/luaclib/Makefile index 3918f09f..62c18e92 100644 --- a/luaclib/Makefile +++ b/luaclib/Makefile @@ -13,9 +13,9 @@ LIBS += -L./ -L../ -L/usr/local/lib # CFLAGS = -Wall -O3 -fPIC --shared -DTCMALLOC -ltcmalloc CFLAGS = -Wall -O3 -fPIC --shared -build : +internal : - ### 以下为内置模块位置 ### + @echo "********** Built-in modules **********" @echo "CC - lsys" @$(CC) -o sys.so src/lsys.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua @echo "CC - ludp" @@ -26,45 +26,10 @@ build : @$(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua @echo "CC - ltcp" @$(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore -llua - ### 以下为内置第三方库编译位置 ### - @echo "CC - lfs" - @$(MAKE) -C src/lfs build - @echo "CC - lffi" - @$(MAKE) -C src/lffi build +3part : - @echo "CC - lpbc" - @$(MAKE) -C src/lpbc build - - @echo "CC - lpeg" - @$(MAKE) -C src/lpeg build - - @echo "CC - lcrypt" - @$(MAKE) -C src/lcrypt build - - @echo "CC - lcjson" - @$(MAKE) -C src/lcjson build - - @echo "CC - lmsgpack" - @$(MAKE) -C src/lmsgpack build - - @echo "CC - lhttpparser" - @$(MAKE) -C src/lhttpparser build - -rebuild : - - ### 以下为内置模块位置 ### - @echo "CC - lsys" - @$(CC) -o sys.so src/lsys.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua - @echo "CC - ludp" - @$(CC) -o udp.so src/ludp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua - @echo "CC - ltask" - @$(CC) -o task.so src/ltask.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua - @echo "CC - ltimer" - @$(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua - @echo "CC - ltcp" - @$(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore -llua - ### 以下为内置第三方库编译位置 ### + @echo "********** Third party modules **********" @echo "CC - lfs" @$(MAKE) -C src/lfs build diff --git a/luaclib/src/lcjson/Makefile b/luaclib/src/lcjson/Makefile index eb2a4219..79ef25d0 100644 --- a/luaclib/src/lcjson/Makefile +++ b/luaclib/src/lcjson/Makefile @@ -19,10 +19,3 @@ DLL = -lcore -llua build: @$(CC) -o cjson.so lua_cjson.c fpconv.c strbuf.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) @mv *.so ../../ - -rebuild: $(OBJS) - @$(CC) -o cjson.so lua_cjson.c fpconv.c strbuf.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) - @mv *.so ../../ - -clean: - rm -rf *.o *.so diff --git a/luaclib/src/lcrypt/Makefile b/luaclib/src/lcrypt/Makefile index e5a1eeb7..7bd5637c 100644 --- a/luaclib/src/lcrypt/Makefile +++ b/luaclib/src/lcrypt/Makefile @@ -18,10 +18,3 @@ LIBS = -L../ -L../../../ -L/usr/local/lib build: @$(CC) -o lcrypt.so lcrypt.c crc.c md5.c url.c sha1.c sha2.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) @mv *.so ../../ - -rebuild: - @$(CC) -o lcrypt.so lcrypt.c crc.c md5.c url.c sha1.c sha2.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) - @mv *.so ../../ - -clean: - rm -rf *.o *.so diff --git a/luaclib/src/lffi/makefile b/luaclib/src/lffi/makefile index 7bc8daf1..e6d6a70b 100644 --- a/luaclib/src/lffi/makefile +++ b/luaclib/src/lffi/makefile @@ -18,10 +18,3 @@ DLL = -lcore -llua build: @$(CC) -o lffi.so ffi.c parser.c ctype.c $(CFLAGS) $(INCLUDE) $(LIB) $(DLL) @mv *.so ../../ - -rebuild: - @$(CC) -o lffi.so ffi.c parser.c ctype.c $(CFLAGS) $(INCLUDE) $(LIB) $(DLL) - @mv *.so ../../ - -clean: - rm -rf *.o *.so diff --git a/luaclib/src/lfs/Makefile b/luaclib/src/lfs/Makefile index f44193da..ae258f13 100644 --- a/luaclib/src/lfs/Makefile +++ b/luaclib/src/lfs/Makefile @@ -18,10 +18,3 @@ DLL = -lcore -llua build: @$(CC) -o lfs.so lfs.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) @mv *.so ../../ - -rebuild: - @$(CC) -o lfs.so lfs.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) - @mv *.so ../../ - -clean: - rm -rf *.o *.so diff --git a/luaclib/src/lhttpparser/Makefile b/luaclib/src/lhttpparser/Makefile index f10fab0a..9bfbd037 100644 --- a/luaclib/src/lhttpparser/Makefile +++ b/luaclib/src/lhttpparser/Makefile @@ -18,14 +18,6 @@ LIBS = -L../ -L../../../ -L/usr/local/lib CFLAGS = -O3 -Wall -shared -fPIC DLL = -lcore -llua - build: @$(CC) -o httpparser.so httpparser.c lhttpparser.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) @mv *.so ../../ - -rebuild: - @$(CC) -o httpparser.so httpparser.c lhttpparser.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) - @mv *.so ../../ - -clean: - rm -rf *.o *.so diff --git a/luaclib/src/lmsgpack/Makefile b/luaclib/src/lmsgpack/Makefile index 028f3f83..84fa7753 100644 --- a/luaclib/src/lmsgpack/Makefile +++ b/luaclib/src/lmsgpack/Makefile @@ -18,10 +18,3 @@ DLL = -lcore -llua build: @$(CC) -o lmsgpack.so lmsgpack.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) @mv *.so ../../ - -rebuild: - @$(CC) -o lmsgpack.so lmsgpack.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) - @mv *.so ../../ - -clean: - rm -rf *.o *.so diff --git a/luaclib/src/lpbc/Makefile b/luaclib/src/lpbc/Makefile index 560a289d..ea2e74f3 100644 --- a/luaclib/src/lpbc/Makefile +++ b/luaclib/src/lpbc/Makefile @@ -18,10 +18,3 @@ DLL = -lcore -llua build: @$(CC) -o lprotobuf.so lpb.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) @mv *.so ../../ - -rebuild: - @$(CC) -o lprotobuf.so lpb.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) - @mv *.so ../../ - -clean: - rm -rf *.o *.so diff --git a/luaclib/src/lpeg/makefile b/luaclib/src/lpeg/makefile index 588b6f4c..391488dc 100644 --- a/luaclib/src/lpeg/makefile +++ b/luaclib/src/lpeg/makefile @@ -16,10 +16,3 @@ DLL = -lcore -llua build: @$(CC) -o lpeg.so lpcap.c lpcode.c lpprint.c lptree.c lpvm.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) @mv *.so ../../ - -rebuild: - @$(CC) -o lpeg.so lpcap.c lpcode.c lpprint.c lptree.c lpvm.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) - @mv *.so ../../ - -clean: - rm -f *.so *.o diff --git a/src/Makefile b/src/Makefile index 7fa94114..782dce4b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -29,14 +29,7 @@ MACRO += -w -O3 -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib build : - @echo "CC - libcore" - @$(CC) -o libcore.so core.c core_ev.c core_memory.c core_sys.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) - @echo "CC - cfadmin" - @$(CC) -o cfadmin core_start.c $(MACRO) $(INCLUDES) $(LIBS) -lcore - @mv *.so cfadmin ../ - -rebuild: - + @echo "********** Core library and executable **********" @echo "CC - libcore" @$(CC) -o libcore.so core.c core_ev.c core_memory.c core_sys.c $(CFLAGS) $(INCLUDES) $(LIBS) $(DLL) @echo "CC - cfadmin" From 4a65a03353187a1cecec144ca83ec83a89328700 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 1 Oct 2019 09:02:45 +0800 Subject: [PATCH 370/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=E5=AE=89=E8=A3=85=E4=B8=8E=E7=BC=96=E8=AF=91=E6=A1=86=E6=9E=B6?= =?UTF-8?q?=E7=9A=84=E5=8A=A8=E6=80=81=E6=BC=94=E7=A4=BA=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 8ad43efa..aded50bd 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,12 @@ * [在线文档](https://candymi.github.io/LuaWeb)(Sorry! Only Chinese.) +### 安装与运行(Build And Run) + +

                  + +

                  + ### 预览图 (preview)

                  From f5cae6367c838e5438e3318c9fc7c00b4fcbf987 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 2 Oct 2019 19:54:33 +0800 Subject: [PATCH 371/956] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 34 +++++++++++++++++----------------- luaclib/src/ludp.c | 20 ++++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index dee004d2..66287c56 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -21,29 +21,29 @@ void SETSOCKETOPT(int sockfd, int mode){ /* 地址重用 */ #ifdef SO_REUSEADDR ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &Enable, sizeof(Enable)); - if (ret) { - LOG("ERROR", "设置 SO_REUSEADDR 失败."); - return exit(-1); - } + if (ret) { + LOG("ERROR", "设置 SO_REUSEADDR 失败."); + return _exit(-1); + } #endif /* 端口重用 */ #ifdef SO_REUSEPORT if (mode == SERVER) { - ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &Enable, sizeof(Enable)); - if (ret) { - LOG("ERROR", "设置 SO_REUSEPORT 失败."); - return exit(-1); - } + ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &Enable, sizeof(Enable)); + if (ret) { + LOG("ERROR", "设置 SO_REUSEPORT 失败."); + return _exit(-1); + } } #endif /* 关闭小包延迟合并算法 */ #ifdef TCP_NODELAY - ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &Enable, sizeof(Enable)); + ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &Enable, sizeof(Enable)); if (ret){ LOG("ERROR", "TCP_NODELAY 设置失败."); - return exit(-1); + return _exit(-1); } #endif @@ -52,7 +52,7 @@ void SETSOCKETOPT(int sockfd, int mode){ ret = setsockopt(sockfd, IPPROTO_TCP, SO_KEEPALIVE, &Enable , sizeof(Enable)); if (ret){ LOG("ERROR", "SO_KEEPALIVE 设置失败."); - return exit(-1); + return _exit(-1); } #endif @@ -62,7 +62,7 @@ void SETSOCKETOPT(int sockfd, int mode){ ret = setsockopt(sockfd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &Enable, sizeof(Enable)); if (ret){ LOG("ERROR", "TCP_DEFER_ACCEPT 设置失败."); - return exit(-1); + return _exit(-1); } } #endif @@ -73,7 +73,7 @@ void SETSOCKETOPT(int sockfd, int mode){ ret = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle , sizeof(keepidle)); if (ret){ LOG("ERROR", "TCP_KEEPIDLE 设置失败."); - return exit(-1); + return _exit(-1); } #endif @@ -83,7 +83,7 @@ void SETSOCKETOPT(int sockfd, int mode){ ret = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, &keepcount , sizeof(keepcount)); if (ret){ LOG("ERROR", "TCP_KEEPCNT 设置失败."); - return exit(-1); + return _exit(-1); } #endif @@ -93,7 +93,7 @@ void SETSOCKETOPT(int sockfd, int mode){ ret = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, &keepinterval , sizeof(keepinterval)); if (ret){ LOG("ERROR", "TCP_KEEPINTVL 设置失败."); - return exit(-1); + return _exit(-1); } #endif @@ -103,7 +103,7 @@ void SETSOCKETOPT(int sockfd, int mode){ ret = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&No, sizeof(No)); if (ret){ LOG("ERROR", "IPV6_V6ONLY 关闭失败."); - return exit(-1); + return _exit(-1); } #endif diff --git a/luaclib/src/ludp.c b/luaclib/src/ludp.c index 7740338f..35c26fb0 100644 --- a/luaclib/src/ludp.c +++ b/luaclib/src/ludp.c @@ -14,19 +14,19 @@ void SETSOCKETOPT(int sockfd) { /* 地址重用 */ #ifdef SO_REUSEADDR ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &Enable, sizeof(Enable)); - if (ret) { - LOG("ERROR", "设置 SO_REUSEADDR 失败."); - return exit(-1); - } + if (ret) { + LOG("ERROR", "设置 SO_REUSEADDR 失败."); + return _exit(-1); + } #endif /* 端口重用 */ #ifdef SO_REUSEPORT - ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &Enable, sizeof(Enable)); - if (ret) { - LOG("ERROR", "设置 SO_REUSEPORT 失败."); - return exit(-1); - } + ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &Enable, sizeof(Enable)); + if (ret) { + LOG("ERROR", "设置 SO_REUSEPORT 失败."); + return _exit(-1); + } #endif /* 开启IPV6与ipv4双栈 */ @@ -35,7 +35,7 @@ void SETSOCKETOPT(int sockfd) { ret = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&No, sizeof(No)); if (ret){ LOG("ERROR", "IPV6_V6ONLY 关闭失败."); - return exit(-1); + return _exit(-1); } #endif From 3d9a8cdc0176d05ff2e74f693bc0b59ef4b5e8ce Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 10 Oct 2019 14:43:36 +0800 Subject: [PATCH 372/956] =?UTF-8?q?=E8=B0=83=E6=95=B4socket=20client?= =?UTF-8?q?=E5=BB=BA=E7=AB=8B=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 53 +++++++++++++++++++++++++--------------------- src/core_sys.h | 1 + 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 66287c56..eaa995a2 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -142,31 +142,36 @@ create_server_fd(int port, int backlog){ static int create_client_fd(const char *ipaddr, int port){ errno = 0; - /* 建立 TCP Client Socket */ - int sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); - if (0 >= sockfd) return -1; - - /* socket option set */ - SETSOCKETOPT(sockfd, CLIENT); - - struct sockaddr_in6 SA; - memset(&SA, 0x0, sizeof(SA)); - - SA.sin6_family = AF_INET6; - SA.sin6_port = htons(port); - int error = inet_pton(AF_INET6, ipaddr, &SA.sin6_addr); - if (1 != error) { - LOG("ERROR", strerror(errno)); - close(sockfd); - return -1; - } - - int ret = connect(sockfd, (struct sockaddr*)&SA, sizeof(SA)); - if (ret < 0 && errno != EINPROGRESS){ - close(sockfd); - return -1; + struct addrinfo hints; + struct addrinfo *hints_ptr = NULL; + struct addrinfo *hints_list = NULL; + + memset(&hints, 0x0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + char Port[16]; + sprintf(Port, "%d", port); + + int status = getaddrinfo(ipaddr, Port, &hints, &hints_list); + if (status) + return -1; + int sockfd = -1; + for (hints_ptr = hints_list; hints_ptr; hints_ptr = hints_ptr->ai_next) { + sockfd = socket(hints_ptr->ai_family, hints_ptr->ai_socktype, hints_ptr->ai_protocol); + if (sockfd < 0) + continue; + SETSOCKETOPT(sockfd, CLIENT); + int ok = connect(sockfd, hints_ptr->ai_addr, hints_ptr->ai_addrlen); + if (ok < 0 && errno != EINPROGRESS){ + sockfd = -1; + close(sockfd); + continue; + } + break; } - return sockfd; + return sockfd; } diff --git a/src/core_sys.h b/src/core_sys.h index 83db194f..70030c64 100644 --- a/src/core_sys.h +++ b/src/core_sys.h @@ -14,6 +14,7 @@ #include #include +#include #include #include #include From 3cb4c9c27c89624dd5e8d320ca1e932996facdb3 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 10 Oct 2019 14:44:26 +0800 Subject: [PATCH 373/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E6=8B=BC=E5=86=99=E9=94=99=E8=AF=AF=E7=9A=84=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/TCP.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index 180c6be6..ba85a779 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -310,7 +310,7 @@ function TCP:connect(domain, port) if connected then return co_wakeup(co, true) end - return co_wakeup(co, false, '连接失败') + return co_wakeup(co, false, 'connect failed') end) self.timer = ti_timeout(self._timeout, function ( ... ) tcp_push(self.CONNECT_IO) @@ -319,7 +319,7 @@ function TCP:connect(domain, port) self.CONNECT_IO = nil self.connect_co = nil self.connect_current_co = nil - return co_wakeup(co, nil, 'connect timeot.') + return co_wakeup(co, nil, 'connect timeout.') end) tcp_connect(self.CONNECT_IO, self.fd, self.connect_co) return co_wait() @@ -368,7 +368,7 @@ function TCP:ssl_connect(domain, port) self.CONNECT_IO = nil self.connect_co = nil self.connect_current_co = nil - return co_wakeup(co, nil, 'ssl_connect timeot.') + return co_wakeup(co, nil, 'ssl_connect timeout.') end) tcp_start(self.CONNECT_IO, self.fd, EVENT_WRITE, self.connect_co) return co_wait() From 83fac462624a73f99293c239bfd09018007c972c Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 10 Oct 2019 15:06:37 +0800 Subject: [PATCH 374/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E5=A4=84?= =?UTF-8?q?=E6=9C=AA=E9=87=8A=E6=94=BE=E7=9A=84=E9=97=AE=E9=A2=98.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index eaa995a2..db23595c 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -171,6 +171,7 @@ create_client_fd(const char *ipaddr, int port){ } break; } + freeaddrinfo(hints_list); return sockfd; } From 9a29b216466cdad48ec85ae80042e9db739dbad3 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 10 Oct 2019 15:07:11 +0800 Subject: [PATCH 375/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E5=A4=84?= =?UTF-8?q?=E6=9C=AA=E9=87=8A=E6=94=BE=E7=9A=84=E9=97=AE=E9=A2=98.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index db23595c..46d0851c 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -171,7 +171,8 @@ create_client_fd(const char *ipaddr, int port){ } break; } - freeaddrinfo(hints_list); + if (hints_list) + freeaddrinfo(hints_list); return sockfd; } From 294165d9d346225055eab7cbef833e15583fd473 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 10 Oct 2019 16:24:12 +0800 Subject: [PATCH 376/956] =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=81=A5=E5=A3=AE=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 126 +++++++++++++++++++++++---------------------- 1 file changed, 64 insertions(+), 62 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 46d0851c..dfb55a26 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -113,67 +113,68 @@ void SETSOCKETOPT(int sockfd, int mode){ static int create_server_fd(int port, int backlog){ errno = 0; - /* 建立 TCP Server Socket */ - int sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); - if (0 >= sockfd) return -1; + /* 建立 TCP Server Socket */ + int sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); + if (0 >= sockfd) + return -1; - /* socket option set */ - SETSOCKETOPT(sockfd, SERVER); + /* socket option set */ + SETSOCKETOPT(sockfd, SERVER); - struct sockaddr_in6 SA; + struct sockaddr_in6 SA; memset(&SA, 0x0, sizeof(SA)); - SA.sin6_family = AF_INET6; - SA.sin6_port = htons(port); - SA.sin6_addr = in6addr_any; + SA.sin6_family = AF_INET6; + SA.sin6_port = htons(port); + SA.sin6_addr = in6addr_any; /* 绑定套接字失败 */ - int bind_success = bind(sockfd, (struct sockaddr *)&SA, sizeof(SA)); - if (0 > bind_success) return -1; + int bind_success = bind(sockfd, (struct sockaddr *)&SA, sizeof(SA)); + if (0 > bind_success) { + close(sockfd); + return -1; + } /* 监听套接字失败 */ - int listen_success = listen(sockfd, backlog); - if (0 > listen_success) return -1; + int listen_success = listen(sockfd, backlog); + if (0 > listen_success) { + close(sockfd); + return -1; + } - return sockfd; + return sockfd; } /* client fd */ static int create_client_fd(const char *ipaddr, int port){ errno = 0; - struct addrinfo hints; - struct addrinfo *hints_ptr = NULL; - struct addrinfo *hints_list = NULL; + /* 建立 TCP Client Socket */ + int sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); + if (0 >= sockfd) return -1; - memset(&hints, 0x0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; + /* socket option set */ + SETSOCKETOPT(sockfd, CLIENT); - char Port[16]; - sprintf(Port, "%d", port); + struct sockaddr_in6 SA; + memset(&SA, 0x0, sizeof(SA)); - int status = getaddrinfo(ipaddr, Port, &hints, &hints_list); - if (status) + SA.sin6_family = AF_INET6; + SA.sin6_port = htons(port); + int error = inet_pton(AF_INET6, ipaddr, &SA.sin6_addr); + if (1 != error) { + LOG("ERROR", strerror(errno)); + close(sockfd); + return -1; + } + + int ret = connect(sockfd, (struct sockaddr*)&SA, sizeof(SA)); + if (ret < 0 && errno != EINPROGRESS){ + LOG("ERROR", strerror(errno)); + close(sockfd); return -1; - int sockfd = -1; - for (hints_ptr = hints_list; hints_ptr; hints_ptr = hints_ptr->ai_next) { - sockfd = socket(hints_ptr->ai_family, hints_ptr->ai_socktype, hints_ptr->ai_protocol); - if (sockfd < 0) - continue; - SETSOCKETOPT(sockfd, CLIENT); - int ok = connect(sockfd, hints_ptr->ai_addr, hints_ptr->ai_addrlen); - if (ok < 0 && errno != EINPROGRESS){ - sockfd = -1; - close(sockfd); - continue; - } - break; } - if (hints_list) - freeaddrinfo(hints_list); - return sockfd; + return sockfd; } @@ -223,29 +224,30 @@ IO_CONNECT(CORE_P_ core_io *io, int revents){ static void /* 接受链接 */ IO_ACCEPT(CORE_P_ core_io *io, int revents){ - if (revents & EV_READ){ - for(;;) { - errno = 0; - struct sockaddr_in6 SA; - socklen_t slen = sizeof(SA); + if (revents & EV_READ){ + for(;;) { + errno = 0; + struct sockaddr_in6 SA; + socklen_t slen = sizeof(SA); memset(&SA, 0x0, slen); - int client = accept(io->fd, (struct sockaddr*)&SA, &slen); - if (0 >= client) { - if (errno != EWOULDBLOCK) - LOG("INFO", strerror(errno)); - return ; - } + int client = accept(io->fd, (struct sockaddr*)&SA, &slen); + if (0 >= client) { + if (errno != EWOULDBLOCK) + LOG("INFO", strerror(errno)); + return ; + } non_blocking(client); //在某些平台下, 这个socket是阻塞的. - lua_State *co = (lua_State *) core_get_watcher_userdata(io); - if (lua_status(co) == LUA_YIELD || lua_status(co) == LUA_OK){ - char buf[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &SA.sin6_addr, buf, INET6_ADDRSTRLEN); - lua_pushinteger(co, client); - lua_pushlstring(co, buf, strlen(buf)); - int status = CO_RESUME(co, NULL, lua_status(co) == LUA_YIELD ? lua_gettop(co) : lua_gettop(co) - 1); - if (status != LUA_YIELD && status != LUA_OK) { - LOG("ERROR", lua_tostring(co, -1)); - LOG("ERROR", "Error Lua Accept Method"); + lua_State *co = (lua_State *) core_get_watcher_userdata(io); + if (lua_status(co) == LUA_YIELD || lua_status(co) == LUA_OK){ + char buf[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &SA.sin6_addr, buf, INET6_ADDRSTRLEN); + lua_pushinteger(co, client); + lua_pushlstring(co, buf, strlen(buf)); + int status = CO_RESUME(co, NULL, lua_status(co) == LUA_YIELD ? lua_gettop(co) : lua_gettop(co) - 1); + if (status != LUA_YIELD && status != LUA_OK) { + LOG("ERROR", lua_tostring(co, -1)); + LOG("ERROR", "Error Lua Accept Method"); + core_io_stop(CORE_LOOP_ io); } } } From b94eac08d6090e4c74a233252d197f4ab6dd5ae7 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 11 Oct 2019 16:07:35 +0800 Subject: [PATCH 377/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=9C=AC=E5=9C=B0?= =?UTF-8?q?=E8=B5=84=E6=BA=90=E4=B8=8A=E4=BC=A0=E7=A4=BA=E4=BE=8Btest=5Fup?= =?UTF-8?q?load.lua?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_upload.lua | 45 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 script/test_upload.lua diff --git a/script/test_upload.lua b/script/test_upload.lua new file mode 100644 index 00000000..440d5253 --- /dev/null +++ b/script/test_upload.lua @@ -0,0 +1,45 @@ +local httpd = require "httpd" +local json = require "json" + +local app = httpd:new("httpd") + +--[[ + + + + 本地文件上传 + + +
                  + +
                  + +
                  + +
                  + + +]] + +app:api("/upload_local", function (content) + if content.files then + for _, item in ipairs(content.files) do + local f, err = io.open("static/"..item.filename, "w+") + if f then + f:write(item.file) + f:flush() + f:close() + else + print("ERROR: " .. err) + end + end + return json.encode { code = 200, status = "OK" } + end + return json.encode { code = 404, status = "没有上传内容." } +end) + +app:static("static", 30) + +app:listen("0.0.0.0", 8080) + +app:run() From df1308aad2e9c7bc11bf3912894d98a8ca5c3401 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 16 Oct 2019 05:11:28 +0800 Subject: [PATCH 378/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=86=85=E7=BD=AEdns?= =?UTF-8?q?=E5=BA=93=E6=9F=A5=E8=AF=A2=E6=95=88=E7=8E=87.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/dns.lua | 316 +++++++++++++++++++--------------------- 1 file changed, 152 insertions(+), 164 deletions(-) diff --git a/lualib/protocol/dns.lua b/lualib/protocol/dns.lua index ed14b46d..e96801f8 100644 --- a/lualib/protocol/dns.lua +++ b/lualib/protocol/dns.lua @@ -21,6 +21,7 @@ local concat = table.concat local insert = table.insert local now = os.time +local random = math.random local fmt = string.format local match = string.match local splite = string.gmatch @@ -35,8 +36,8 @@ local prefix = '::ffff:' local dns = {} local QTYPE = { - A = 1, - AAAA = 28, + A = 1, + AAAA = 28, } local dns_cache = {} @@ -51,28 +52,30 @@ local function gen_id() end local function check_cache(domain) - local query = dns_cache[domain] - if not query then - return - end - if query then - if not query.ttl or query.ttl > now() then - return query.ip - end - dns_cache[domain] = nil - end + local query = dns_cache[domain] + if not query then return + end + local ttl = query.ttl + if not ttl then + return query.ip + end + if ttl > now() then + return query.ip + end + dns_cache[domain] = nil + return end local function check_ip(ip) - if ip then - if check_ipv4(ip) then - return true, 4 - end - if check_ipv6(ip) then - return true, 6 - end + if type(ip) == 'string' and ip ~= '' then + if check_ipv4(ip) then + return true, 4 + end + if check_ipv6(ip) then + return true, 6 end + end end local function gen_cache() @@ -80,18 +83,13 @@ local function gen_cache() if file then for line in file:lines() do local ip, domain = match(line, '([^#%G]*)[%G]+([^%G]+)') - -- print(line, 'ip=['..(ip or '')..']', 'domain=['..(domain or '')..']') local ok, v = check_ip(ip) if ok then - if v == 4 then - if not dns_cache[domain] then - dns_cache[domain] = {ip = prefix..ip} - end - end - if v == 6 then - if not dns_cache[domain] then - dns_cache[domain] = {ip = ip} + if not dns_cache[domain] then + if v == 4 then + ip = prefix..ip end + dns_cache[domain] = {ip = ip} end end end @@ -101,38 +99,38 @@ local function gen_cache() end if #dns_list < 1 then - local file = io.open("/etc/resolv.conf", "r") - if file then - for line in file:lines() do - local ip = match(line, "nameserver (.-)$") - local ok = check_ip(ip) - if ok then - insert(dns_list, ip) - end + local file = io.open("/etc/resolv.conf", "r") + if file then + for line in file:lines() do + local ip = match(line, "nameserver (.-)$") + local ok, v = check_ip(ip) + if ok and v then + insert(dns_list, ip) end - file:close() - end - if #dns_list < 1 then - dns_list = {"114.114.114.114", "8.8.8.8"} end - gen_cache() + file:close() + end + if #dns_list < 1 then + dns_list = {"114.114.114.114", "8.8.8.8"} + end + gen_cache() end local function get_dns_client() - if #dns_list >= 1 then - local ip = dns_list[1] - local udp = UDP:new():timeout(dns._timeout or 30) - local ok, v = check_ip(ip) - if v == 4 then - ip = prefix..ip - end - local ok = udp:connect(ip, 53) - if not ok then - return nil, 'Create UDP Socket error.' - end - return udp, v + if #dns_list >= 1 then + local ip = dns_list[random(1, #dns_list)] + local udp = UDP:new():timeout(dns._timeout or 30) + local ok, v = check_ip(ip) + if v == 4 then + ip = prefix .. ip end - return nil, "Can't find system dns in /etc/resolve.conf." + local ok = udp:connect(ip, 53) + if not ok then + return nil, 'Create UDP Socket error.' + end + return udp, v + end + return nil, "Can't find system dns in /etc/resolve.conf." end local function pack_header() @@ -148,13 +146,13 @@ local function pack_question(name, version) -- local Query_Type = QTYPE.AAAA -- IPv6 local qtype = QTYPE.A if version == 6 then - qtype = QTYPE.AAAA + qtype = QTYPE.AAAA end local Query_Type = qtype local Query_Class = 0x01 -- IN internet local question = {} for sp in splite(name, "([^%.]*)") do - insert(question, pack(">s1", sp)) + insert(question, pack(">s1", sp)) end insert(question, "\0") insert(question, pack(">HH", Query_Type, Query_Class)) @@ -162,8 +160,8 @@ local function pack_question(name, version) end local function unpack_header(chunk) - local tid, flags, qdcount, ancount, nscount, arcount, nbyte = unpack(">HHHHHH", chunk) - return { tid = tid, flags = flags, qdcount = qdcount, ancount = ancount}, nbyte + local tid, flags, qdcount, ancount, nscount, arcount, nbyte = unpack(">HHHHHH", chunk) + return { tid = tid, flags = flags, qdcount = qdcount, ancount = ancount}, nbyte end local function unpack_name(chunk, nbyte) @@ -202,14 +200,14 @@ local function unpack_answer(chunk, nbyte) end local function unpack_rdata(chunk, qtype) - if qtype == QTYPE.AAAA then - return fmt('%x:%x:%x:%x:%x:%x:%x:%x', unpack(">HHHHHHHH", chunk)) - end - return fmt("%d.%d.%d.%d", unpack(">BBBB", chunk)) + if qtype == QTYPE.AAAA then + return fmt('%x:%x:%x:%x:%x:%x:%x:%x', unpack(">HHHHHHHH", chunk)) + end + return fmt("%d.%d.%d.%d", unpack(">BBBB", chunk)) end - local cos = {} + -- 如果有其它协程也在等待查询, 那么一起唤醒它们 local function check_wait(domain, wlist, ...) for _, w in ipairs(wlist) do @@ -218,126 +216,116 @@ local function check_wait(domain, wlist, ...) cos[domain] = nil end - - local function dns_query(domain) - local wlist = {} - cos[domain] = wlist - -- local start = os.time() + os.clock() - -- print("开始解析域名:["..domain.."], 开始时间: ", start) - local dns_client, msg = get_dns_client() - if not dns_client then - check_wait(domain, wlist, nil, msg) - return nil, msg - end - local dns_resp, len, readable - cf_fork(function () - local req = pack_header()..pack_question(domain, msg) - local times = 1 - while 1 do - dns_client:send(req) - cf_sleep(1) - if readable then - return - end - Log:WARN("第"..times.."次尝试解析["..domain.."]:") - times = times + 1 + local wlist = {} + cos[domain] = wlist + local dns_client, msg = get_dns_client() + if not dns_client then + check_wait(domain, wlist, nil, msg) + return nil, msg + end + local dns_resp, len, readable + cf_fork(function () + local req = pack_header()..pack_question(domain, msg) + local times = 1 + while 1 do + dns_client:send(req) + cf_sleep(1) + if readable then + return end - end) - dns_resp, len = dns_client:recv() - dns_client:close() - readable = true - if not dns_resp or not len or len < LIMIT_HEADER_LEN then - local err = "1. dns解析失败." - check_wait(domain, wlist, nil, err) - return nil, err - end - local answer_header, nbyte = unpack_header(dns_resp) - if answer_header.qdcount ~= 1 then - local err = "2. 错误的DNS回应." - check_wait(domain, wlist, nil, err) - return nil, err + Log:WARN("第"..times.."次尝试解析["..domain.."]:") + times = times + 1 end - if not answer_header.ancount or answer_header.ancount < 1 then - local err = "3. 无法解析的域名." - check_wait(domain, wlist, nil, err) - return nil, err - end - local question, nbyte = unpack_question(dns_resp, nbyte) - - if question.name ~= domain then - local err = "4. 回应域名与请求解析的域名不一致." - check_wait(domain, wlist, nil, err) - return nil, err - end - local answer - local t = now() - for i = 1, answer_header.ancount do - answer, nbyte = unpack_answer(dns_resp, nbyte) - if answer.atype == QTYPE.A or answer.atype == QTYPE.AAAA then - answer.ip = unpack_rdata(answer.rdata, answer.atype) - local cache = dns_cache[domain] - if not cache then + end) + dns_resp, len = dns_client:recv() + dns_client:close() + readable = true + if not dns_resp or not len or len < LIMIT_HEADER_LEN then + check_wait(domain, wlist, nil, "1. Malformed message length.") + return nil, err + end + local answer_header, nbyte = unpack_header(dns_resp) + if answer_header.qdcount ~= 1 then + check_wait(domain, wlist, nil, "2. Malformed message response.") + return nil, err + end + if not answer_header.ancount or answer_header.ancount < 1 then + check_wait(domain, wlist, nil, "3. Unresolved domain name.") + return nil, err + end + local question, nbyte = unpack_question(dns_resp, nbyte) + if question.name ~= domain then + check_wait(domain, wlist, nil, "4. Inconsistent query domain.") + return nil, err + end + local answer + local t = now() + for i = 1, answer_header.ancount do + answer, nbyte = unpack_answer(dns_resp, nbyte) + if answer.atype == QTYPE.A or answer.atype == QTYPE.AAAA then + answer.ip = unpack_rdata(answer.rdata, answer.atype) + local cache = dns_cache[domain] + if not cache then + dns_cache[domain] = {ip = answer.ip, ttl = t + answer.ttl} + else + if cache.ttl and cache.ttl < t + answer.ttl then dns_cache[domain] = {ip = answer.ip, ttl = t + answer.ttl} - else - if cache.ttl and cache.ttl < t + answer.ttl then - dns_cache[domain] = {ip = answer.ip, ttl = t + answer.ttl} - end end end end - local ok, v = check_ip(answer.ip) - if not ok then - local err = "5. 未知的IP地址." - check_wait(domain, wlist, nil, err..domain) - return nil, err..domain - end - if ok and v == 4 then - answer.ip = prefix..answer.ip - end - check_wait(domain, wlist, true, answer.ip) - return true, answer.ip + end + local ok, v = check_ip(answer.ip) + if not ok then + check_wait(domain, wlist, nil, "5. Unknown IP address." .. domain) + return nil, err..domain + end + if ok and v == 4 then + answer.ip = prefix..answer.ip + end + check_wait(domain, wlist, true, answer.ip) + return true, answer.ip end function dns.flush() - dns_cache = {} - gen_cache() + dns_cache = {} + gen_cache() end -- 这里设置每个dns查询请求的timeout与retry时间 function dns.timeout(timeout) - dns._timeout = timeout + dns._timeout = timeout end function dns.resolve(domain) - -- 如果传进来一个空值, 则直接返回false - if not domain or domain == '' then - return nil, "attemp to resolve a nil or empty host name." - end - -- 如果是正确的ipv4地址直接返回 - local ok, v = check_ip(domain) - if ok then - if 6 == v then - return ok, domain - end - return ok, prefix..domain - end - -- 如果有dns缓存直接返回 - local ip = check_cache(domain) - if ip then - if check_ipv6(ip) then - return true, ip - end - return true, prefix..ip + -- 检查参数是否有效 + if type(domain) ~= 'string' or domain == '' then + return nil, "attempt to pass an invalid domain." + end + -- 如果是正确的ipv4地址直接返回 + local ok, v = check_ip(domain) + if ok then + if 6 == v then + return ok, domain end - -- 如果有其他协程也正巧在查询这个域名, 那么就加入到等待列表内 - local wlist = cos[domain] - if wlist then - local co = cf_self() - insert(wlist, co) - return cf_wait() + return ok, prefix..domain + end + -- 如果有dns缓存直接返回 + local ip = check_cache(domain) + if ip then + if check_ipv6(ip) then + return true, ip end - return dns_query(domain) + return true, prefix..ip + end + -- 如果有其他协程也正巧在查询这个域名, 那么就加入到等待列表内 + local wlist = cos[domain] + if wlist then + local co = cf_self() + insert(wlist, co) + return cf_wait() + end + return dns_query(domain) end -- require "utils" -- var_dump(dns_cache) From 3467308f89b9753690a3b8863c60be7b827fbe69 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 16 Oct 2019 05:18:40 +0800 Subject: [PATCH 379/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9dns=E5=BA=93=E7=9A=84?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E4=BB=A3=E7=A0=81,=20=E5=B9=B6=E7=AE=80?= =?UTF-8?q?=E5=8C=96=E6=B5=8B=E8=AF=95=E9=80=BB=E8=BE=91.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_dns.lua | 168 +++++++++++--------------------------------- 1 file changed, 40 insertions(+), 128 deletions(-) diff --git a/script/test_dns.lua b/script/test_dns.lua index f892adae..07cc6938 100644 --- a/script/test_dns.lua +++ b/script/test_dns.lua @@ -1,137 +1,49 @@ - +local LOG = require "logging" local dns = require "protocol.dns" local dns_resolve = dns.resolve local cf = require "cf" +local fork = cf.fork local system = require "system" local now = system.now local fmt = string.format - --- 百度 -for i = 1, 10 do - cf.fork(function ( ... ) - local t1 = now() - local domain = 'www.baidu.com' - if i == 1 then - print("开始解析 :", domain, "时间:", fmt("%0.8f/Sec", t1)) - end - local ok, ip = dns_resolve(domain) - local t2 = now() - if i == 10 then - print("结束解析 :", domain, "时间:", fmt("%0.8f/Sec", t2), "耗时:", fmt("%0.8f/Sec", t2 - t1)) - end - end) -end - --- 腾讯 -for i = 1, 10 do - cf.fork(function ( ... ) - local t1 = now() - local domain = 'www.qq.com' - if i == 1 then - print("开始解析 :", domain, "时间:", fmt("%0.8f/Sec", t1)) - end - local ok, ip = dns_resolve(domain) - local t2 = now() - if i == 10 then - print("结束解析 :", domain, "时间:", fmt("%0.8f/Sec", t2), "耗时:", fmt("%0.8f/Sec", t2 - t1)) - end - end) -end - --- 淘宝 -for i = 1, 10 do - cf.fork(function ( ... ) - local t1 = now() - local domain = 'www.taobao.com' - if i == 1 then - print("开始解析 :", domain, "时间:", fmt("%0.8f/Sec", t1)) - end - local ok, ip = dns_resolve(domain) - local t2 = now() - if i == 10 then - print("结束解析 :", domain, "时间:", fmt("%0.8f/Sec", t2), "耗时:", fmt("%0.8f/Sec", t2 - t1)) - end - end) -end - --- 谷歌 -for i = 1, 10 do - cf.fork(function ( ... ) - local t1 = now() - local domain = 'www.google.com' - if i == 1 then - print("开始解析 :", domain, "时间:", fmt("%0.8f/Sec", t1)) - end - local ok, ip = dns_resolve(domain) - local t2 = now() - if i == 10 then - print("结束解析 :", domain, "时间:", fmt("%0.8f/Sec", t2), "耗时:", fmt("%0.8f/Sec", t2 - t1)) - end - end) -end - --- 网易 -for i = 1, 10 do - cf.fork(function ( ... ) - local t1 = now() - local domain = 'www.163.com' - if i == 1 then - print("开始解析 :", domain, "时间:", fmt("%0.8f/Sec", t1)) - end - local ok, ip = dns_resolve(domain) - local t2 = now() - if i == 10 then - print("结束解析 :", domain, "时间:", fmt("%0.8f/Sec", t2), "耗时:", fmt("%0.8f/Sec", t2 - t1)) - end - end) -end - --- 京东 -for i = 1, 10 do - cf.fork(function ( ... ) - local t1 = now() - local domain = 'www.jd.com' - if i == 1 then - print("开始解析 :", domain, "时间:", fmt("%0.8f/Sec", t1)) - end - local ok, ip = dns_resolve(domain) - local t2 = now() - if i == 10 then - print("结束解析 :", domain, "时间:", fmt("%0.8f/Sec", t2), "耗时:", fmt("%0.8f/Sec", t2 - t1)) - end - end) -end - --- 唯品会 -for i = 1, 10 do - cf.fork(function ( ... ) - local t1 = now() - local domain = 'www.vip.com' - if i == 1 then - print("开始解析 :", domain, "时间:", fmt("%0.8f/Sec", t1)) - end - local ok, ip = dns_resolve(domain) - local t2 = now() - if i == 10 then - print("结束解析 :", domain, "时间:", fmt("%0.8f/Sec", t2), "耗时:", fmt("%0.8f/Sec", t2 - t1)) - end - end) -end - --- 盛大 -for i = 1, 10 do - cf.fork(function ( ... ) - local t1 = now() - local domain = 'www.sdo.com' - if i == 1 then - print("开始解析 :", domain, "时间:", fmt("%0.8f/Sec", t1)) - end - local ok, ip = dns_resolve(domain) - local t2 = now() - if i == 10 then - print("结束解析 :", domain, "时间:", fmt("%0.8f/Sec", t2), "耗时:", fmt("%0.8f/Sec", t2 - t1)) - end - end) +local find = string.find +local match = string.match + +local pairs = pairs + +-- 去除ipv4->ipv6映射前缀 +local function delete_ipv4_prefix (ip) + if system.is_ipv4(ip) then + return ip + end + return find(ip, '::ffff') and match(ip, "::[fF]+:([%d%.]+)") or ip +end + +local domains = { + ["百度"] = "www.baidu.com", + ["腾讯"] = "www.qq.com", + ["淘宝"] = "www.taobao.com", + ["谷歌"] = "www.google.com", + ["网易"] = "www.163.com", + ["京东"] = "www.jd.com", + ["唯品会"] = "www.vip.com", + ["盛大"] = "www.sdo.com", +} + +-- 查询次数 +local min, max = 1, 10000 + +for company, domain in pairs(domains) do + local t1 = now() + for i = min, max do + fork(function (...) + local ok, ip = dns_resolve(domain) + if i == max then + local t2 = now() + LOG:DEBUG("结束解析 : ".. company .. "[".. domain .."]" , "[".. delete_ipv4_prefix(ip) .."]", "总耗时: " .. fmt("%0.8f/Sec", t2 - t1)) + end + end) + end end From 3c908311ad68bd97b39d4009f0d9e6473422a3a9 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 17 Oct 2019 00:25:37 +0800 Subject: [PATCH 380/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0test=5Fcsv=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_csv.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/script/test_csv.lua b/script/test_csv.lua index a7f16e14..ab52cc26 100644 --- a/script/test_csv.lua +++ b/script/test_csv.lua @@ -3,3 +3,6 @@ local csv = require "csv" local Log = require("logging"):new() Log:DEBUG(csv.loadfile("./Excel.csv")) + + +local csv.dump("./Excel-1.csv", csv.loadfile("./Excel.csv")) From 64d12c227829f934c8c6a4609966023008cc1b05 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 17 Oct 2019 00:26:57 +0800 Subject: [PATCH 381/956] =?UTF-8?q?csv=E5=BA=93=E5=A2=9E=E5=8A=A0writefile?= =?UTF-8?q?=E6=96=B9=E6=B3=95,=20=E7=94=A8=E4=BA=8Edump=E8=A1=A8=E7=BB=93?= =?UTF-8?q?=E6=9E=84=E6=95=B0=E6=8D=AE=E5=88=B0csv=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/csv.lua | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/lualib/csv.lua b/lualib/csv.lua index 2f78ab8f..c947ba13 100644 --- a/lualib/csv.lua +++ b/lualib/csv.lua @@ -1,3 +1,6 @@ +local concat = table.concat +local type = type + local csv = {} -- 读取并且解析CSV文件, 格式如下: @@ -13,6 +16,9 @@ local csv = {} -- 规则2: 每行不允许出现空格与逗号引号等特殊字符, 空格字符将会被删除掉; -- 规则3: 打开csv文件失败将返回nil与一个err错误信息. function csv.loadfile (path) + if type(path) ~= 'string' or path == '' then + return nil, "invalid args." + end local file, err = io.open(path, "r") if not file then return nil, err @@ -33,4 +39,23 @@ function csv.loadfile (path) return tab end +-- 规则同上 +function csv.writefile (path, t) + if type(path) ~= 'string' or path == '' or type(t) ~= 'table' or #t < 1 then + return nil, "invalid args." + end + local file, err = io.open(path, "w") + if not file then + return nil, err + end + local headers = t[1] + file:write(concat(headers, ",") .. '\n') + for index = 2, #t do + file:write(concat(t[index], ',') .. '\n') + end + file:flush() + file:close() + return true +end + return csv From 8554df2c888692ed0bb7a909d05b90e1278dd558 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 17 Oct 2019 02:02:56 +0800 Subject: [PATCH 382/956] =?UTF-8?q?csv=E5=8A=A0=E5=85=A5=E5=86=99=E7=BC=93?= =?UTF-8?q?=E5=86=B2=E6=9C=BA=E5=88=B6,=20=E4=BC=98=E5=8C=96csv=E5=A4=A7?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E9=87=8F=E5=86=99=E5=85=A5=E6=95=88=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/csv.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lualib/csv.lua b/lualib/csv.lua index c947ba13..3a9441b8 100644 --- a/lualib/csv.lua +++ b/lualib/csv.lua @@ -48,10 +48,12 @@ function csv.writefile (path, t) if not file then return nil, err end - local headers = t[1] - file:write(concat(headers, ",") .. '\n') - for index = 2, #t do - file:write(concat(t[index], ',') .. '\n') + file:setvbuf("full", 1 << 20) + for index = 1, #t do + local contents = t[index] + if type(contents) == 'table' then + file:write(concat(contents, ',') .. '\n') + end end file:flush() file:close() From 2c5782cde47e02ff67a0ab0f2d0ca6c00b3c247c Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 17 Oct 2019 02:44:50 +0800 Subject: [PATCH 383/956] =?UTF-8?q?=E5=AE=8C=E5=96=84csv=E5=BA=93=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_csv.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/script/test_csv.lua b/script/test_csv.lua index ab52cc26..90bb584d 100644 --- a/script/test_csv.lua +++ b/script/test_csv.lua @@ -1,8 +1,7 @@ local csv = require "csv" -local Log = require("logging"):new() +local LOG = require("logging") -Log:DEBUG(csv.loadfile("./Excel.csv")) +LOG:DEBUG(csv.loadfile("./Excel.csv")) - -local csv.dump("./Excel-1.csv", csv.loadfile("./Excel.csv")) +LOG:DEBUG(csv.writefile("./Excel-1.csv", csv.loadfile("./Excel.csv"))) From 282395bf924be71c702046dc95e167d4c629d72f Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 17 Oct 2019 02:50:32 +0800 Subject: [PATCH 384/956] =?UTF-8?q?=E5=8E=BB=E6=8E=89=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_ffi.lua | 68 ++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/script/test_ffi.lua b/script/test_ffi.lua index 036b49b1..f48c8123 100644 --- a/script/test_ffi.lua +++ b/script/test_ffi.lua @@ -1,37 +1,37 @@ local Log = require "logging":new() local ffi = require "lffi" --- -- 数据类型长度测试 --- Log:DEBUG("uint8_t长度为:"..ffi.sizeof(ffi.new("uint8_t"))) --- Log:DEBUG("uint16_t长度为:"..ffi.sizeof(ffi.new("uint16_t"))) --- Log:DEBUG("uint32_t长度为:"..ffi.sizeof(ffi.new("uint32_t"))) --- Log:DEBUG("uint64_t长度为:"..ffi.sizeof(ffi.new("uint64_t"))) --- --- -- 字符串测试 --- local cdata = ffi.new("char [?]", #"admin", "admin") --- Log:DEBUG("将lua字符串转换为cdata:", cdata) --- local str = ffi.string(cdata) --- Log:DEBUG("将cdata转换为lua字符串:", str) --- --- Log:DEBUG("测试cdata字符串类型是否可以索引:", cdata[0], cdata[1], cdata[2], cdata[3], cdata[4]) --- Log:DEBUG("测试cdata字符串类型是否可以转换:", string.char(cdata[0])..string.char(cdata[1])..string.char(cdata[2])..string.char(cdata[3])..string.char(cdata[4])) --- --- -- 整型数组测试 --- local array = ffi.new("int[?]", 3, 1, 2, 3) -- 初始化方法 1 --- local array = ffi.new("int[3]", 1, 2, 3) -- 初始化方法 2 --- Log:DEBUG(array[0], array[1], array[2]) --- --- -- 结构体创建测试 --- ffi.cdef [[ --- typedef struct cuboid { uint8_t h, w, l; } cuboid_t; --- ]] --- --- local cuboid = ffi.new("cuboid_t", 2 ^ 4, 2 ^ 5, 2 ^ 6) --- Log:DEBUG("创建长方体", cuboid, cuboid.h, cuboid.w, cuboid.l) --- Log:DEBUG("计算体积", cuboid.h * cuboid.w * cuboid.l) --- --- local cuboid_array = ffi.new("cuboid_t[3]", {{11, 12, 13}, {21, 22, 23}, {31, 32, 33}}) --- Log:DEBUG("创建3个长方体并且初始化", cuboid_array) --- Log:DEBUG("3个长方体的长度分别为:", cuboid_array[0].l, cuboid_array[1].l, cuboid_array[2].l) --- Log:DEBUG("3个长方体的宽度分别为:", cuboid_array[0].w, cuboid_array[1].w, cuboid_array[2].w) --- Log:DEBUG("3个长方体的高度分别为:", cuboid_array[0].h, cuboid_array[1].h, cuboid_array[2].h) +-- 数据类型长度测试 +Log:DEBUG("uint8_t长度为:"..ffi.sizeof(ffi.new("uint8_t"))) +Log:DEBUG("uint16_t长度为:"..ffi.sizeof(ffi.new("uint16_t"))) +Log:DEBUG("uint32_t长度为:"..ffi.sizeof(ffi.new("uint32_t"))) +Log:DEBUG("uint64_t长度为:"..ffi.sizeof(ffi.new("uint64_t"))) + +-- 字符串测试 +local cdata = ffi.new("char [?]", #"admin", "admin") +Log:DEBUG("将lua字符串转换为cdata:", cdata) +local str = ffi.string(cdata) +Log:DEBUG("将cdata转换为lua字符串:", str) + +Log:DEBUG("测试cdata字符串类型是否可以索引:", cdata[0], cdata[1], cdata[2], cdata[3], cdata[4]) +Log:DEBUG("测试cdata字符串类型是否可以转换:", string.char(cdata[0])..string.char(cdata[1])..string.char(cdata[2])..string.char(cdata[3])..string.char(cdata[4])) + +-- 整型数组测试 +local array = ffi.new("int[?]", 3, 1, 2, 3) -- 初始化方法 1 +local array = ffi.new("int[3]", 1, 2, 3) -- 初始化方法 2 +Log:DEBUG(array[0], array[1], array[2]) + +-- 结构体创建测试 +ffi.cdef [[ + typedef struct cuboid { uint8_t h, w, l; } cuboid_t; +]] + +local cuboid = ffi.new("cuboid_t", 2 ^ 4, 2 ^ 5, 2 ^ 6) +Log:DEBUG("创建长方体", cuboid, cuboid.h, cuboid.w, cuboid.l) +Log:DEBUG("计算体积", cuboid.h * cuboid.w * cuboid.l) + +local cuboid_array = ffi.new("cuboid_t[3]", {{11, 12, 13}, {21, 22, 23}, {31, 32, 33}}) +Log:DEBUG("创建3个长方体并且初始化", cuboid_array) +Log:DEBUG("3个长方体的长度分别为:", cuboid_array[0].l, cuboid_array[1].l, cuboid_array[2].l) +Log:DEBUG("3个长方体的宽度分别为:", cuboid_array[0].w, cuboid_array[1].w, cuboid_array[2].w) +Log:DEBUG("3个长方体的高度分别为:", cuboid_array[0].h, cuboid_array[1].h, cuboid_array[2].h) From 2a406e34f31564e71d841fc5ceb898151056ecab Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 17 Oct 2019 02:52:39 +0800 Subject: [PATCH 385/956] =?UTF-8?q?=E4=B8=BAcfadmin=E5=A2=9E=E5=8A=A0-e?= =?UTF-8?q?=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 17 +++++++---------- src/core_start.c | 38 +++++++++++++++++++++++++++++--------- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/src/core.c b/src/core.c index 38e614cf..7ee14783 100644 --- a/src/core.c +++ b/src/core.c @@ -86,7 +86,7 @@ L_ALLOC(void *ud, void *ptr, size_t osize, size_t nsize){ void init_lua_libs(lua_State *L){ - /* lua 标准库 */ + /* lua 标准库 */ luaL_openlibs(L); lua_pushglobaltable(L); @@ -153,7 +153,7 @@ signal_init(){ } void -init_main(){ +init_main(const char script_entry[]){ int status = 0; @@ -164,7 +164,7 @@ init_main(){ CO_GCRESET(L); - status = luaL_loadfile(L, "script/main.lua"); + status = luaL_loadfile(L, script_entry); if (status > 1){ LOG("ERROR", lua_tostring(L, -1)); return lua_close(L), _exit(-1); @@ -175,23 +175,20 @@ init_main(){ LOG("ERROR", lua_tostring(L, -1)); return lua_close(L), _exit(-1); } - if (status == LUA_YIELD) { + + if (status == LUA_YIELD) signal_init(); - } } void -core_sys_init(){ - +core_sys_init(const char script_entry[]){ /* hook libev 内存分配 */ core_ev_set_allocator(EV_ALLOC); - /* hook 事件循环错误信息 */ core_ev_set_syserr_cb(ERROR_CB); - /* 初始化Lua脚本 */ - init_main(); + init_main(script_entry); } diff --git a/src/core_start.c b/src/core_start.c index ecaf6bde..0f04f1a3 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -1,7 +1,10 @@ #include "core.h" -void -write_pid(const char *filename) { +#define MAX_ENTRY_LENGTH (1 << 8) + +static char script_entry[MAX_ENTRY_LENGTH] = "script/main.lua"; + +void write_pid(const char *filename) { errno = 0; FILE *f = fopen(filename, "w"); if (!f) { @@ -9,22 +12,39 @@ write_pid(const char *filename) { return exit(-1); } fprintf(f, "%d\n", getpid()); + fflush(f); fclose(f); } -int -main(int argc, char const *argv[]) +void check_args(int argc, char const *argv[]) { + if (argc > 1) { + for (uint32_t index = 0; index < argc; index ++) { + if (!strcmp("-d", argv[index])){ + daemon(1, 0); + continue; + } + if (!strcmp("-e", argv[index])) { + if (argc > index){ + memset(script_entry, 0x0, MAX_ENTRY_LENGTH); + memmove(script_entry, argv[index + 1], strlen(argv[index + 1])); + } + continue; + } + + } + } +} + +int main(int argc, char const *argv[]) { -// #if !defined(__APPLE__) - /* 后台运行 */ - if (argc > 1 && 0 == strcmp("-d", argv[argc-1])) daemon(1, 0); -// #endif + + check_args(argc, argv); /* 建立Pid文件 */ write_pid("cfadmin.pid"); /* 系统初始化 */ - core_sys_init(); + core_sys_init(script_entry); /* 运行事件循环 */ core_sys_run(); From 6d28c81b90405823cb5d8f5df2ad595d2c085f54 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 17 Oct 2019 03:12:16 +0800 Subject: [PATCH 386/956] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E4=B8=8E=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_start.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core_start.c b/src/core_start.c index 0f04f1a3..ccfef1fd 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -18,7 +18,8 @@ void write_pid(const char *filename) { void check_args(int argc, char const *argv[]) { if (argc > 1) { - for (uint32_t index = 0; index < argc; index ++) { + uint32_t index; + for (index = 0; index < argc; index ++) { if (!strcmp("-d", argv[index])){ daemon(1, 0); continue; From d26e5dc7e4f1a01e8e702767e9852cee31d9b213 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 18 Oct 2019 08:04:03 +0800 Subject: [PATCH 387/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=83=E7=89=9BSDK?= =?UTF-8?q?=E8=AE=A4=E8=AF=81=E5=A4=B1=E8=B4=A5=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/cloud/qiniu/crypt.lua | 17 +++++++++++++++++ lualib/cloud/qiniu/token.lua | 12 ++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 lualib/cloud/qiniu/crypt.lua diff --git a/lualib/cloud/qiniu/crypt.lua b/lualib/cloud/qiniu/crypt.lua new file mode 100644 index 00000000..9c918400 --- /dev/null +++ b/lualib/cloud/qiniu/crypt.lua @@ -0,0 +1,17 @@ +local crypt = require "crypt" + +local Crypt = { + hmac_sha1 = crypt.hmac_sha1, + urlencode = crypt.urlencode, + urldecode = crypt.urldecode, +} + +function Crypt.urlsafe_base64encode(data) + return crypt.base64encode(data):gsub('+', '-'):gsub('/', '_') +end + +function Crypt.urlsafe_base64decode(data) + return crypt.base64decode(data:gsub('-', '+'):gsub("_", "/")) +end + +return Crypt \ No newline at end of file diff --git a/lualib/cloud/qiniu/token.lua b/lualib/cloud/qiniu/token.lua index 93e7b82b..4667056a 100644 --- a/lualib/cloud/qiniu/token.lua +++ b/lualib/cloud/qiniu/token.lua @@ -1,8 +1,8 @@ local json = require "json" -local crypt = require "crypt" +local crypt = require "cloud.qiniu.crypt" local hmac_sha1 = crypt.hmac_sha1 local urlencode = crypt.urlencode -local base64encode = crypt.base64encode +local base64encode = crypt.urlsafe_base64encode local type = type local next = next @@ -69,7 +69,7 @@ function Token.newAuthorization (AccessKey, SecretKey, opt) else auth = auth .. '\n\n' end - return 'Qiniu'.. ' ' .. AccessKey .. ':' .. crypt.base64encode(crypt.hmac_sha1(SecretKey, auth)) + return 'Qiniu'.. ' ' .. AccessKey .. ':' .. base64encode(hmac_sha1(SecretKey, auth)) end -- 上传凭证 @@ -97,8 +97,8 @@ function Token.getUploadToken (AccessKey, SecretKey, roles) fileType = toint(roles.filetype) ~= 0 and 1 or 0, -- 文件存储类型(0 为普通存储,1 为低频存储) 默认为: 0 mimeLimit = type(roles.mimeLimit) == 'string' and roles.mimeLimit or nil, -- 限制文件类型 } - local encodedPutPolicy = crypt.base64encode(json.encode(Policy)) - return AccessKey .. ':' .. crypt.base64encode(crypt.hmac_sha1(SecretKey, encodedPutPolicy)) .. ':' .. encodedPutPolicy + local encodedPutPolicy = base64encode(json.encode(Policy)) + return AccessKey .. ':' .. base64encode(hmac_sha1(SecretKey, encodedPutPolicy)) .. ':' .. encodedPutPolicy end -- 下载凭证 @@ -112,7 +112,7 @@ function Token.getDownloadToken (AccessKey, SecretKey, opt) if type(opt) ~= 'table' or not opt.url or not toint(opt.expires) then return nil, "invaild roles." end - return concat({ opt.url .. '?'..'e='.. opt.expires, 'token='.. AccessKey .. ':' .. crypt.base64encode(crypt.hmac_sha1(SecretKey, opt.url)) }, '&') + return concat({ opt.url .. '?'..'e='.. opt.expires, 'token='.. AccessKey .. ':' .. base64encode(hmac_sha1(SecretKey, opt.url)) }, '&') end return Token From daa7e3c4ed39b6e8342230b6852de9b631989f4e Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 18 Oct 2019 08:04:35 +0800 Subject: [PATCH 388/956] =?UTF-8?q?test=5Fupload=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E4=B8=83=E7=89=9BSDK=E4=B8=8A=E4=BC=A0=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_upload.lua | 59 +++++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/script/test_upload.lua b/script/test_upload.lua index 440d5253..4e86c42d 100644 --- a/script/test_upload.lua +++ b/script/test_upload.lua @@ -3,23 +3,25 @@ local json = require "json" local app = httpd:new("httpd") ---[[ +app:use("/upload", function(content) + return [[ 本地文件上传 -
                  - -
                  - -
                  - -
                  +
                  + +
                  + +
                  + +
                  ]] +end) app:api("/upload_local", function (content) if content.files then @@ -38,7 +40,46 @@ app:api("/upload_local", function (content) return json.encode { code = 404, status = "没有上传内容." } end) -app:static("static", 30) + +app:use('/qiniu_upload', function(content) + return [[ + + + + 七牛文件上传 + + + +
                  + + + +
                  +

                  无token

                  + + + +]] +end) + +local oss = require "cloud.qiniu.oss" +app:api('/qiniu_token', function (content) + local access_key = "your_access_key" + local secret_key = "your_secret_key" + return json.encode { + code = 200, + token = oss.getUploadToken(access_key, secret_key, { + bucket = "candymi", -- 对应bucket名称(这个必填, 其它选填) + }), + } +end) + +app:static("static") app:listen("0.0.0.0", 8080) From 8a4ed42bd7e9b268f4346c4d592bab06ee30cc09 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 18 Oct 2019 08:30:02 +0800 Subject: [PATCH 389/956] =?UTF-8?q?=E4=BC=98=E5=8C=96UDP=E5=BA=93=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/UDP.lua | 106 ++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/lualib/internal/UDP.lua b/lualib/internal/UDP.lua index d45bfcdd..915c46a9 100644 --- a/lualib/internal/UDP.lua +++ b/lualib/internal/UDP.lua @@ -11,77 +11,79 @@ local co_wait = co.wait local UDP = class("UDP") function UDP:ctor(opt) - self.udp = udp.new() + self.udp = udp.new() end -- 超时时间 function UDP:timeout(Interval) - if Interval and Interval > 0 then - self._timeout = Interval - end - return self + if Interval and Interval > 0 then + self._timeout = Interval + end + return self end - function UDP:connect(ip, port) - self.fd = udp.connect(ip, port) - if not self.fd or self.fd <= 0 then - return nil, "Can't Creat UDP Socket" - end - return true + self.fd = udp.connect(ip, port) + if not self.fd or self.fd <= 0 then + return nil, "Can't Creat UDP Socket" + end + return true end function UDP:recv(...) - if self.udp then - local co = co_self() - self.read_co = co_new(function ( ... ) - local data, len = udp.recv(self.fd) - if self.timer then - self.timer:stop() - self.timer = nil - end - udp.stop(self.udp) - self.read_co = nil - if data then - return co_wakeup(co, data, len) - end - return co_wakeup(co, nil, '未知的udp错误') - end) - self.timer = ti.timeout(self._timeout, function ( ... ) - udp.stop(self.udp) - self.read_co = nil - self.timer = nil - return co_wakeup(co, nil, 'udp_recv timout(超时)..') - end) - udp.start(self.udp, self.fd, self.read_co) - return co_wait() - end + if self.udp then + local co = co_self() + self.read_co = co_new(function ( ... ) + local data, len = udp.recv(self.fd) + if self.timer then + self.timer:stop() + self.timer = nil + end + udp.stop(self.udp) + self.read_co = nil + if data then + return co_wakeup(co, data, len) + end + return co_wakeup(co, nil, '未知的udp错误') + end) + self.timer = ti.timeout(self._timeout, function ( ... ) + udp.stop(self.udp) + self.read_co = nil + self.timer = nil + return co_wakeup(co, nil, 'udp_recv timout(超时)..') + end) + udp.start(self.udp, self.fd, self.read_co) + return co_wait() + end end function UDP:send(data) - if type(data) ~= 'string' or not self.fd or self.fd <= 0 then - Log:ERROR("Send udp Error: 不完整的参数:"..(data or '')..','..(self.fd or '-1')) - return - end - return udp.send(self.fd, data, #data) + if type(data) ~= 'string' or not self.fd or self.fd <= 0 then + Log:ERROR("Send udp Error: 不完整的参数:"..(data or '')..','..(self.fd or '-1')) + return + end + return udp.send(self.fd, data, #data) end function UDP:close() + if self.udp then + udp.stop(udp) + self.udp = nil + end - if self.udp then - self.udp = nil - end - - if self.fd then - udp.close(self.fd) - self.fd = nil - end + if self.timer then + ti.stop(self.timer) + self.timer = nil + end - if self._timeout then - self._timeout = nil - end + if self.fd then + udp.close(self.fd) + self.fd = nil + end - -- var_dump(self) + if self._timeout then + self._timeout = nil + end end return UDP From 42eeb8d8a7e71d54e8d513b37822cd4503c99d0b Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 18 Oct 2019 08:39:43 +0800 Subject: [PATCH 390/956] =?UTF-8?q?=E6=A8=A1=E5=9D=97=E5=8C=96http?= =?UTF-8?q?=E5=8D=8F=E8=AE=AE=E5=AE=9E=E7=8E=B0=E4=BB=A3=E7=A0=81,=20?= =?UTF-8?q?=E5=87=8F=E5=B0=91=E6=97=A0=E7=94=A8=E7=9A=84=E5=AF=BC=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/protocol.lua | 10 +- lualib/protocol/http.lua | 697 -------------------------------- lualib/protocol/http/code.lua | 71 ++++ lualib/protocol/http/init.lua | 513 +++++++++++++++++++++++ lualib/protocol/http/mime.lua | 91 +++++ lualib/protocol/http/parser.lua | 34 ++ 6 files changed, 714 insertions(+), 702 deletions(-) delete mode 100644 lualib/protocol/http.lua create mode 100644 lualib/protocol/http/code.lua create mode 100644 lualib/protocol/http/init.lua create mode 100644 lualib/protocol/http/mime.lua create mode 100644 lualib/protocol/http/parser.lua diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua index 0a0ca150..11f0e6cc 100644 --- a/lualib/httpc/protocol.lua +++ b/lualib/httpc/protocol.lua @@ -3,10 +3,10 @@ local tcp = require "internal.TCP" local url = require "url" local url_encode = url.encode -local HTTP = require "protocol.http" -local FILEMIME = HTTP.FILEMIME -local PARSER_HTTP_RESPONSE = HTTP.PARSER_HTTP_RESPONSE -local RESPONSE_CHUNKED_PARSER = HTTP.RESPONSE_CHUNKED_PARSER +local HTTP_PARSER = require "protocol.http.parser" +local FILEMIME = require "protocol.http.mime" +local PARSER_HTTP_RESPONSE = HTTP_PARSER.PARSER_HTTP_RESPONSE +local RESPONSE_CHUNKED_PARSER = HTTP_PARSER.RESPONSE_CHUNKED_PARSER local type = type local assert = assert @@ -355,7 +355,7 @@ local function build_file_req (opt) filename = '' end insert(body, fmt(cd, fmt('name="%s"', name) .. '; ' .. fmt('filename="%s"', filename))) - insert(body, fmt(ct, FILEMIME(file.type or '') or 'application/octet-stream') .. CRLF) + insert(body, fmt(ct, FILEMIME[file.type or ''] or 'application/octet-stream') .. CRLF) end insert(body, file.file) end diff --git a/lualib/protocol/http.lua b/lualib/protocol/http.lua deleted file mode 100644 index a7b867e7..00000000 --- a/lualib/protocol/http.lua +++ /dev/null @@ -1,697 +0,0 @@ -local log = require "logging" -local sys = require "system" -local tcp = require "internal.TCP" -local wsserver = require "protocol.websocket.server" - -local Log = log:new({ dump = true, path = 'protocol-http'}) - -local crypt = require "crypt" -local sha1 = crypt.sha1 -local base64 = crypt.base64encode -local now = sys.now -local DATE = require("sys").date -local insert = table.insert - -local form = require "httpd.Form" -local FILE_TYPE = form.FILE -local ARGS_TYPE = form.ARGS -local form_multipart = form.multipart -local form_urlencode = form.urlencode - -local Cookie = require "httpd.Cookie" -local clCookie = Cookie.clean -- 清理 -local secCookie = Cookie.setSecure -- 设置Cookie加密字段 -local seCookie = Cookie.serialization -- 序列化 -local deCookie = Cookie.deserialization -- 反序列化 - - -local Router = require "httpd.Router" -local ROUTE_FIND = Router.find -local ROUTE_REGISTERY = Router.registery - -local httpparser = require "httpparser" -local PARSER_HTTP_REQUEST = httpparser.parser_http_request -local PARSER_HTTP_RESPONSE = httpparser.parser_http_response -local RESPONSE_CHUNKED_PARSER = httpparser.parser_response_chunked - -local type = type -local tostring = tostring -local next = next -local pcall = pcall -local ipairs = ipairs -local time = os.time -local lower = string.lower -local upper = string.upper -local match = string.match -local fmt = string.format -local toint = math.tointeger -local find = string.find -local split = string.sub -local splite = string.gmatch -local spliter = string.gsub -local remove = table.remove -local concat = table.concat - -local CRLF = '\x0d\x0a' -local CRLF2 = '\x0d\x0a\x0d\x0a' -local RE_CRLF2 = '[\x0d]?\x0a[\x0d]?\x0a' - -local SERVER = 'cf web/0.1' - -local HTTP_CODE = { - - [100] = "HTTP/1.1 100 Continue", - [101] = "HTTP/1.1 101 Switching Protocol", - [102] = "HTTP/1.1 102 Processing", - - [200] = "HTTP/1.1 200 OK", - [201] = "HTTP/1.1 201 Created", - [202] = "HTTP/1.1 202 Accepted", - [203] = "HTTP/1.1 203 Non-Authoritative Information", - [204] = "HTTP/1.1 204 No Content", - [205] = "HTTP/1.1 205 Reset Content", - [206] = "HTTP/1.1 206 Partial Content", - [207] = "HTTP/1.1 207 Multi-Status", - [208] = "HTTP/1.1 208 Multi-Status", - [226] = "HTTP/1.1 226 IM Used", - - [300] = "HTTP/1.1 300 Multiple Choice", - [301] = "HTTP/1.1 301 Moved Permanently", - [302] = "HTTP/1.1 302 Found", - [303] = "HTTP/1.1 303 See Other", - [304] = "HTTP/1.1 304 Not Modified", - [305] = "HTTP/1.1 305 Use Proxy", - [306] = "HTTP/1.1 306 unused", - [307] = "HTTP/1.1 307 Temporary Redirect", - [305] = "HTTP/1.1 308 Permanent Redirect", - - [400] = "HTTP/1.1 400 Bad Request", - [401] = "HTTP/1.1 401 Unauthorized", - [402] = "HTTP/1.1 402 Payment Required", - [403] = "HTTP/1.1 403 Forbidden", - [404] = "HTTP/1.1 404 Not Found", - [405] = "HTTP/1.1 405 Method Not Allowed", - [406] = "HTTP/1.1 406 Not Acceptable", - [407] = "HTTP/1.1 407 Proxy Authentication Required", - [408] = "HTTP/1.1 408 Request Timeout", - [409] = "HTTP/1.1 409 Conflict", - - [410] = "HTTP/1.1 410 Gone", - [411] = "HTTP/1.1 411 Length Required", - [412] = "HTTP/1.1 412 Precondition Failed", - [413] = "HTTP/1.1 413 Payload Too Large", - [414] = "HTTP/1.1 414 URI Too Long", - [415] = "HTTP/1.1 415 Unsupported Media Type", - [416] = "HTTP/1.1 416 Requested Range Not Satisfiable", - [417] = "HTTP/1.1 417 Expectation Failed", - [418] = "HTTP/1.1 418 I'm a teapot", - - [421] = "HTTP/1.1 421 Misdirected Request", - [422] = "HTTP/1.1 422 Unprocessable Entity (WebDAV)", - [423] = "HTTP/1.1 423 Locked (WebDAV)", - [424] = "HTTP/1.1 424 Failed Dependency", - [426] = "HTTP/1.1 426 Upgrade Required", - [428] = "HTTP/1.1 428 Precondition Required", - [429] = "HTTP/1.1 429 Too Many Requests", - [431] = "HTTP/1.1 431 Request Header Fields Too Large", - [451] = "HTTP/1.1 451 Unavailable For Legal Reasons", - - [500] = "HTTP/1.1 500 Internal Server Error", - [501] = "HTTP/1.1 501 Not Implemented", - [502] = "HTTP/1.1 502 Bad Gateway", - [503] = "HTTP/1.1 503 Service Unavailable", - [504] = "HTTP/1.1 504 Gateway Timeout", - [505] = "HTTP/1.1 505 HTTP Version Not Supported", - [506] = "HTTP/1.1 506 Variant Also Negotiates", - [507] = "HTTP/1.1 507 Insufficient Storage", - [508] = "HTTP/1.1 508 Loop Detected (WebDAV)", - [510] = "HTTP/1.1 510 Not Extended", - [503] = "HTTP/1.1 511 Network Authentication Required", - -} - -local MIME = { - -- 文本格式 - ['xml'] = 'application/xml', - ['htm'] = 'text/html', - ['html'] = 'text/html', - ['shtml'] = 'text/html', - ['xhtml'] = 'application/xhtml+xml', - ['txt'] = 'text/plain', - ['css'] = 'text/css', - ['json'] = 'application/json', - ['rtf'] = 'application/rtf', - ['ogx'] = 'application/ogg', - -- 图片格式 - ['bmp'] = 'image/bmp', - ['png'] = 'image/png', - ['gif'] = 'image/gif', - ['jpeg'] = 'image/jpeg', - ['jpg'] = 'image/jpeg', - ['ico'] = 'image/x-icon', - ['tif'] = 'image/tiff', - ['tiff'] = 'image/tiff', - ['svg'] = 'image/svg+xml', - -- 音频 - ['au'] = 'audio/basic', - ['wav'] = 'audio/wav', - ['acc'] = 'audio/aac', - ['mp3'] = 'audio/mpeg', - ['oga'] = 'audio/ogg', - -- 视频 - ['avi'] = 'video/x-msvideo', - ['mpa'] = 'video/mpeg', - ['mpe'] = 'video/mpeg', - ['mp2'] = 'video/mpeg', - ['mpe'] = 'video/mpeg', - ['mpeg'] = 'video/mpeg', - ['mp4'] = 'audio/mp4', - ['qt'] = 'video/mpeg', - ['mov'] = 'video/mpeg', - ['webm'] = 'video/webm', - ['flv'] = 'video/x-flv', - ['m4v'] = 'video/x-m4v', - ['3gp'] = 'video/3gpp', - ['3gpp'] = 'video/3gpp', - ['ts'] = 'video/mp2t', - ['ogv'] = 'video/ogg', - -- 文档 - ['csv'] = 'text/csv', - ['dot'] = 'application/msword', - ['doc'] = 'application/msword', - ['mdb'] = 'application/x-msaccess', - ['xls'] = 'application/vnd.ms-excel', - ['ppt'] = 'application/vnd.ms-powerpoint', - ['chm'] = 'application/vnd.ms-htmlhelp', - ['xlsx'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - ['docx'] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - ['pptx'] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation', - ['pdf'] = 'application/pdf', - -- 密匙 - ['der'] = 'application/x-x509-ca-cert', - ['cer'] = 'application/x-x509-ca-cert', - ['pem'] = 'application/x-x509-ca-cert', - ['crt'] = 'application/x-x509-ca-cert', - ['p10'] = 'application/pkcs10', - ['p12'] = 'application/x-pkcs12', - ['pfx'] = 'application/x-pkcs12', - ['p7c'] = 'application/x-pkcs7-mime', - ['p7m'] = 'application/x-pkcs7-mime', - ['p7b'] = 'application/x-pkcs7-certificates', - ['spc'] = 'application/x-pkcs7-certificates', - ['p7r'] = 'application/x-pkcs7-certreqresp', - ['p7s'] = 'application/x-pkcs7-signature', - -- 压缩文件 - ['7z'] = 'application/x-7z-compressed', - ['zip'] = 'application/zip', - ['gz'] = 'application/x-gzip', - ['tar'] = 'application/x-tar', - ['gtar'] = 'application/x-gtar', - ['tgz'] = 'application/x-compressed', - ['rar'] = 'application/x-rar-compressed', - -- 源文件 - ['c'] = 'text/plain', - ['cxx'] = 'text/plain', - ['cpp'] = 'text/plain', - ['h'] = 'text/plain', - ['hpp'] = 'text/plain', - ['py'] = 'text/plain', - ['cs'] = 'text/plain', - ['lua'] = 'text/plain', - ['js'] = 'application/javascript', - -- TODO -} - -local HTTP_PROTOCOL = { - API = 1, - [1] = "API", - USE = 2, - [2] = "USE", - STATIC = 3, - [3] = "STATIC", - WS = 4, - [4] = "WS", -} - --- 解析http请求 -function HTTP_PROTOCOL.PARSER_HTTP_REQUEST (buffer) - local ok, method, path, version, header = pcall(PARSER_HTTP_REQUEST, buffer) - if not ok then - return nil - end - return method, path, version, header -end - --- 解析http回应 -function HTTP_PROTOCOL.PARSER_HTTP_RESPONSE (buffer) - local ok, version, code, status, header = pcall(PARSER_HTTP_RESPONSE, buffer) - if not ok then - return nil - end - return version, code, status, header -end - --- 解析回应chunked -function HTTP_PROTOCOL.RESPONSE_CHUNKED_PARSER (data) - local ok, data, pos = pcall(RESPONSE_CHUNKED_PARSER, data) - if not ok then - return nil, -1 - end - return data, pos -end - --- 以下为 HTTP Server 所需所用方法 -local function REQUEST_STATUCODE_RESPONSE(code) - return HTTP_CODE[code] or "attempt to Passed A Invaid Code to response message." -end - -local function REQUEST_MIME_RESPONSE(mime) - return MIME[mime] -end - -function HTTP_PROTOCOL.FILEMIME(mime) - return MIME[mime] -end - --- -- 路由注册 -HTTP_PROTOCOL.ROUTE_REGISTERY = ROUTE_REGISTERY - --- -- 路由查找 -HTTP_PROTOCOL.ROUTE_FIND = ROUTE_FIND - -local function HTTP_DATE() - return DATE("Date: %a, %d %b %Y %X GMT") - -- return os.date("Date: %a, %d %b %Y %X GMT") -end - -local function HTTP_EXPIRES(timestamp) - return DATE("Expires: %a, %d %b %Y %X GMT", timestamp) - -- return os.date("Date: %a, %d %b %Y %X GMT") -end - -local function PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEADER) - local content = {} - if METHOD == "GET" then - local spl_pos = find(PATH, '%?') - if spl_pos and spl_pos < #PATH then - content['args'] = form_urlencode(PATH) - end - elseif METHOD == "POST" or METHOD == "PUT" then - local body_len = toint(HEADER['Content-Length']) or toint(HEADER['Content-length']) - if body_len and body_len > 0 then - local BODY = '' - local RECV_BODY = true - local CRLF_START, CRLF_END = find(buffer, CRLF2) - if #buffer > CRLF_END then - BODY = split(buffer, CRLF_END + 1, -1) - if #BODY == body_len then - RECV_BODY = false - end - end - if RECV_BODY then - local buffers = {BODY} - while 1 do - local buf = sock:recv(1024) - if not buf then - return - end - buffers[#buffers+1] = buf - local buffer = concat(buffers) - if #buffer >= (max_body_size or 1024 * 1024) then - return nil, 413 - end - if #buffer == body_len then - BODY = buffer - break - end - end - end - local FILE_ENCODE = 'multipart/form-data' - local XML_ENCODE_1 = 'text/xml' - local XML_ENCODE_2 = 'application/xml' - local JSON_ENCODE = 'application/json' - local URL_ENCODE = 'application/x-www-form-urlencoded' - local format = match(HEADER['Content-type'] or HEADER['Content-Type'] or '', '(.-/[^;]*)') - if format == FILE_ENCODE then - local BOUNDARY = match(HEADER['Content-Type'], '^.+=[%-]*(.+)') - if BOUNDARY and BOUNDARY ~= '' then - local typ, body = form_multipart(BODY, BOUNDARY) - if typ == FILE_TYPE then - content['files'] = body - elseif typ == ARGS_TYPE then - content['args'] = {} - for _, args in ipairs(body) do - content['args'][args[1]] = args[2] - end - end - end - elseif format == JSON_ENCODE then - content['json'] = true - content['body'] = BODY - elseif format == XML_ENCODE_1 or format == XML_ENCODE_2 then - content['xml'] = true - content['body'] = BODY - elseif format == URL_ENCODE then - content['args'] = form_urlencode(BODY) - else - content['body'] = BODY - end - end - elseif METHOD == "HEAD" or METHOD == "OPTIONS" then - return true, nil - else - -- 暂未支持其他方法 - return - end - return true, content -end - -local function X_Forwarded_FORMAT(tab) - local ip_list - if tab and type(tab) == 'string' then - for ip in splite(tab, '([^ ,;]+)') do - if not ip_list then - ip_list = {ip} - else - if ip ~= ip_list[#ip_list] then - ip_list[#ip_list+1] = ip - end - end - end - return concat(ip_list, ' -> ') - end - return tab -end --- 一些错误返回 -local function ERROR_RESPONSE(http, code, path, ip, forword, method, speed) - http:tolog(code, path, ip, X_Forwarded_FORMAT(forword) or ip, method, speed) - return concat({concat({ - REQUEST_STATUCODE_RESPONSE(code), - HTTP_DATE(), - 'Origin: *', - 'Allow: GET, POST, PUT, HEAD, OPTIONS', - 'Connection: close', - 'Content-length: 0', - 'Server: ' .. (http.__server or SERVER), - }, CRLF), CRLF2}) -end - --- WebSocket -local function Switch_Protocol(http, cls, sock, header, method, version, path, ip, start_time) - if method ~= 'GET' then - return sock:send(ERROR_RESPONSE(http, 400, path, ip, header['X-Forwarded-For'] or ip, method, now() - start_time)) - end - if version ~= 1.1 then - return sock:send(ERROR_RESPONSE(http, 400, path, ip, header['X-Forwarded-For'] or ip, method, now() - start_time)) - end - if not header['Upgrade'] or lower(header['Upgrade']) ~= 'websocket' then - return sock:send(ERROR_RESPONSE(http, 401, path, ip, header['X-Forwarded-For'] or ip, method, now() - start_time)) - end - if header['Sec-WebSocket-Version'] ~= '13' then - return sock:send(ERROR_RESPONSE(http, 403, path, ip, header['X-Forwarded-For'] or ip, method, now() - start_time)) - end - local sec_key = header['Sec-WebSocket-Key'] - if not sec_key or sec_key == '' then - return sock:send(ERROR_RESPONSE(http, 505, path, ip, header['X-Forwarded-For'] or ip, method, now() - start_time)) - end - local response = { - REQUEST_STATUCODE_RESPONSE(101), - HTTP_DATE(), - 'Server: '..(http.__server or SERVER), - 'Upgrade: WebSocket', - 'Connection: Upgrade', - 'Sec-WebSocket-Accept: '..base64(sha1(sec_key..'258EAFA5-E914-47DA-95CA-C5AB0DC85B11')) - } - local protocol = header['Sec-Websocket-Protocol'] - if protocol then -- 仅支持协议回传 - response[#response+1] = "Sec-Websocket-Protocol: "..tostring(protocol) - end - http:tolog(101, path, header['X-Real-IP'] or ip, X_Forwarded_FORMAT(header['X-Forwarded-For'] or ip), method, now() - start_time) - local ok = sock:send(concat(response, CRLF)..CRLF2) - if not ok then - return - end - return wsserver:new({cls = cls, sock = sock}):start() -end - -local function send_header (sock, header) - header[#header+1] = CRLF - return sock:send(concat(header, CRLF)) -end - -local function send_body (sock, body, filepath) - if not body then - return sock:sendfile(filepath, 65535) - end - return sock:send(body) -end - -function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) - local buffers = {} - local ttl = http.ttl - local server = http.__server - local timeout = http.__timeout or 30 - local cookie = http.__cookie - local cookie_secure = http.__cookie_secure - local before_func = http._before_func - local max_path_size = http.__max_path_size - local max_header_size = http.__max_header_size - local max_body_size = http.__max_body_size - secCookie(cookie_secure) -- 如果需要 - local sock = tcp:new():set_fd(fd):timeout(timeout) - while 1 do - local buf = sock:recv(1024) - if not buf then - return sock:close() - end - buffers[#buffers+1] = buf - local buffer = concat(buffers) - local CRLF_START, CRLF_END = find(buffer, RE_CRLF2) - if CRLF_START and CRLF_END then - local start = now() - -- 协议有问题返回400 - local METHOD, PATH, VERSION, HEADER = PARSER_HTTP_REQUEST(buffer) - if not METHOD or not PATH or not VERSION then - sock:send(ERROR_RESPONSE(http, 400, PATH, HEADER and HEADER['X-Real-IP'] or ipaddr, HEADER and HEADER['X-Forwarded-For'] or ipaddr, METHOD or "GET", now() - start)) - return sock:close() - end - -- 超过自定义最大PATH长度限制 - if PATH and #PATH > (max_path_size or 1024) then - sock:send(ERROR_RESPONSE(http, 414, PATH, HEADER and HEADER['X-Real-IP'] or ipaddr, HEADER and HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) - return sock:close() - end - -- 没有HEADER返回400 - if not HEADER or not next(HEADER) then - sock:send(ERROR_RESPONSE(http, 400, PATH, HEADER and HEADER['X-Real-IP'] or ipaddr, HEADER and HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) - return sock:close() - end - -- 超过自定义最大HEADER长度限制 - if #buffer - CRLF_START > (max_header_size or 65535) then - sock:send(ERROR_RESPONSE(http, 431, PATH, HEADER and HEADER['X-Real-IP'] or ipaddr, HEADER and HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) - return sock:close() - end - -- 这里根据PATH先查找路由, 如果没有直接返回404. - local cls, typ = ROUTE_FIND(METHOD, PATH) - if not cls or not typ then - sock:send(ERROR_RESPONSE(http, 404, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) - return sock:close() - end - -- 根据请求方法进行解析, 解析失败返回501 - local ok, content = PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEADER) - if not ok then - if content == 413 then - sock:send(ERROR_RESPONSE(http, 413, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) - return sock:close() - end - sock:send(ERROR_RESPONSE(http, 501, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) - return sock:close() - end - if not content then -- 没有 Content则返回200; - http:tolog(200, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, now() - start) - sock:send(concat({REQUEST_STATUCODE_RESPONSE(200), HTTP_DATE(), - 'Origin: *', - 'Allow: GET, POST, PUT, HEAD, OPTIONS', - 'Access-Control-Allow-Origin: *', - 'Access-Control-Allow-Headers: *', - 'Access-Control-Allow-Methods: GET, POST, PUT, HEAD, OPTIONS', - 'Access-Control-Allow-Credentials: true', - 'Access-Control-Max-Age: 86400', - 'Connection: keep-alive', - 'Server: ' .. (server or SERVER), - }, CRLF)..CRLF2) - return sock:close() - end - content['ROUTE'] = HTTP_PROTOCOL[typ] - content['method'], content['path'], content['headers'] = METHOD, PATH, HEADER - -- before 函数只影响接口与view - if before_func and (typ == HTTP_PROTOCOL.API or typ == HTTP_PROTOCOL.USE) then - local ok, code, data = pcall(before_func, content) - if not ok then -- before 函数执行出错 - Log:ERROR(code) - sock:send(ERROR_RESPONSE(http, 500, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) - return sock:close() - end - if code then - if type(code) == "number" then - if code < 200 or code > 500 then - Log:ERROR("before function: Illegal return value") - sock:send(ERROR_RESPONSE(http, 500, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) - return sock:close() - elseif code == 301 or code == 302 then - http:tolog(code, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, now() - start) - sock:send(concat({ - REQUEST_STATUCODE_RESPONSE(code), HTTP_DATE(), - 'Connection: close', - 'Server: ' .. (server or SERVER), - 'Location: ' .. (data or "https://github.com/CandyMi/core_framework"), - }, CRLF)..CRLF2) - return sock:close() - elseif code ~= 200 then - if data then - if type(data) == 'string' and data ~= '' then - http:tolog(code, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, now() - start) - sock:send(concat({concat({ - REQUEST_STATUCODE_RESPONSE(code), HTTP_DATE(), - 'Origin: *', - 'Allow: GET, POST, PUT, HEAD, OPTIONS', - 'Server: ' .. (server or SERVER), - 'Connection: close', - 'Content-Type: ' .. REQUEST_MIME_RESPONSE('html'), - 'Content-Length: '..tostring(#data), - }, CRLF), CRLF2, data})) - return sock:close() - end - end - sock:send(ERROR_RESPONSE(http, code, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) - return sock:close() - end - else - sock:send(ERROR_RESPONSE(http, 401, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) - return sock:close() - end - else - sock:send(ERROR_RESPONSE(http, 401, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) - return sock:close() - end - end - - local header = { } - local ok, body, body_len, filepath, static, statucode - - if typ == HTTP_PROTOCOL.API or typ == HTTP_PROTOCOL.USE then - -- 如果httpd开启了记录Cookie字段, 则每次尝试是否deCookie - if cookie and typ == HTTP_PROTOCOL.USE then - deCookie(content['headers']["Cookie"]) - end - if type(cls) == "table" then - local method = cls[lower(METHOD)] - if not method or type(method) ~= 'function' then -- 注册的路由未实现这个方法 - sock:send(ERROR_RESPONSE(http, 405, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) - return sock:close() - end - local c = cls:new(content) - ok, body = pcall(method, c) - else - ok, body = pcall(cls, content) - end - -- 如果httpd开启了记录Cookie字段, 则每次尝试是否需要seCookie - if cookie and typ == HTTP_PROTOCOL.USE then - local Cookies = seCookie() - for _, Cookie in ipairs(Cookies) do - header[#header+1] = Cookie - end - clCookie() - end - if not ok then - Log:ERROR(body or "empty response.") - sock:send(ERROR_RESPONSE(http, 500, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) - return sock:close() - end - statucode = 200 - insert(header, 1, REQUEST_STATUCODE_RESPONSE(statucode)) - elseif typ == HTTP_PROTOCOL.WS then - local ok, msg = pcall(Switch_Protocol, http, cls, sock, HEADER, METHOD, VERSION, PATH, HEADER['X-Real-IP'] or ipaddr, start) - if not ok then - Log:ERROR(msg) - end - return sock:close() - else - local file_type - local path = PATH - local pos, _ = find(PATH, '%?') - if pos then - path = split(PATH, 1, pos - 1) - end - body_len, filepath, file_type = cls(path) - if not body_len then - statucode = 404 - sock:send(ERROR_RESPONSE(http, statucode, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) - return sock:close() - end - statucode = 200 - header[#header+1] = REQUEST_STATUCODE_RESPONSE(statucode) - local conten_type = REQUEST_MIME_RESPONSE(lower(file_type or '')) - if not conten_type then - header[#header+1] = 'Content-Disposition: attachment' -- 确保浏览器提示需要下载 - static = fmt('Content-Type: %s', 'application/octet-stream') - else - static = fmt('Content-Type: %s', conten_type..'; charset=utf-8') - end - -- 如果是静态文件, 增加默认跨域访问支持 - header[#header+1] = "Access-Control-Allow-Origin: *" - end - header[#header+1] = HTTP_DATE() - header[#header+1] = 'Origin: *' - header[#header+1] = 'Allow: GET, POST, PUT, HEAD, OPTIONS' - header[#header+1] = 'Server: ' .. (server or SERVER) - local Connection = 'Connection: keep-alive' - if not HEADER['Connection'] or lower(HEADER['Connection']) == 'close' then - Connection = 'Connection: close' - end - header[#header+1] = Connection - if Connection == 'Connection: keep-alive' then - header[#header+1] = "Keep-Alive: timeout="..timeout - end - if typ == HTTP_PROTOCOL.API or typ == HTTP_PROTOCOL.USE then - if typ == HTTP_PROTOCOL.API then - header[#header+1] = 'Content-Type: ' .. REQUEST_MIME_RESPONSE('json') .. "; charset=utf-8" - else - header[#header+1] = 'Content-Type: ' .. REQUEST_MIME_RESPONSE('html') .. "; charset=utf-8" - end - if type(body) ~= 'string' or body == '' then - Log:ERROR("Response Error ["..(split(PATH , 1, (find(PATH, '?') or 0 ) - 1)).."]: response must be a string and not empty.") - sock:send(ERROR_RESPONSE(http, 500, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) - return sock:close() - end - header[#header+1] = 'Content-Length: '.. #body - header[#header+1] = 'Cache-Control: no-cache, no-store, must-revalidate' - header[#header+1] = 'Cache-Control: no-cache' - else - if ttl then - header[#header+1] = HTTP_EXPIRES(time() + ttl) - end - if body_len then - header[#header+1] = 'Content-Length: '.. body_len - end - header[#header+1] = static - end - -- 不计算数据传输时间, 仅计算实际回调处理所用时间. - http:tolog(statucode, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, now() - start) - -- 根据实际情况分块发送 - local ok = send_header(sock, header) and send_body(sock, body, filepath) or false - if not ok then - return sock:close() - end - if statucode ~= 200 or Connection ~= 'Connection: keep-alive' then - return sock:close() - end - buffers = {} - end - if #buffers ~= 0 and #buffer > (max_header_size or 65535) then - sock:send(ERROR_RESPONSE(http, 431, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) - return sock:close() - end - end -end - -return HTTP_PROTOCOL diff --git a/lualib/protocol/http/code.lua b/lualib/protocol/http/code.lua new file mode 100644 index 00000000..f7f614b4 --- /dev/null +++ b/lualib/protocol/http/code.lua @@ -0,0 +1,71 @@ +return { + + [100] = "HTTP/1.1 100 Continue", + [101] = "HTTP/1.1 101 Switching Protocol", + [102] = "HTTP/1.1 102 Processing", + + [200] = "HTTP/1.1 200 OK", + [201] = "HTTP/1.1 201 Created", + [202] = "HTTP/1.1 202 Accepted", + [203] = "HTTP/1.1 203 Non-Authoritative Information", + [204] = "HTTP/1.1 204 No Content", + [205] = "HTTP/1.1 205 Reset Content", + [206] = "HTTP/1.1 206 Partial Content", + [207] = "HTTP/1.1 207 Multi-Status", + [208] = "HTTP/1.1 208 Multi-Status", + [226] = "HTTP/1.1 226 IM Used", + + [300] = "HTTP/1.1 300 Multiple Choice", + [301] = "HTTP/1.1 301 Moved Permanently", + [302] = "HTTP/1.1 302 Found", + [303] = "HTTP/1.1 303 See Other", + [304] = "HTTP/1.1 304 Not Modified", + [305] = "HTTP/1.1 305 Use Proxy", + [306] = "HTTP/1.1 306 unused", + [307] = "HTTP/1.1 307 Temporary Redirect", + [305] = "HTTP/1.1 308 Permanent Redirect", + + [400] = "HTTP/1.1 400 Bad Request", + [401] = "HTTP/1.1 401 Unauthorized", + [402] = "HTTP/1.1 402 Payment Required", + [403] = "HTTP/1.1 403 Forbidden", + [404] = "HTTP/1.1 404 Not Found", + [405] = "HTTP/1.1 405 Method Not Allowed", + [406] = "HTTP/1.1 406 Not Acceptable", + [407] = "HTTP/1.1 407 Proxy Authentication Required", + [408] = "HTTP/1.1 408 Request Timeout", + [409] = "HTTP/1.1 409 Conflict", + + [410] = "HTTP/1.1 410 Gone", + [411] = "HTTP/1.1 411 Length Required", + [412] = "HTTP/1.1 412 Precondition Failed", + [413] = "HTTP/1.1 413 Payload Too Large", + [414] = "HTTP/1.1 414 URI Too Long", + [415] = "HTTP/1.1 415 Unsupported Media Type", + [416] = "HTTP/1.1 416 Requested Range Not Satisfiable", + [417] = "HTTP/1.1 417 Expectation Failed", + [418] = "HTTP/1.1 418 I'm a teapot", + + [421] = "HTTP/1.1 421 Misdirected Request", + [422] = "HTTP/1.1 422 Unprocessable Entity (WebDAV)", + [423] = "HTTP/1.1 423 Locked (WebDAV)", + [424] = "HTTP/1.1 424 Failed Dependency", + [426] = "HTTP/1.1 426 Upgrade Required", + [428] = "HTTP/1.1 428 Precondition Required", + [429] = "HTTP/1.1 429 Too Many Requests", + [431] = "HTTP/1.1 431 Request Header Fields Too Large", + [451] = "HTTP/1.1 451 Unavailable For Legal Reasons", + + [500] = "HTTP/1.1 500 Internal Server Error", + [501] = "HTTP/1.1 501 Not Implemented", + [502] = "HTTP/1.1 502 Bad Gateway", + [503] = "HTTP/1.1 503 Service Unavailable", + [504] = "HTTP/1.1 504 Gateway Timeout", + [505] = "HTTP/1.1 505 HTTP Version Not Supported", + [506] = "HTTP/1.1 506 Variant Also Negotiates", + [507] = "HTTP/1.1 507 Insufficient Storage", + [508] = "HTTP/1.1 508 Loop Detected (WebDAV)", + [510] = "HTTP/1.1 510 Not Extended", + [503] = "HTTP/1.1 511 Network Authentication Required", + +} \ No newline at end of file diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua new file mode 100644 index 00000000..d2bf0a71 --- /dev/null +++ b/lualib/protocol/http/init.lua @@ -0,0 +1,513 @@ +local log = require "logging" +local sys = require "system" +local tcp = require "internal.TCP" +local wsserver = require "protocol.websocket.server" + +local Log = log:new({ dump = true, path = 'protocol-http'}) + +local crypt = require "crypt" +local sha1 = crypt.sha1 +local base64encode = crypt.base64encode +local now = sys.now +local DATE = require("sys").date +local insert = table.insert + +local form = require "httpd.Form" +local FILE_TYPE = form.FILE +local ARGS_TYPE = form.ARGS +local form_multipart = form.multipart +local form_urlencode = form.urlencode + +local Cookie = require "httpd.Cookie" +local clCookie = Cookie.clean -- 清理 +local secCookie = Cookie.setSecure -- 设置Cookie加密字段 +local seCookie = Cookie.serialization -- 序列化 +local deCookie = Cookie.deserialization -- 反序列化 + + +local Router = require "httpd.Router" +local ROUTE_FIND = Router.find +local ROUTE_REGISTERY = Router.registery + +local type = type +local tostring = tostring +local next = next +local pcall = pcall +local ipairs = ipairs +local time = os.time +local lower = string.lower +local upper = string.upper +local match = string.match +local fmt = string.format +local toint = math.tointeger +local find = string.find +local split = string.sub +local splite = string.gmatch +local spliter = string.gsub +local remove = table.remove +local concat = table.concat + +local CRLF = '\x0d\x0a' +local CRLF2 = '\x0d\x0a\x0d\x0a' +local RE_CRLF2 = '[\x0d]?\x0a[\x0d]?\x0a' + +local SERVER = 'cf web/0.1' + +local HTTP_CODE = require "protocol.http.code" + +local MIME = require "protocol.http.mime" + +local HTTP_PARSER = require "protocol.http.parser" +local PARSER_HTTP_REQUEST = HTTP_PARSER.PARSER_HTTP_REQUEST +local PARSER_HTTP_RESPONSE = HTTP_PARSER.PARSER_HTTP_RESPONSE +local RESPONSE_CHUNKED_PARSER = HTTP_PARSER.RESPONSE_CHUNKED_PARSER + +local HTTP_PROTOCOL = { + API = 1, + [1] = "API", + USE = 2, + [2] = "USE", + STATIC = 3, + [3] = "STATIC", + WS = 4, + [4] = "WS", + PARSER_HTTP_REQUEST = PARSER_HTTP_REQUEST, + PARSER_HTTP_RESPONSE = PARSER_HTTP_RESPONSE, + RESPONSE_CHUNKED_PARSER = RESPONSE_CHUNKED_PARSER, +} + +-- 以下为 HTTP Server 所需所用方法 +local function REQUEST_STATUCODE_RESPONSE(code) + return HTTP_CODE[code] or "attempt to Passed A Invaid Code to response message." +end + +local function REQUEST_MIME_RESPONSE(mime) + return MIME[mime] +end + +function HTTP_PROTOCOL.FILEMIME(mime) + return MIME[mime] +end + +-- -- 路由注册 +HTTP_PROTOCOL.ROUTE_REGISTERY = ROUTE_REGISTERY + +-- -- 路由查找 +HTTP_PROTOCOL.ROUTE_FIND = ROUTE_FIND + +local function HTTP_DATE() + return DATE("Date: %a, %d %b %Y %X GMT") + -- return os.date("Date: %a, %d %b %Y %X GMT") +end + +local function HTTP_EXPIRES(timestamp) + return DATE("Expires: %a, %d %b %Y %X GMT", timestamp) + -- return os.date("Date: %a, %d %b %Y %X GMT") +end + +local function PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEADER) + local content = {} + if METHOD == "GET" then + local spl_pos = find(PATH, '%?') + if spl_pos and spl_pos < #PATH then + content['args'] = form_urlencode(PATH) + end + elseif METHOD == "POST" or METHOD == "PUT" then + local body_len = toint(HEADER['Content-Length']) or toint(HEADER['Content-length']) + if body_len and body_len > 0 then + local BODY = '' + local RECV_BODY = true + local CRLF_START, CRLF_END = find(buffer, CRLF2) + if #buffer > CRLF_END then + BODY = split(buffer, CRLF_END + 1, -1) + if #BODY == body_len then + RECV_BODY = false + end + end + if RECV_BODY then + local buffers = {BODY} + while 1 do + local buf = sock:recv(1024) + if not buf then + return + end + buffers[#buffers+1] = buf + local buffer = concat(buffers) + if #buffer >= (max_body_size or 1024 * 1024) then + return nil, 413 + end + if #buffer == body_len then + BODY = buffer + break + end + end + end + local FILE_ENCODE = 'multipart/form-data' + local XML_ENCODE_1 = 'text/xml' + local XML_ENCODE_2 = 'application/xml' + local JSON_ENCODE = 'application/json' + local URL_ENCODE = 'application/x-www-form-urlencoded' + local format = match(HEADER['Content-type'] or HEADER['Content-Type'] or '', '(.-/[^;]*)') + if format == FILE_ENCODE then + local BOUNDARY = match(HEADER['Content-Type'], '^.+=[%-]*(.+)') + if BOUNDARY and BOUNDARY ~= '' then + local typ, body = form_multipart(BODY, BOUNDARY) + if typ == FILE_TYPE then + content['files'] = body + elseif typ == ARGS_TYPE then + content['args'] = {} + for _, args in ipairs(body) do + content['args'][args[1]] = args[2] + end + end + end + elseif format == JSON_ENCODE then + content['json'] = true + content['body'] = BODY + elseif format == XML_ENCODE_1 or format == XML_ENCODE_2 then + content['xml'] = true + content['body'] = BODY + elseif format == URL_ENCODE then + content['args'] = form_urlencode(BODY) + else + content['body'] = BODY + end + end + elseif METHOD == "HEAD" or METHOD == "OPTIONS" then + return true, nil + else + -- 暂未支持其他方法 + return + end + return true, content +end + +local function X_Forwarded_FORMAT(tab) + local ip_list + if tab and type(tab) == 'string' then + for ip in splite(tab, '([^ ,;]+)') do + if not ip_list then + ip_list = {ip} + else + if ip ~= ip_list[#ip_list] then + ip_list[#ip_list+1] = ip + end + end + end + return concat(ip_list, ' -> ') + end + return tab +end +-- 一些错误返回 +local function ERROR_RESPONSE(http, code, path, ip, forword, method, speed) + http:tolog(code, path, ip, X_Forwarded_FORMAT(forword) or ip, method, speed) + return concat({concat({ + REQUEST_STATUCODE_RESPONSE(code), + HTTP_DATE(), + 'Origin: *', + 'Allow: GET, POST, PUT, HEAD, OPTIONS', + 'Connection: close', + 'Content-length: 0', + 'Server: ' .. (http.__server or SERVER), + }, CRLF), CRLF2}) +end + +-- WebSocket +local function Switch_Protocol(http, cls, sock, header, method, version, path, ip, start_time) + if method ~= 'GET' then + return sock:send(ERROR_RESPONSE(http, 400, path, ip, header['X-Forwarded-For'] or ip, method, now() - start_time)) + end + if version ~= 1.1 then + return sock:send(ERROR_RESPONSE(http, 400, path, ip, header['X-Forwarded-For'] or ip, method, now() - start_time)) + end + if not header['Upgrade'] or lower(header['Upgrade']) ~= 'websocket' then + return sock:send(ERROR_RESPONSE(http, 401, path, ip, header['X-Forwarded-For'] or ip, method, now() - start_time)) + end + if header['Sec-WebSocket-Version'] ~= '13' then + return sock:send(ERROR_RESPONSE(http, 403, path, ip, header['X-Forwarded-For'] or ip, method, now() - start_time)) + end + local sec_key = header['Sec-WebSocket-Key'] + if not sec_key or sec_key == '' then + return sock:send(ERROR_RESPONSE(http, 505, path, ip, header['X-Forwarded-For'] or ip, method, now() - start_time)) + end + local response = { + REQUEST_STATUCODE_RESPONSE(101), + HTTP_DATE(), + 'Server: '..(http.__server or SERVER), + 'Upgrade: WebSocket', + 'Connection: Upgrade', + 'Sec-WebSocket-Accept: '..base64encode(sha1(sec_key..'258EAFA5-E914-47DA-95CA-C5AB0DC85B11')) + } + local protocol = header['Sec-Websocket-Protocol'] + if protocol then -- 仅支持协议回传 + response[#response+1] = "Sec-Websocket-Protocol: "..tostring(protocol) + end + http:tolog(101, path, header['X-Real-IP'] or ip, X_Forwarded_FORMAT(header['X-Forwarded-For'] or ip), method, now() - start_time) + local ok = sock:send(concat(response, CRLF)..CRLF2) + if not ok then + return + end + return wsserver:new({cls = cls, sock = sock}):start() +end + +local function send_header (sock, header) + header[#header+1] = CRLF + return sock:send(concat(header, CRLF)) +end + +local function send_body (sock, body, filepath) + if not body then + return sock:sendfile(filepath, 65535) + end + return sock:send(body) +end + +function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) + local buffers = {} + local ttl = http.ttl + local server = http.__server + local timeout = http.__timeout or 30 + local cookie = http.__cookie + local cookie_secure = http.__cookie_secure + local before_func = http._before_func + local max_path_size = http.__max_path_size + local max_header_size = http.__max_header_size + local max_body_size = http.__max_body_size + secCookie(cookie_secure) -- 如果需要 + local sock = tcp:new():set_fd(fd):timeout(timeout) + while 1 do + local buf = sock:recv(1024) + if not buf then + return sock:close() + end + buffers[#buffers+1] = buf + local buffer = concat(buffers) + local CRLF_START, CRLF_END = find(buffer, RE_CRLF2) + if CRLF_START and CRLF_END then + local start = now() + -- 协议有问题返回400 + local METHOD, PATH, VERSION, HEADER = PARSER_HTTP_REQUEST(buffer) + if not METHOD or not PATH or not VERSION then + sock:send(ERROR_RESPONSE(http, 400, PATH, HEADER and HEADER['X-Real-IP'] or ipaddr, HEADER and HEADER['X-Forwarded-For'] or ipaddr, METHOD or "GET", now() - start)) + return sock:close() + end + -- 超过自定义最大PATH长度限制 + if PATH and #PATH > (max_path_size or 1024) then + sock:send(ERROR_RESPONSE(http, 414, PATH, HEADER and HEADER['X-Real-IP'] or ipaddr, HEADER and HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) + return sock:close() + end + -- 没有HEADER返回400 + if not HEADER or not next(HEADER) then + sock:send(ERROR_RESPONSE(http, 400, PATH, HEADER and HEADER['X-Real-IP'] or ipaddr, HEADER and HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) + return sock:close() + end + -- 超过自定义最大HEADER长度限制 + if #buffer - CRLF_START > (max_header_size or 65535) then + sock:send(ERROR_RESPONSE(http, 431, PATH, HEADER and HEADER['X-Real-IP'] or ipaddr, HEADER and HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) + return sock:close() + end + -- 这里根据PATH先查找路由, 如果没有直接返回404. + local cls, typ = ROUTE_FIND(METHOD, PATH) + if not cls or not typ then + sock:send(ERROR_RESPONSE(http, 404, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) + return sock:close() + end + -- 根据请求方法进行解析, 解析失败返回501 + local ok, content = PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEADER) + if not ok then + if content == 413 then + sock:send(ERROR_RESPONSE(http, 413, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) + return sock:close() + end + sock:send(ERROR_RESPONSE(http, 501, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) + return sock:close() + end + if not content then -- 没有 Content则返回200; + http:tolog(200, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, now() - start) + sock:send(concat({REQUEST_STATUCODE_RESPONSE(200), HTTP_DATE(), + 'Origin: *', + 'Allow: GET, POST, PUT, HEAD, OPTIONS', + 'Access-Control-Allow-Origin: *', + 'Access-Control-Allow-Headers: *', + 'Access-Control-Allow-Methods: GET, POST, PUT, HEAD, OPTIONS', + 'Access-Control-Allow-Credentials: true', + 'Access-Control-Max-Age: 86400', + 'Connection: keep-alive', + 'Server: ' .. (server or SERVER), + }, CRLF)..CRLF2) + return sock:close() + end + content['ROUTE'] = HTTP_PROTOCOL[typ] + content['method'], content['path'], content['headers'] = METHOD, PATH, HEADER + -- before 函数只影响接口与view + if before_func and (typ == HTTP_PROTOCOL.API or typ == HTTP_PROTOCOL.USE) then + local ok, code, data = pcall(before_func, content) + if not ok then -- before 函数执行出错 + Log:ERROR(code) + sock:send(ERROR_RESPONSE(http, 500, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) + return sock:close() + end + if code then + if type(code) == "number" then + if code < 200 or code > 500 then + Log:ERROR("before function: Illegal return value") + sock:send(ERROR_RESPONSE(http, 500, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) + return sock:close() + elseif code == 301 or code == 302 then + http:tolog(code, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, now() - start) + sock:send(concat({ + REQUEST_STATUCODE_RESPONSE(code), HTTP_DATE(), + 'Connection: close', + 'Server: ' .. (server or SERVER), + 'Location: ' .. (data or "https://github.com/CandyMi/core_framework"), + }, CRLF)..CRLF2) + return sock:close() + elseif code ~= 200 then + if data then + if type(data) == 'string' and data ~= '' then + http:tolog(code, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, now() - start) + sock:send(concat({concat({ + REQUEST_STATUCODE_RESPONSE(code), HTTP_DATE(), + 'Origin: *', + 'Allow: GET, POST, PUT, HEAD, OPTIONS', + 'Server: ' .. (server or SERVER), + 'Connection: close', + 'Content-Type: ' .. REQUEST_MIME_RESPONSE('html'), + 'Content-Length: '..tostring(#data), + }, CRLF), CRLF2, data})) + return sock:close() + end + end + sock:send(ERROR_RESPONSE(http, code, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) + return sock:close() + end + else + sock:send(ERROR_RESPONSE(http, 401, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) + return sock:close() + end + else + sock:send(ERROR_RESPONSE(http, 401, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) + return sock:close() + end + end + + local header = { } + local ok, body, body_len, filepath, static, statucode + + if typ == HTTP_PROTOCOL.API or typ == HTTP_PROTOCOL.USE then + -- 如果httpd开启了记录Cookie字段, 则每次尝试是否deCookie + if cookie and typ == HTTP_PROTOCOL.USE then + deCookie(content['headers']["Cookie"]) + end + if type(cls) == "table" then + local method = cls[lower(METHOD)] + if not method or type(method) ~= 'function' then -- 注册的路由未实现这个方法 + sock:send(ERROR_RESPONSE(http, 405, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) + return sock:close() + end + local c = cls:new(content) + ok, body = pcall(method, c) + else + ok, body = pcall(cls, content) + end + -- 如果httpd开启了记录Cookie字段, 则每次尝试是否需要seCookie + if cookie and typ == HTTP_PROTOCOL.USE then + local Cookies = seCookie() + for _, Cookie in ipairs(Cookies) do + header[#header+1] = Cookie + end + clCookie() + end + if not ok then + Log:ERROR(body or "empty response.") + sock:send(ERROR_RESPONSE(http, 500, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) + return sock:close() + end + statucode = 200 + insert(header, 1, REQUEST_STATUCODE_RESPONSE(statucode)) + elseif typ == HTTP_PROTOCOL.WS then + local ok, msg = pcall(Switch_Protocol, http, cls, sock, HEADER, METHOD, VERSION, PATH, HEADER['X-Real-IP'] or ipaddr, start) + if not ok then + Log:ERROR(msg) + end + return sock:close() + else + local file_type + local path = PATH + local pos, _ = find(PATH, '%?') + if pos then + path = split(PATH, 1, pos - 1) + end + body_len, filepath, file_type = cls(path) + if not body_len then + statucode = 404 + sock:send(ERROR_RESPONSE(http, statucode, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) + return sock:close() + end + statucode = 200 + header[#header+1] = REQUEST_STATUCODE_RESPONSE(statucode) + local conten_type = REQUEST_MIME_RESPONSE(lower(file_type or '')) + if not conten_type then + header[#header+1] = 'Content-Disposition: attachment' -- 确保浏览器提示需要下载 + static = fmt('Content-Type: %s', 'application/octet-stream') + else + static = fmt('Content-Type: %s', conten_type..'; charset=utf-8') + end + -- 如果是静态文件, 增加默认跨域访问支持 + header[#header+1] = "Access-Control-Allow-Origin: *" + end + header[#header+1] = HTTP_DATE() + header[#header+1] = 'Origin: *' + header[#header+1] = 'Allow: GET, POST, PUT, HEAD, OPTIONS' + header[#header+1] = 'Server: ' .. (server or SERVER) + local Connection = 'Connection: keep-alive' + if not HEADER['Connection'] or lower(HEADER['Connection']) == 'close' then + Connection = 'Connection: close' + end + header[#header+1] = Connection + if Connection == 'Connection: keep-alive' then + header[#header+1] = "Keep-Alive: timeout="..timeout + end + if typ == HTTP_PROTOCOL.API or typ == HTTP_PROTOCOL.USE then + if typ == HTTP_PROTOCOL.API then + header[#header+1] = 'Content-Type: ' .. REQUEST_MIME_RESPONSE('json') .. "; charset=utf-8" + else + header[#header+1] = 'Content-Type: ' .. REQUEST_MIME_RESPONSE('html') .. "; charset=utf-8" + end + if type(body) ~= 'string' or body == '' then + Log:ERROR("Response Error ["..(split(PATH , 1, (find(PATH, '?') or 0 ) - 1)).."]: response must be a string and not empty.") + sock:send(ERROR_RESPONSE(http, 500, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) + return sock:close() + end + header[#header+1] = 'Content-Length: '.. #body + header[#header+1] = 'Cache-Control: no-cache, no-store, must-revalidate' + header[#header+1] = 'Cache-Control: no-cache' + else + if ttl then + header[#header+1] = HTTP_EXPIRES(time() + ttl) + end + if body_len then + header[#header+1] = 'Content-Length: '.. body_len + end + header[#header+1] = static + end + -- 不计算数据传输时间, 仅计算实际回调处理所用时间. + http:tolog(statucode, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, now() - start) + -- 根据实际情况分块发送 + local ok = send_header(sock, header) and send_body(sock, body, filepath) or false + if not ok then + return sock:close() + end + if statucode ~= 200 or Connection ~= 'Connection: keep-alive' then + return sock:close() + end + buffers = {} + end + if #buffers ~= 0 and #buffer > (max_header_size or 65535) then + sock:send(ERROR_RESPONSE(http, 431, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) + return sock:close() + end + end +end + +return HTTP_PROTOCOL diff --git a/lualib/protocol/http/mime.lua b/lualib/protocol/http/mime.lua new file mode 100644 index 00000000..956efc0f --- /dev/null +++ b/lualib/protocol/http/mime.lua @@ -0,0 +1,91 @@ +return { + -- 文本格式 + ['xml'] = 'application/xml', + ['htm'] = 'text/html', + ['html'] = 'text/html', + ['shtml'] = 'text/html', + ['xhtml'] = 'application/xhtml+xml', + ['txt'] = 'text/plain', + ['css'] = 'text/css', + ['json'] = 'application/json', + ['rtf'] = 'application/rtf', + ['ogx'] = 'application/ogg', + -- 图片格式 + ['bmp'] = 'image/bmp', + ['png'] = 'image/png', + ['gif'] = 'image/gif', + ['jpeg'] = 'image/jpeg', + ['jpg'] = 'image/jpeg', + ['ico'] = 'image/x-icon', + ['tif'] = 'image/tiff', + ['tiff'] = 'image/tiff', + ['svg'] = 'image/svg+xml', + -- 音频 + ['au'] = 'audio/basic', + ['wav'] = 'audio/wav', + ['acc'] = 'audio/aac', + ['mp3'] = 'audio/mpeg', + ['oga'] = 'audio/ogg', + -- 视频 + ['avi'] = 'video/x-msvideo', + ['mpa'] = 'video/mpeg', + ['mpe'] = 'video/mpeg', + ['mp2'] = 'video/mpeg', + ['mpe'] = 'video/mpeg', + ['mpeg'] = 'video/mpeg', + ['mp4'] = 'audio/mp4', + ['qt'] = 'video/mpeg', + ['mov'] = 'video/mpeg', + ['webm'] = 'video/webm', + ['flv'] = 'video/x-flv', + ['m4v'] = 'video/x-m4v', + ['3gp'] = 'video/3gpp', + ['3gpp'] = 'video/3gpp', + ['ts'] = 'video/mp2t', + ['ogv'] = 'video/ogg', + -- 文档 + ['csv'] = 'text/csv', + ['dot'] = 'application/msword', + ['doc'] = 'application/msword', + ['mdb'] = 'application/x-msaccess', + ['xls'] = 'application/vnd.ms-excel', + ['ppt'] = 'application/vnd.ms-powerpoint', + ['chm'] = 'application/vnd.ms-htmlhelp', + ['xlsx'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + ['docx'] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + ['pptx'] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + ['pdf'] = 'application/pdf', + -- 密匙 + ['der'] = 'application/x-x509-ca-cert', + ['cer'] = 'application/x-x509-ca-cert', + ['pem'] = 'application/x-x509-ca-cert', + ['crt'] = 'application/x-x509-ca-cert', + ['p10'] = 'application/pkcs10', + ['p12'] = 'application/x-pkcs12', + ['pfx'] = 'application/x-pkcs12', + ['p7c'] = 'application/x-pkcs7-mime', + ['p7m'] = 'application/x-pkcs7-mime', + ['p7b'] = 'application/x-pkcs7-certificates', + ['spc'] = 'application/x-pkcs7-certificates', + ['p7r'] = 'application/x-pkcs7-certreqresp', + ['p7s'] = 'application/x-pkcs7-signature', + -- 压缩文件 + ['7z'] = 'application/x-7z-compressed', + ['zip'] = 'application/zip', + ['gz'] = 'application/x-gzip', + ['tar'] = 'application/x-tar', + ['gtar'] = 'application/x-gtar', + ['tgz'] = 'application/x-compressed', + ['rar'] = 'application/x-rar-compressed', + -- 源文件 + ['c'] = 'text/plain', + ['cxx'] = 'text/plain', + ['cpp'] = 'text/plain', + ['h'] = 'text/plain', + ['hpp'] = 'text/plain', + ['py'] = 'text/plain', + ['cs'] = 'text/plain', + ['lua'] = 'text/plain', + ['js'] = 'application/javascript', + -- TODO +} \ No newline at end of file diff --git a/lualib/protocol/http/parser.lua b/lualib/protocol/http/parser.lua new file mode 100644 index 00000000..589b8caa --- /dev/null +++ b/lualib/protocol/http/parser.lua @@ -0,0 +1,34 @@ +local httpparser = require "httpparser" +local PARSER_HTTP_REQUEST = httpparser.parser_http_request +local PARSER_HTTP_RESPONSE = httpparser.parser_http_response +local RESPONSE_CHUNKED_PARSER = httpparser.parser_response_chunked + +local http_parser = {} + +function http_parser.PARSER_HTTP_REQUEST (buffer) + local ok, method, path, version, header = pcall(PARSER_HTTP_REQUEST, buffer) + if not ok then + return nil + end + return method, path, version, header +end + +-- 解析http回应 +function http_parser.PARSER_HTTP_RESPONSE (buffer) + local ok, version, code, status, header = pcall(PARSER_HTTP_RESPONSE, buffer) + if not ok then + return nil + end + return version, code, status, header +end + +-- 解析回应chunked +function http_parser.RESPONSE_CHUNKED_PARSER (data) + local ok, data, pos = pcall(RESPONSE_CHUNKED_PARSER, data) + if not ok then + return nil, -1 + end + return data, pos +end + +return http_parser \ No newline at end of file From 3613ca66ef746c7f11ed196f11f1c6d32dc663b9 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 18 Oct 2019 10:43:52 +0800 Subject: [PATCH 391/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0SDK=E7=AE=80=E4=BB=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/cloud/README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 lualib/cloud/README.md diff --git a/lualib/cloud/README.md b/lualib/cloud/README.md new file mode 100644 index 00000000..4244bd74 --- /dev/null +++ b/lualib/cloud/README.md @@ -0,0 +1,21 @@ +# 云平台Lua SDK + + 框架提供一部分云平台服务调用的内置Lua版SDK. + +## 七牛 + + * 存储服务 + + * 短信服务 + + * 内容审核 + + * 直播服务 + +## 腾讯 + + * 位置服务 + +## paypal + + * 支付业务 \ No newline at end of file From 8cf1734669f69f3b4057d51474a6f6a7fe093cc2 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 18 Oct 2019 20:10:02 +0800 Subject: [PATCH 392/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0tencent.location?= =?UTF-8?q?=E7=9A=84sign=E5=85=BC=E5=AE=B9=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/cloud/tencent/location.lua | 120 ++++++++++++------------------ 1 file changed, 48 insertions(+), 72 deletions(-) diff --git a/lualib/cloud/tencent/location.lua b/lualib/cloud/tencent/location.lua index fb4898c5..27843f15 100644 --- a/lualib/cloud/tencent/location.lua +++ b/lualib/cloud/tencent/location.lua @@ -2,6 +2,8 @@ local httpc = require "httpc" local crypt = require "crypt" local type = type +local pairs = pairs +local ipairs = ipairs local assert = assert --[[ @@ -10,6 +12,23 @@ local assert = assert 所有接口数据均返回原生http code与json数据, 请开发者自行进行接口判断与json decode. ]] +local function sign(sn, path, opt) + local key_sorts = {} + for key, value in pairs(opt) do + key_sorts[#key_sorts+1] = key + end + table.sort(key_sorts) + local args = {} + local signs = {} + for index, key in ipairs(key_sorts) do + signs[#signs+1] = key .. '=' .. opt[key] + args[#args+1] = {key, opt[key]} + end + local sig = crypt.md5(path .. '?' .. table.concat(signs, "&") .. sn, true) + args[#args+1] = {"sig", sig} + return args +end + local Location = { __Version__ = 0.1 } -- IP定位 @@ -17,25 +36,14 @@ local Location = { __Version__ = 0.1 } ip : IP地址 ]] function Location.getIpLocation (accesskey, sn, ip) - return httpc.get("https://apis.map.qq.com/ws/location/v1/ip", nil, { - {"ip", ip}, - {"key", accesskey}, - {"sig", crypt.md5("/ws/location/v1/ip?" - .. "ip=" .. ip .. "&" .. "key=" .. accesskey - .. sn , true) - } - }) + return httpc.get("https://apis.map.qq.com/ws/location/v1/ip", nil, sign(sn, "/ws/location/v1/ip", { + ip = ip, key = accesskey + })) end -- 获取行政规划区列表 function Location.getDistrictList (accesskey, sn) - return httpc.get("https://apis.map.qq.com/ws/district/v1/list", nil, { - {"key", accesskey}, - {"sig", crypt.md5("/ws/district/v1/list?" - .. "key=" .. accesskey - .. sn , true) - } - }) + return httpc.get("https://apis.map.qq.com/ws/district/v1/list", nil, sign(sn, "/ws/district/v1/list", {key = accesskey})) end -- 获取指定行政规划区 @@ -43,14 +51,9 @@ end id: 父级行政区划ID,缺省时则返回最顶级行政区划 ]] function Location.getDistrictChildren (accesskey, sn, id) - return httpc.get("https://apis.map.qq.com/ws/district/v1/getchildren", nil, { - {"key", accesskey}, - {"id", id}, - {"sig", crypt.md5("/ws/district/v1/getchildren?" - .. "id=" .. id .. "&" .. "key=" .. accesskey - .. sn , true) - } - }) + return httpc.get("https://apis.map.qq.com/ws/district/v1/getchildren", nil, sign(sn, "/ws/district/v1/getchildren", { + id = id, key = accesskey + })) end -- 关键词猜测(补全) @@ -63,35 +66,24 @@ end page_size: 每页返回数量 ]] function Location.searchSuggestion (accesskey, sn, keyword, region, region_fix, page_index, page_size) - return httpc.get("https://apis.map.qq.com/ws/place/v1/suggestion", nil, { - {"key", accesskey}, - {"keyword", keyword}, - {"page_index", page_index or 1}, - {"page_size", page_size or 10}, - {"region", region or '' }, - {"region_fix", region_fix or 1}, - {"sig", crypt.md5("/ws/place/v1/suggestion?" - .. "key=" .. accesskey .. "&" .. "keyword=" .. keyword .. "&" - .. "page_index=" .. (page_index or 1) .. "&" .. "page_size=" .. (page_size or 10) .. "&" - .. "region=" .. (region or '') .. "&" .. "region_fix=" .. (region_fix or 1) - .. sn , true) - } - }) + return httpc.get("https://apis.map.qq.com/ws/place/v1/suggestion", nil, sign(sn, "/ws/place/v1/suggestion", { + key = accesskey, keyword = keyword, + page_index = page_index or 1, + page_size = page_size or 10, + region = region or '', + region_fix = region_fix or 1, + })) end -function Location.searchPlace (accesskey, sn, keyword, boundary) - return httpc.get("https://apis.map.qq.com/ws/place/v1/search", nil, { - {"boundary", boundary}, - {"key", accesskey}, - {"keyword", keyword}, - {"page_index", page_index or 1}, - {"page_size", page_size or 10}, - {"sig", crypt.md5("/ws/place/v1/search?" - .. "boundary=" .. boundary .. "&" .. "key=" .. accesskey .. "&" .. "keyword=" .. keyword .. "&" - .. "page_index=" .. (page_index or 1) .. "&" .. "page_size=" .. (page_size or 10) .. "&" - .. sn , true) - } - }) +function Location.searchPlace (accesskey, sn, keyword, boundary, page_index, page_size, order) + return httpc.get("https://apis.map.qq.com/ws/place/v1/search", nil, sign(sn, "/ws/place/v1/search", { + key = accesskey, + keyword = keyword, + boundary = boundary, + page_index = page_index, + page_size = page_size, + order = order, + })) end -- 关键词搜索行政规划区 @@ -99,14 +91,9 @@ end keyword: 行政区关键词 ]] function Location.searchDistrict (accesskey, sn, keyword) - return httpc.get("https://apis.map.qq.com/ws/district/v1/search", nil, { - {"key", accesskey}, - {"keyword", keyword}, - {"sig", crypt.md5("/ws/district/v1/search?" - .. "key=" .. accesskey .. "&" .. "keyword=" .. keyword - .. sn , true) - } - }) + return httpc.get("https://apis.map.qq.com/ws/district/v1/search", nil, sign(sn, "/ws/district/v1/search", { + key = accesskey, keyword = keyword + })) end -- 距离计算(一对多) @@ -116,20 +103,9 @@ end to: 目的经纬度 ]] function Location.getDistance (accesskey, sn, opt) - assert(type(opt) == 'table', "invaild arguments") - assert(opt.from, "invaild current position.") - assert(opt.to, "invaild Destination position.") - assert(opt.mode == 'driving' or opt.mode == 'walking', "invaild mode. [driving | walking]") - return httpc.get("https://apis.map.qq.com/ws/distance/v1/", nil, { - {"from", opt.from}, - {"mode", opt.mode}, - {"key", accesskey}, - {"to", opt.to}, - {"sig", crypt.md5("/ws/distance/v1/?" - .. "from=" .. opt.from .. "&" .. "key=" .. accesskey .. "&" .. "mode=" .. opt.mode .. "&" .. "to=" .. opt.to - .. sn , true) - } - }) + return httpc.get("https://apis.map.qq.com/ws/distance/v1/", nil, sign(sn, "/ws/distance/v1/", { + key = accesskey, to = opt.to, from = opt.from, mode = opt.mode, + })) end return Location From 975467f04809c51d5e26d3ba4a679e2fa65fe074 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 18 Oct 2019 20:10:23 +0800 Subject: [PATCH 393/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dtest=5Flocation.lua?= =?UTF-8?q?=E7=9A=84=E8=AF=AD=E6=B3=95=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_location.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/test_location.lua b/script/test_location.lua index 1e6cc569..8f93d843 100644 --- a/script/test_location.lua +++ b/script/test_location.lua @@ -11,7 +11,7 @@ local sn = "Your_SN_Key" -- local code, ret = location.getIpLocation(accesskey, sn, '8.8.8.8') -- print(code, ret) --- local code, ret = location.getDistrictList(accesskey, sn,) +-- local code, ret = location.getDistrictList(accesskey, sn) -- print(code, ret) -- local code, ret = location.getDistrictChildren(accesskey, sn, 110000) From e933b8c31f54fff2f63603695b6da9f8dbc2a01a Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 18 Oct 2019 20:42:41 +0800 Subject: [PATCH 394/956] =?UTF-8?q?qiniu=E5=BA=93=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E8=BA=AB=E4=BB=BD=E8=AF=81=E8=AF=86=E5=88=AB=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/cloud/qiniu/ocr.lua | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 lualib/cloud/qiniu/ocr.lua diff --git a/lualib/cloud/qiniu/ocr.lua b/lualib/cloud/qiniu/ocr.lua new file mode 100644 index 00000000..7a66c3f3 --- /dev/null +++ b/lualib/cloud/qiniu/ocr.lua @@ -0,0 +1,15 @@ +local token = require "cloud.qiniu.token" +local httpc = require "httpc" +local json = require "json" + +local ocr = { __Version__ = 0.1, host = "ai.qiniuapi.com"} + +-- 身份证识别 +function ocr.idcard(AccessKey, SecretKey, uri) + local path = "/v1/ocr/idcard" + local body = json.encode { data = { uri = uri } } + local Authorization = token.newAuthorization (AccessKey, SecretKey, { method = "POST", path = path, host = ocr.host, body = body }) + return httpc.json("http://ai.qiniuapi.com/v1/ocr/idcard", { {"Authorization", Authorization} }, body) +end + +return ocr \ No newline at end of file From 5ad8bda605888b857844311f45477a729f66ac06 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 18 Oct 2019 20:45:59 +0800 Subject: [PATCH 395/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0test=5Fqiniu.lua?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_qiniu.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/script/test_qiniu.lua b/script/test_qiniu.lua index 465795f5..75cd6f36 100644 --- a/script/test_qiniu.lua +++ b/script/test_qiniu.lua @@ -1,6 +1,7 @@ local token = require "cloud.qiniu.token" local censor = require "cloud.qiniu.censor" local Stream = require "cloud.qiniu.Stream" +local ocr = require "cloud.qiniu.ocr" local sms = require "cloud.qiniu.sms" local oss = require "cloud.qiniu.oss" local LOG = require "logging" @@ -106,3 +107,8 @@ local SecretKey = 'Your_Real_SecretKey' -- -- local code, ret = censor.getVideoCensorResult(AccessKey, SecretKey, job_id) -- LOG:DEBUG(code, ret) +-- +-- -- *******身份证识别******** +-- +-- local code, ret = ocr.idcard(AccessKey, SecretKey, "http://5b0988e595225.cdn.sohucs.com/images/20190111/fe9b7bb1afac482c89963174b2feac14.jpeg") +-- LOG:DEBUG(code, ret) From f7670ce4ac7024f4cb60a81de8c8ee171808d1f0 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 19 Oct 2019 02:29:39 +0800 Subject: [PATCH 396/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0cloud.translate?= =?UTF-8?q?=E7=BF=BB=E8=AF=91=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/cloud/translate/baidu.lua | 73 +++++++++++++++++++++++++++++++ lualib/cloud/translate/youdao.lua | 52 ++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 lualib/cloud/translate/baidu.lua create mode 100644 lualib/cloud/translate/youdao.lua diff --git a/lualib/cloud/translate/baidu.lua b/lualib/cloud/translate/baidu.lua new file mode 100644 index 00000000..acf915aa --- /dev/null +++ b/lualib/cloud/translate/baidu.lua @@ -0,0 +1,73 @@ +local httpc = require "httpc" +local crypt = require "crypt" +local md5 = crypt.md5 + +local type = type +local pairs = pairs +local time = os.time +local random = math.random +local concat = table.concat + + +local baidu = { __Version__ = 0.1, host = "https://fanyi-api.baidu.com/api/trans/vip/translate"} + +--[[ +文档地址 :http://api.fanyi.baidu.com/api/trans/product/apidoc + +语言简写 名称 +auto 自动检测 +zh 中文 +en 英语 +yue 粤语 +wyw 文言文 +jp 日语 +kor 韩语 +fra 法语 +spa 西班牙语 +th 泰语 +ara 阿拉伯语 +ru 俄语 +pt 葡萄牙语 +de 德语 +it 意大利语 +el 希腊语 +nl 荷兰语 +pl 波兰语 +bul 保加利亚语 +est 爱沙尼亚语 +dan 丹麦语 +fin 芬兰语 +cs 捷克语 +rom 罗马尼亚语 +slo 斯洛文尼亚语 +swe 瑞典语 +hu 匈牙利语 +cht 繁体中文 +vie 越南语 +]] + +-- 签名 +local function sign(app_id, app_key, salt, opt) + local sig = md5(concat({app_id, opt.q, salt, app_key}), true) + local args = {{"appid", app_id}, {"salt", salt}, {"sign", sig}} + for key, value in pairs(opt) do + args[#args+1] = {key, value} + end + return args +end + +-- 翻译接口 +function baidu.translate(app_id, app_key, opt) + assert(type(app_id) == 'string' and app_id ~= '', "invalid baidu translate app_id.") + assert(type(app_key) == 'string' and app_key ~= '', "invalid baidu translate app_key.") + assert(type(opt) == 'table' and type(opt.q) == 'string' and opt.q ~= '' , "invalid baidu translate opt.") + return httpc.post(baidu.host, nil, sign(app_id, app_key, random(1, time()), { + q = opt.q, -- 必填(其它选填) + to = opt.to or "en", + from = opt.from or "auto", + tts = opt.tts and 0 or 1, + dict = opt.dict and 0 or 1, + })) +end + +return baidu \ No newline at end of file diff --git a/lualib/cloud/translate/youdao.lua b/lualib/cloud/translate/youdao.lua new file mode 100644 index 00000000..22100616 --- /dev/null +++ b/lualib/cloud/translate/youdao.lua @@ -0,0 +1,52 @@ +local httpc = require "httpc" + +local type = type + +--[[ + 免费的有道词典接口, 采用https安全传输. +]] + +local youdao = { __Version__ = 0.1, host = "https://fanyi.youdao.com/translate"} + +-- 中文 >> 英语 +youdao.ZH_CN2EN = "ZH_CN2EN" +-- 中文 >> 日语 +youdao.ZH_CN2JA = "ZH_CN2JA" +-- 中文 >> 韩语 +youdao.ZH_CN2KR = "ZH_CN2KR" +-- 中文 >> 法语 +youdao.ZH_CN2FR = "ZH_CN2FR" +-- 中文 >> 俄语 +youdao.ZH_CN2RU = "ZH_CN2RU" +-- 中文 >> 西语 +youdao.ZH_CN2SP = "ZH_CN2SP" +-- 英语 >> 中文 +youdao.EN2ZH_CN = "EN2ZH_CN" +-- 日语 >> 中文 +youdao.JA2ZH_CN = "JA2ZH_CN" +-- 韩语 >> 中文 +youdao.KR2ZH_CN = "KR2ZH_CN" +-- 法语 >> 中文 +youdao.FR2ZH_CN = "FR2ZH_CN" +-- 俄语 >> 中文 +youdao.RU2ZH_CN = "RU2ZH_CN" +-- 西语 >> 中文 +youdao.SP2ZH_CN = "SP2ZH_CN" + +-- 转换 +function youdao.translate(translate_type, translate_text) + translate_type = youdao[translate_type] + if type(translate) ~= 'string' then + translate_type = "AUTO" + end + if type(translate_text) ~= 'string' or translate_text == '' then + return nil, "invalid translate_text." + end + return httpc.get(youdao.host, nil, { + {"doctype", "json"}, + {"i", translate_text}, + {"type", translate_type}, + }) +end + +return youdao \ No newline at end of file From 292ac152ad3a0dba719856f27398da8e66c8b03e Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 19 Oct 2019 02:30:40 +0800 Subject: [PATCH 397/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0translate=E7=BF=BB?= =?UTF-8?q?=E8=AF=91=E6=8E=A5=E5=8F=A3=E4=BD=BF=E7=94=A8=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_translate.lua | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 script/test_translate.lua diff --git a/script/test_translate.lua b/script/test_translate.lua new file mode 100644 index 00000000..96783217 --- /dev/null +++ b/script/test_translate.lua @@ -0,0 +1,33 @@ +local baidu = require "cloud.translate.baidu" +local youdao = require "cloud.translate.youdao" + +--[[ + 免费的有道词典接口, 采用https安全传输. + youdao.ZH_CN2EN -- 中文 >> 英语 + youdao.ZH_CN2JA -- 中文 >> 日语 + youdao.ZH_CN2KR -- 中文 >> 韩语 + youdao.ZH_CN2FR -- 中文 >> 法语 + youdao.ZH_CN2RU -- 中文 >> 俄语 + youdao.ZH_CN2SP -- 中文 >> 西语 + youdao.EN2ZH_CN -- 英语 >> 中文 + youdao.JA2ZH_CN -- 日语 >> 中文 + youdao.KR2ZH_CN -- 韩语 >> 中文 + youdao.FR2ZH_CN -- 法语 >> 中文 + youdao.RU2ZH_CN -- 俄语 >> 中文 + youdao.SP2ZH_CN -- 西语 >> 中文 +]] + +local query = "床前明月光, 疑是地上霜. 举头望明月, 低头思故乡." + +-- 免费 +local code, ret = youdao.translate(youdao.ZH_CN2EN, query) +print(code, ret) + +-- 数量收费 +local app_id, app_key = "your_app_id", "your_app_key" +local code, ret = baidu.translate(app_id, app_key, { + q = query, + to = "en", + from = "zh", +}) +print(code, ret) \ No newline at end of file From dec779f171809ec9f7d5b111fba63a6ebeb0dabf Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 19 Oct 2019 05:01:54 +0800 Subject: [PATCH 398/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0payjs=E8=81=9A?= =?UTF-8?q?=E5=90=88=E6=94=AF=E4=BB=98SDK,=E6=8F=90=E4=BE=9B=E6=97=A0?= =?UTF-8?q?=E7=BC=9D=E5=BF=AB=E9=80=9F=E6=8E=A5=E5=85=A5=E8=83=BD=E5=8A=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/cloud/payjs/README.md | 39 ++++++++++++++++++++++++++++++++++ lualib/cloud/payjs/cashier.lua | 11 ++++++++++ lualib/cloud/payjs/face.lua | 11 ++++++++++ lualib/cloud/payjs/js.lua | 11 ++++++++++ lualib/cloud/payjs/micro.lua | 11 ++++++++++ lualib/cloud/payjs/native.lua | 11 ++++++++++ lualib/cloud/payjs/order.lua | 32 ++++++++++++++++++++++++++++ lualib/cloud/payjs/sign.lua | 30 ++++++++++++++++++++++++++ lualib/cloud/payjs/utils.lua | 26 +++++++++++++++++++++++ 9 files changed, 182 insertions(+) create mode 100644 lualib/cloud/payjs/README.md create mode 100644 lualib/cloud/payjs/cashier.lua create mode 100644 lualib/cloud/payjs/face.lua create mode 100644 lualib/cloud/payjs/js.lua create mode 100644 lualib/cloud/payjs/micro.lua create mode 100644 lualib/cloud/payjs/native.lua create mode 100644 lualib/cloud/payjs/order.lua create mode 100644 lualib/cloud/payjs/sign.lua create mode 100644 lualib/cloud/payjs/utils.lua diff --git a/lualib/cloud/payjs/README.md b/lualib/cloud/payjs/README.md new file mode 100644 index 00000000..0b530f83 --- /dev/null +++ b/lualib/cloud/payjs/README.md @@ -0,0 +1,39 @@ +## PayJS聚合支付Lua SDK + + PayJS集合支付SDK, 进入[官网](https://payjs.cn/)注册. + + 使用详情请参考[社区文档](https://help.payjs.cn). + +## SDK提供的支持 + +### 支付 + + * 扫码支付 + + * 人脸支付 + + * 付款码支付 + + * JSPAI支付 + +## 订单相关 + + * 订单查询 + + * 订单关闭 + + * 订单撤销 + + * 订单退款 + +### 其它 + + * 查询银行编码 + + * 查询openid + + * 查询用户资料 + + * 查询商户资料 + +## 待补充 \ No newline at end of file diff --git a/lualib/cloud/payjs/cashier.lua b/lualib/cloud/payjs/cashier.lua new file mode 100644 index 00000000..624d1db8 --- /dev/null +++ b/lualib/cloud/payjs/cashier.lua @@ -0,0 +1,11 @@ +local sign = require "cloud.payjs.sign" +local httpc = require "httpc" + +local cashier = { __Version__ = 0.1, host = "https://payjs.cn/api/cashier" } + +-- 收银台支付接口 +function cashier.pay(mchid, key, opt) + return httpc.post(cashier.host, nil, sign(mchid, key, opt)) +end + +return cashier \ No newline at end of file diff --git a/lualib/cloud/payjs/face.lua b/lualib/cloud/payjs/face.lua new file mode 100644 index 00000000..77288572 --- /dev/null +++ b/lualib/cloud/payjs/face.lua @@ -0,0 +1,11 @@ +local sign = require "cloud.payjs.sign" +local httpc = require "httpc" + +local face = { __Version__ = 0.1, host = "https://payjs.cn/api/facepay" } + +-- 人脸支付接口 +function face.pay(mchid, key, opt) + return httpc.post(face.host, nil, sign(mchid, key, opt)) +end + +return face \ No newline at end of file diff --git a/lualib/cloud/payjs/js.lua b/lualib/cloud/payjs/js.lua new file mode 100644 index 00000000..e1202d98 --- /dev/null +++ b/lualib/cloud/payjs/js.lua @@ -0,0 +1,11 @@ +local sign = require "cloud.payjs.sign" +local httpc = require "httpc" + +local js = { __Version__ = 0.1, host = "https://payjs.cn/api/jsapi" } + +-- JSAPI支付接口 +function js.pay(mchid, key, opt) + return httpc.post(js.host, nil, sign(mchid, key, opt)) +end + +return js \ No newline at end of file diff --git a/lualib/cloud/payjs/micro.lua b/lualib/cloud/payjs/micro.lua new file mode 100644 index 00000000..95745f2f --- /dev/null +++ b/lualib/cloud/payjs/micro.lua @@ -0,0 +1,11 @@ +local sign = require "cloud.payjs.sign" +local httpc = require "httpc" + +local micro = { __Version__ = 0.1, host = "https://payjs.cn/api/micropay" } + +-- 付款码支付接口 +function micro.pay(mchid, key, opt) + return httpc.post(micro.host, nil, sign(mchid, key, opt)) +end + +return micro \ No newline at end of file diff --git a/lualib/cloud/payjs/native.lua b/lualib/cloud/payjs/native.lua new file mode 100644 index 00000000..9f6fe185 --- /dev/null +++ b/lualib/cloud/payjs/native.lua @@ -0,0 +1,11 @@ +local sign = require "cloud.payjs.sign" +local httpc = require "httpc" + +local native = { __Version__ = 0.1, host = "https://payjs.cn/api/native" } + +-- 扫码支付接口 +function native.pay(mchid, key, opt) + return httpc.post(native.host, nil, sign(mchid, key, opt)) +end + +return native \ No newline at end of file diff --git a/lualib/cloud/payjs/order.lua b/lualib/cloud/payjs/order.lua new file mode 100644 index 00000000..0b242b10 --- /dev/null +++ b/lualib/cloud/payjs/order.lua @@ -0,0 +1,32 @@ +local sign = require "cloud.payjs.sign" +local httpc = require "httpc" + +local order = { + __Version__ = 0.1, + check = "https://payjs.cn/api/check", + close = "https://payjs.cn/api/close", + reverse = "https://payjs.cn/api/reverse", + refund = "https://payjs.cn/api/refund", +} + +-- 订单查询 +function order.order_check(mchid, key, payjs_order_id) + return httpc.get(order.check, nil, sign(mchid, key, {payjs_order_id = payjs_order_id})) +end + +-- 订单关闭 +function order.order_close(mchid, key, payjs_order_id) + return httpc.post(order.close, nil, sign(mchid, key, {payjs_order_id = payjs_order_id})) +end + +-- 订单撤销 +function order.order_reverse(mchid, key, payjs_order_id) + return httpc.post(order.reverse, nil, sign(mchid, key, {payjs_order_id = payjs_order_id})) +end + +-- 退款 +function order.order_refund(mchid, key, payjs_order_id) + return httpc.post(order.refund, nil, sign(mchid, key, {payjs_order_id = payjs_order_id})) +end + +return order \ No newline at end of file diff --git a/lualib/cloud/payjs/sign.lua b/lualib/cloud/payjs/sign.lua new file mode 100644 index 00000000..374c3257 --- /dev/null +++ b/lualib/cloud/payjs/sign.lua @@ -0,0 +1,30 @@ +local crypt = require "crypt" +local md5 = crypt.md5 + +local pairs = pairs +local ipairs = ipairs +local sort = table.sort +local concat = table.concat + +local __Version__ = 0.1 + +return function (mchid, key, map) + map["mchid"] = mchid + local keys = {} + for key, value in pairs(map) do + if value ~= '' then + keys[#keys+1] = key + end + end + sort(keys) + local args = {} + local parms = {} + for index, key in ipairs(keys) do + local k, v = key, map[key] + args[#args+1] = {k, v} + parms[#parms+1] = k .. '=' .. v + end + parms[#parms+1] = "key=" .. key + args[#args+1] = {"sign", md5(concat(parms, "&"), true):upper()} + return args +end \ No newline at end of file diff --git a/lualib/cloud/payjs/utils.lua b/lualib/cloud/payjs/utils.lua new file mode 100644 index 00000000..6deff245 --- /dev/null +++ b/lualib/cloud/payjs/utils.lua @@ -0,0 +1,26 @@ +local sign = require "cloud.payjs.sign" +local httpc = require "httpc" + +local utils = { + __Version__ = 0.1, + info = "https://payjs.cn/api/info", + bank = "https://payjs.cn/api/bank", + openid = "https://payjs.cn/api/openid", +} + +-- 查询商户信息 +function utils.mch_info(mchid, key) + return httpc.get(utils.info, nil, sign(mchid, key, {})) +end + +-- 查询银行编码 +function utils.bank_code(mchid, key, bank) + return httpc.get(utils.bank, nil, sign(mchid, key, {bank = bank})) +end + +-- 获取用户 OPENID +function utils.mch_openid(mchid, key, callback_url) + return httpc.get(utils.openid, nil, sign(mchid, key, {callback_url = callback_url})) +end + +return utils \ No newline at end of file From a2ac167be10728c06e59873d94bb465c03b8bfa4 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 19 Oct 2019 12:31:16 +0800 Subject: [PATCH 399/956] =?UTF-8?q?=E4=B8=BApayjs=E5=BA=93=E5=8D=95?= =?UTF-8?q?=E7=8B=AC=E5=BC=80=E5=90=AF=E4=B8=80=E4=B8=AARepositories,=20?= =?UTF-8?q?=E4=B8=8D=E5=AD=98=E6=94=BE=E5=86=8Dcf=E6=A1=86=E6=9E=B6?= =?UTF-8?q?=E5=86=85=E9=83=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/cloud/payjs/README.md | 39 ---------------------------------- lualib/cloud/payjs/cashier.lua | 11 ---------- lualib/cloud/payjs/face.lua | 11 ---------- lualib/cloud/payjs/js.lua | 11 ---------- lualib/cloud/payjs/micro.lua | 11 ---------- lualib/cloud/payjs/native.lua | 11 ---------- lualib/cloud/payjs/order.lua | 32 ---------------------------- lualib/cloud/payjs/sign.lua | 30 -------------------------- lualib/cloud/payjs/utils.lua | 26 ----------------------- 9 files changed, 182 deletions(-) delete mode 100644 lualib/cloud/payjs/README.md delete mode 100644 lualib/cloud/payjs/cashier.lua delete mode 100644 lualib/cloud/payjs/face.lua delete mode 100644 lualib/cloud/payjs/js.lua delete mode 100644 lualib/cloud/payjs/micro.lua delete mode 100644 lualib/cloud/payjs/native.lua delete mode 100644 lualib/cloud/payjs/order.lua delete mode 100644 lualib/cloud/payjs/sign.lua delete mode 100644 lualib/cloud/payjs/utils.lua diff --git a/lualib/cloud/payjs/README.md b/lualib/cloud/payjs/README.md deleted file mode 100644 index 0b530f83..00000000 --- a/lualib/cloud/payjs/README.md +++ /dev/null @@ -1,39 +0,0 @@ -## PayJS聚合支付Lua SDK - - PayJS集合支付SDK, 进入[官网](https://payjs.cn/)注册. - - 使用详情请参考[社区文档](https://help.payjs.cn). - -## SDK提供的支持 - -### 支付 - - * 扫码支付 - - * 人脸支付 - - * 付款码支付 - - * JSPAI支付 - -## 订单相关 - - * 订单查询 - - * 订单关闭 - - * 订单撤销 - - * 订单退款 - -### 其它 - - * 查询银行编码 - - * 查询openid - - * 查询用户资料 - - * 查询商户资料 - -## 待补充 \ No newline at end of file diff --git a/lualib/cloud/payjs/cashier.lua b/lualib/cloud/payjs/cashier.lua deleted file mode 100644 index 624d1db8..00000000 --- a/lualib/cloud/payjs/cashier.lua +++ /dev/null @@ -1,11 +0,0 @@ -local sign = require "cloud.payjs.sign" -local httpc = require "httpc" - -local cashier = { __Version__ = 0.1, host = "https://payjs.cn/api/cashier" } - --- 收银台支付接口 -function cashier.pay(mchid, key, opt) - return httpc.post(cashier.host, nil, sign(mchid, key, opt)) -end - -return cashier \ No newline at end of file diff --git a/lualib/cloud/payjs/face.lua b/lualib/cloud/payjs/face.lua deleted file mode 100644 index 77288572..00000000 --- a/lualib/cloud/payjs/face.lua +++ /dev/null @@ -1,11 +0,0 @@ -local sign = require "cloud.payjs.sign" -local httpc = require "httpc" - -local face = { __Version__ = 0.1, host = "https://payjs.cn/api/facepay" } - --- 人脸支付接口 -function face.pay(mchid, key, opt) - return httpc.post(face.host, nil, sign(mchid, key, opt)) -end - -return face \ No newline at end of file diff --git a/lualib/cloud/payjs/js.lua b/lualib/cloud/payjs/js.lua deleted file mode 100644 index e1202d98..00000000 --- a/lualib/cloud/payjs/js.lua +++ /dev/null @@ -1,11 +0,0 @@ -local sign = require "cloud.payjs.sign" -local httpc = require "httpc" - -local js = { __Version__ = 0.1, host = "https://payjs.cn/api/jsapi" } - --- JSAPI支付接口 -function js.pay(mchid, key, opt) - return httpc.post(js.host, nil, sign(mchid, key, opt)) -end - -return js \ No newline at end of file diff --git a/lualib/cloud/payjs/micro.lua b/lualib/cloud/payjs/micro.lua deleted file mode 100644 index 95745f2f..00000000 --- a/lualib/cloud/payjs/micro.lua +++ /dev/null @@ -1,11 +0,0 @@ -local sign = require "cloud.payjs.sign" -local httpc = require "httpc" - -local micro = { __Version__ = 0.1, host = "https://payjs.cn/api/micropay" } - --- 付款码支付接口 -function micro.pay(mchid, key, opt) - return httpc.post(micro.host, nil, sign(mchid, key, opt)) -end - -return micro \ No newline at end of file diff --git a/lualib/cloud/payjs/native.lua b/lualib/cloud/payjs/native.lua deleted file mode 100644 index 9f6fe185..00000000 --- a/lualib/cloud/payjs/native.lua +++ /dev/null @@ -1,11 +0,0 @@ -local sign = require "cloud.payjs.sign" -local httpc = require "httpc" - -local native = { __Version__ = 0.1, host = "https://payjs.cn/api/native" } - --- 扫码支付接口 -function native.pay(mchid, key, opt) - return httpc.post(native.host, nil, sign(mchid, key, opt)) -end - -return native \ No newline at end of file diff --git a/lualib/cloud/payjs/order.lua b/lualib/cloud/payjs/order.lua deleted file mode 100644 index 0b242b10..00000000 --- a/lualib/cloud/payjs/order.lua +++ /dev/null @@ -1,32 +0,0 @@ -local sign = require "cloud.payjs.sign" -local httpc = require "httpc" - -local order = { - __Version__ = 0.1, - check = "https://payjs.cn/api/check", - close = "https://payjs.cn/api/close", - reverse = "https://payjs.cn/api/reverse", - refund = "https://payjs.cn/api/refund", -} - --- 订单查询 -function order.order_check(mchid, key, payjs_order_id) - return httpc.get(order.check, nil, sign(mchid, key, {payjs_order_id = payjs_order_id})) -end - --- 订单关闭 -function order.order_close(mchid, key, payjs_order_id) - return httpc.post(order.close, nil, sign(mchid, key, {payjs_order_id = payjs_order_id})) -end - --- 订单撤销 -function order.order_reverse(mchid, key, payjs_order_id) - return httpc.post(order.reverse, nil, sign(mchid, key, {payjs_order_id = payjs_order_id})) -end - --- 退款 -function order.order_refund(mchid, key, payjs_order_id) - return httpc.post(order.refund, nil, sign(mchid, key, {payjs_order_id = payjs_order_id})) -end - -return order \ No newline at end of file diff --git a/lualib/cloud/payjs/sign.lua b/lualib/cloud/payjs/sign.lua deleted file mode 100644 index 374c3257..00000000 --- a/lualib/cloud/payjs/sign.lua +++ /dev/null @@ -1,30 +0,0 @@ -local crypt = require "crypt" -local md5 = crypt.md5 - -local pairs = pairs -local ipairs = ipairs -local sort = table.sort -local concat = table.concat - -local __Version__ = 0.1 - -return function (mchid, key, map) - map["mchid"] = mchid - local keys = {} - for key, value in pairs(map) do - if value ~= '' then - keys[#keys+1] = key - end - end - sort(keys) - local args = {} - local parms = {} - for index, key in ipairs(keys) do - local k, v = key, map[key] - args[#args+1] = {k, v} - parms[#parms+1] = k .. '=' .. v - end - parms[#parms+1] = "key=" .. key - args[#args+1] = {"sign", md5(concat(parms, "&"), true):upper()} - return args -end \ No newline at end of file diff --git a/lualib/cloud/payjs/utils.lua b/lualib/cloud/payjs/utils.lua deleted file mode 100644 index 6deff245..00000000 --- a/lualib/cloud/payjs/utils.lua +++ /dev/null @@ -1,26 +0,0 @@ -local sign = require "cloud.payjs.sign" -local httpc = require "httpc" - -local utils = { - __Version__ = 0.1, - info = "https://payjs.cn/api/info", - bank = "https://payjs.cn/api/bank", - openid = "https://payjs.cn/api/openid", -} - --- 查询商户信息 -function utils.mch_info(mchid, key) - return httpc.get(utils.info, nil, sign(mchid, key, {})) -end - --- 查询银行编码 -function utils.bank_code(mchid, key, bank) - return httpc.get(utils.bank, nil, sign(mchid, key, {bank = bank})) -end - --- 获取用户 OPENID -function utils.mch_openid(mchid, key, callback_url) - return httpc.get(utils.openid, nil, sign(mchid, key, {callback_url = callback_url})) -end - -return utils \ No newline at end of file From d06c67945c6d5bd6b43307b1a32d8edd4316f30b Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 19 Oct 2019 15:44:43 +0800 Subject: [PATCH 400/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/cloud/README.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lualib/cloud/README.md b/lualib/cloud/README.md index 4244bd74..c3aa7152 100644 --- a/lualib/cloud/README.md +++ b/lualib/cloud/README.md @@ -2,7 +2,7 @@ 框架提供一部分云平台服务调用的内置Lua版SDK. -## 七牛 +## 七牛 SDK * 存储服务 @@ -12,10 +12,16 @@ * 直播服务 -## 腾讯 +## 腾讯 SDK * 位置服务 -## paypal +## paypal SDK - * 支付业务 \ No newline at end of file + * 支付业务 + +## 翻译SDK + + * 百度翻译 + + * 有道翻译 \ No newline at end of file From 854fd2ef3ff38bef29d59e19506364fe4b935626 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 19 Oct 2019 15:54:59 +0800 Subject: [PATCH 401/956] =?UTF-8?q?=E6=B8=85=E7=90=86=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E5=86=97=E4=BD=99=E7=9A=84=E6=B3=A8=E9=87=8A=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/DB/init.lua | 95 +++++++++++++++++++------------------------ lualib/crypt/init.lua | 70 ------------------------------- 2 files changed, 42 insertions(+), 123 deletions(-) diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index c28c572d..ee8059cb 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -21,47 +21,41 @@ local select = select local tostring = tostring local tonumber = tonumber -local rep = string.rep -local find = string.find local fmt = string.format -local lower = string.lower -local upper = string.upper -local match = string.match local insert = table.insert local remove = table.remove local concat = table.concat -local unpack = table.unpack -- 空闲连接时间 local WAIT_TIMEOUT = 31536000 -- 数据库连接创建函数 local function DB_CREATE (opt) - local times = 1 - local db - while 1 do - db = mysql:new() - db:set_timeout(3) - local connect, err = db:connect(opt) - if connect then - break - end - Log:WARN('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接") - db:close() - times = times + 1 - timer.sleep(3) - end - if not opt.INITIALIZATION then -- 设置连接超时时间 - db:query(fmt('SET GLOBAL wait_timeout=%s', WAIT_TIMEOUT)) - db:query(fmt('SET GLOBAL interactive_timeout=%s', WAIT_TIMEOUT)) - end - if opt.stmts then - for rkey, stmt in pairs(opt.stmts) do - assert(db:query(stmt), "["..stmt.."] 预编译失败.") + local times = 1 + local db + while 1 do + db = mysql:new() + db:set_timeout(3) + local connect, err = db:connect(opt) + if connect then + break end + Log:WARN('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接") + db:close() + times = times + 1 + timer.sleep(3) + end + if not opt.INITIALIZATION then -- 设置连接超时时间 + db:query(fmt('SET GLOBAL wait_timeout=%s', WAIT_TIMEOUT)) + db:query(fmt('SET GLOBAL interactive_timeout=%s', WAIT_TIMEOUT)) + end + if opt.stmts then + for rkey, stmt in pairs(opt.stmts) do + assert(db:query(stmt), "["..stmt.."] 预编译失败.") end - return db + end + return db end local function add_wait(self, co) @@ -69,7 +63,7 @@ local function add_wait(self, co) end local function pop_wait(self) - return remove(self.co_pool) + return remove(self.co_pool) end local function add_db(self, db) @@ -78,15 +72,15 @@ end -- 负责创建连接/加入等待队列 local function pop_db(self) - if #self.db_pool > 0 then - return remove(self.db_pool) - end - if self.current < self.max then - self.current = self.current + 1 - return DB_CREATE(self) - end - add_wait(self, co_self()) - return co_wait() + if #self.db_pool > 0 then + return remove(self.db_pool) + end + if self.current < self.max then + self.current = self.current + 1 + return DB_CREATE(self) + end + add_wait(self, co_self()) + return co_wait() end local DB = class("DB") @@ -158,33 +152,28 @@ function DB:execute (rkey, ...) end arg_keys[#arg_keys+1] = key req1[#req1+1] = concat({key, "=", "'", value, "'"}) - -- Log:DEBUG(key, value) end - -- Log:DEBUG(stmt) - -- Log:DEBUG(concat({"SET ", concat(req1, ", "), ";", " EXECUTE ", rkey, " USING ", concat(arg_keys, ", "), ";"})) return self:query(concat({"SET ", concat(req1, ", "), ";", " EXECUTE ", rkey, " USING ", concat(arg_keys, ", "), ";"})) end -- 原始查询语句 function DB:query(query) if not self.INITIALIZATION then - return nil, "DB尚未初始化" + return nil, "DB尚未初始化" end assert(type(query) == 'string' and query ~= '' , "原始SQL类型错误(query):"..tostring(query)) - -- Log:DEBUG(query) local db, ret, err while 1 do - db = pop_db(self) - if db then - ret, err = db:query(query) - if db.state then - break - end - -- Log:ERROR(err) - db:close() - self.current = self.current - 1 - db, ret, err = nil, nil, nil + db = pop_db(self) + if db then + ret, err = db:query(query) + if db.state then + break end + db:close() + self.current = self.current - 1 + db, ret, err = nil, nil, nil + end end local co = pop_wait(self) if co then diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index 8475c5c1..7a683f4e 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -49,11 +49,6 @@ local crypt = {} function crypt.md5(str, hex) local hash = md5(str) if hash and hex then - -- local tab = new_tab(#hash, 0) - -- for i = 1, #hash do - -- tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) - -- end - -- return concat(tab) return hexencode(hash) end return hash @@ -62,11 +57,6 @@ end function crypt.hmac_md5 (key, text, hex) local hash = hmac_md5(key, text) if hash and hex then - -- local tab = new_tab(#hash, 0) - -- for i = 1, #hash do - -- tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) - -- end - -- return concat(tab) return hexencode(hash) end return hash @@ -75,11 +65,6 @@ end function crypt.sha1(str, hex) local hash = sha1(str) if hash and hex then - -- local tab = new_tab(#hash, 0) - -- for i = 1, #hash do - -- tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) - -- end - -- return concat(tab) return hexencode(hash) end return hash @@ -88,11 +73,6 @@ end function crypt.sha256 (str, hex) local hash = sha256(str) if hash and hex then - -- local tab = new_tab(#hash, 0) - -- for i = 1, #hash do - -- tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) - -- end - -- return concat(tab) return hexencode(hash) end return hash @@ -101,11 +81,6 @@ end function crypt.sha512 (str, hex) local hash = sha512(str) if hash and hex then - -- local tab = new_tab(#hash, 0) - -- for i = 1, #hash do - -- tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) - -- end - -- return concat(tab) return hexencode(hash) end return hash @@ -115,11 +90,6 @@ end function crypt.hmac_sha1 (key, text, hex) local hash = hmac_sha1(key, text) if hash and hex then - -- local tab = new_tab(#hash, 0) - -- for i = 1, #hash do - -- tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) - -- end - -- return concat(tab) return hexencode(hash) end return hash @@ -128,11 +98,6 @@ end function crypt.hmac_sha256 (key, text, hex) local hash = hmac_sha256(key, text) if hash and hex then - -- local tab = new_tab(#hash, 0) - -- for i = 1, #hash do - -- tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) - -- end - -- return concat(tab) return hexencode(hash) end return hash @@ -141,11 +106,6 @@ end function crypt.hmac_sha512 (key, text, hex) local hash = hmac_sha512(key, text) if hash and hex then - -- local tab = new_tab(#hash, 0) - -- for i = 1, #hash do - -- tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) - -- end - -- return concat(tab) return hexencode(hash) end return hash @@ -154,11 +114,6 @@ end function crypt.xor_str (str, sec, hex) local hash = xor_str(str, sec) if hash and hex then - -- local tab = new_tab(#hash, 0) - -- for i = 1, #hash do - -- tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) - -- end - -- return concat(tab) return hexencode(hash) end return hash @@ -167,11 +122,6 @@ end function crypt.randomkey(hex) local hash = randomkey() if hash and hex then - -- local tab = new_tab(#hash, 0) - -- for i = 1, #hash do - -- tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) - -- end - -- return concat(tab) return hexencode(hash) end return hash @@ -180,11 +130,6 @@ end function crypt.hashkey (key, hex) local hash = hashkey(key) if hash and hex then - -- local tab = new_tab(#hash, 0) - -- for i = 1, #hash do - -- tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) - -- end - -- return concat(tab) return hexencode(hash) end return hash @@ -193,11 +138,6 @@ end function crypt.hmac_hash (key, text, hex) local hash = hmac_hash(key, text) if hash and hex then - -- local tab = new_tab(#hash, 0) - -- for i = 1, #hash do - -- tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) - -- end - -- return concat(tab) return hexencode(hash) end return hash @@ -206,11 +146,6 @@ end function crypt.hmac64 (key, text, hex) local hash = hmac64(key, text) if hash and hex then - -- local tab = new_tab(#hash, 0) - -- for i = 1, #hash do - -- tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) - -- end - -- return concat(tab) return hexencode(hash) end return hash @@ -219,11 +154,6 @@ end function crypt.hmac64_md5 (key, text, hex) local hash = hmac64_md5(key, text) if hash and hex then - -- local tab = new_tab(#hash, 0) - -- for i = 1, #hash do - -- tab[#tab+1] = fmt('%02x', byte(match(hash, '.', i))) - -- end - -- return concat(tab) return hexencode(hash) end return hash From f0cad5b3260e705aa69cb09b18874568b4bbea9c Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 19 Oct 2019 21:47:52 +0800 Subject: [PATCH 402/956] =?UTF-8?q?=E6=89=A9=E5=A4=A7http=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE=E6=8E=A5=E5=8F=97=E7=BC=93=E5=86=B2=E5=8C=BA=E5=A4=A7?= =?UTF-8?q?=E5=B0=8F,=20=E4=BC=98=E5=8C=96http=E4=B8=8A=E4=BC=A0=E6=95=88?= =?UTF-8?q?=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index d2bf0a71..84341ec8 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -127,7 +127,7 @@ local function PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEA if RECV_BODY then local buffers = {BODY} while 1 do - local buf = sock:recv(1024) + local buf = sock:recv(65535) if not buf then return end @@ -276,7 +276,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) secCookie(cookie_secure) -- 如果需要 local sock = tcp:new():set_fd(fd):timeout(timeout) while 1 do - local buf = sock:recv(1024) + local buf = sock:recv(65535) if not buf then return sock:close() end From f41a05b6de6c2858157d9a84a9177ae195dab436 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 19 Oct 2019 21:48:40 +0800 Subject: [PATCH 403/956] =?UTF-8?q?=E6=89=A9=E5=A4=A7httpc=E6=8E=A5?= =?UTF-8?q?=E6=94=B6=E7=BC=93=E5=86=B2=E5=8C=BA=E5=A4=A7=E5=B0=8F,?= =?UTF-8?q?=E6=8F=90=E5=8D=87httpc=E6=8E=A5=E6=94=B6=E6=95=88=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/protocol.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua index 11f0e6cc..b6b63e39 100644 --- a/lualib/httpc/protocol.lua +++ b/lualib/httpc/protocol.lua @@ -136,7 +136,7 @@ local function httpc_response(sock, SSL) local content = {} local times = 0 while 1 do - local data, len = sock_recv(sock, SSL, 2048) + local data, len = sock_recv(sock, SSL, 65535) if not data then return nil, SSL.." A peer of remote server close this connection." end @@ -163,7 +163,7 @@ local function httpc_response(sock, SSL) local content = {split(DATA, posB + 1, #DATA)} local Len = #content[1] while 1 do - local data, len = sock_recv(sock, SSL, 2048) + local data, len = sock_recv(sock, SSL, 65535) if not data then return nil, SSL.."[Content_Length] A peer of remote server close this connection." end @@ -189,7 +189,7 @@ local function httpc_response(sock, SSL) insert(content, buf) end while 1 do - local data, len = sock_recv(sock, SSL, 2048) + local data, len = sock_recv(sock, SSL, 65535) if not data then return CODE, SSL.."[chunked] A peer of remote server close this connection A." end From 51a07ed4c5df64b3c84db4a96e2501a6c9e2b2a5 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 21 Oct 2019 00:02:37 +0800 Subject: [PATCH 404/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/README.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 lualib/README.md diff --git a/lualib/README.md b/lualib/README.md new file mode 100644 index 00000000..f178368b --- /dev/null +++ b/lualib/README.md @@ -0,0 +1,39 @@ +## 内置库列表 + + * mail库 + + * csv库 + + * SDK库 + + * DB库(MySQL) + + * Cache库(Redis) + + * logging库 + + * httpc库 + + * MQ库 + + * crypt库 + + * webhook库 + + * json库 + + * msgpack库 + + * protobuf库 + + * template库 + + * cf库 + + * admin库 + + * xml库 + + * httpd库 + + * system库 \ No newline at end of file From ec20993fa0035542f9c54ef70be84858d9c5eb68 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 21 Oct 2019 03:13:17 +0800 Subject: [PATCH 405/956] =?UTF-8?q?=E4=BC=98=E5=8C=96httpd=E5=A4=A7?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E9=87=8F=E8=AF=B7=E6=B1=82=E6=95=88=E7=8E=87?= =?UTF-8?q?=E4=B8=8E=E5=86=85=E5=AD=98=E5=8D=A0=E7=94=A8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index 84341ec8..d870a0d9 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -10,6 +10,7 @@ local sha1 = crypt.sha1 local base64encode = crypt.base64encode local now = sys.now local DATE = require("sys").date +local new_tab = require("sys").new_tab local insert = table.insert local form = require "httpd.Form" @@ -24,7 +25,6 @@ local secCookie = Cookie.setSecure -- 设置Cookie加密字段 local seCookie = Cookie.serialization -- 序列化 local deCookie = Cookie.deserialization -- 反序列化 - local Router = require "httpd.Router" local ROUTE_FIND = Router.find local ROUTE_REGISTERY = Router.registery @@ -39,6 +39,7 @@ local lower = string.lower local upper = string.upper local match = string.match local fmt = string.format +local ceil = math.ceil local toint = math.tointeger local find = string.find local split = string.sub @@ -78,7 +79,7 @@ local HTTP_PROTOCOL = { -- 以下为 HTTP Server 所需所用方法 local function REQUEST_STATUCODE_RESPONSE(code) - return HTTP_CODE[code] or "attempt to Passed A Invaid Code to response message." + return HTTP_CODE[code] or "attempt to passed a invalid code to response message." end local function REQUEST_MIME_RESPONSE(mime) @@ -125,19 +126,20 @@ local function PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEA end end if RECV_BODY then - local buffers = {BODY} + local buf_len = #BODY + local buffers = body_len > 65535 and new_tab(ceil(body_len / 65535), 0) or {} while 1 do - local buf = sock:recv(65535) + local buf, len = sock:recv(65535) if not buf then return end - buffers[#buffers+1] = buf - local buffer = concat(buffers) - if #buffer >= (max_body_size or 1024 * 1024) then + buf_len = buf_len + len + if buf_len >= (max_body_size or 1024 * 1024) then return nil, 413 end - if #buffer == body_len then - BODY = buffer + buffers[#buffers + 1] = buf + if buf_len == body_len then + BODY = concat(buffers) break end end @@ -276,7 +278,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) secCookie(cookie_secure) -- 如果需要 local sock = tcp:new():set_fd(fd):timeout(timeout) while 1 do - local buf = sock:recv(65535) + local buf = sock:recv(8192) if not buf then return sock:close() end From c53ada64b4a0c24567246a4201b99297a05ea0df Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 21 Oct 2019 05:11:49 +0800 Subject: [PATCH 406/956] =?UTF-8?q?=E4=B8=BAcrypt=E5=BA=93=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0base64=E7=9A=84=E5=AE=89=E5=85=A8url=E7=BC=96=E7=A0=81?= =?UTF-8?q?=E4=B8=8E=E8=A7=A3=E7=A0=81,=E5=90=8C=E6=97=B6=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E4=B8=83=E7=89=9B=E5=BA=93=E7=9A=84=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E4=B8=BA=E7=9B=B4=E6=8E=A5=E5=BC=95=E7=94=A8=E6=AD=A4=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/cloud/qiniu/crypt.lua | 12 ++++-------- lualib/crypt/init.lua | 8 ++++++++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lualib/cloud/qiniu/crypt.lua b/lualib/cloud/qiniu/crypt.lua index 9c918400..78b6cb15 100644 --- a/lualib/cloud/qiniu/crypt.lua +++ b/lualib/cloud/qiniu/crypt.lua @@ -1,17 +1,13 @@ local crypt = require "crypt" +local base64urlencode = crypt.base64urlencode +local base64urldecode = crypt.base64urldecode local Crypt = { hmac_sha1 = crypt.hmac_sha1, urlencode = crypt.urlencode, urldecode = crypt.urldecode, + urlsafe_base64encode = base64urlencode, + urlsafe_base64decode = base64urldecode, } -function Crypt.urlsafe_base64encode(data) - return crypt.base64encode(data):gsub('+', '-'):gsub('/', '_') -end - -function Crypt.urlsafe_base64decode(data) - return crypt.base64decode(data:gsub('-', '+'):gsub("_", "/")) -end - return Crypt \ No newline at end of file diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index 7a683f4e..9866d990 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -167,6 +167,14 @@ function crypt.base64decode (...) return base64decode(...) end +function crypt.base64urlencode(data) + return base64encode(data):gsub('+', '-'):gsub('/', '_') +end + +function crypt.base64urldecode(data) + return base64decode(data:gsub('-', '+'):gsub('_', '/')) +end + function crypt.hexencode (...) return hexencode(...) end From 39fdec90749ce07156c0e777adc00e048f545e12 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 21 Oct 2019 05:12:35 +0800 Subject: [PATCH 407/956] =?UTF-8?q?httpd.http=E5=AE=9E=E7=8E=B0=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0jwt=E4=B8=8Ebasic=E7=94=9F=E6=88=90=E6=96=B9=E6=B3=95,?= =?UTF-8?q?=20=E4=BD=BF=E7=94=A8=E6=96=B9=E5=BC=8F=E8=AF=B7=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E5=8F=82=E8=80=83=E6=AD=A4=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/http.lua | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lualib/httpd/http.lua b/lualib/httpd/http.lua index 5649b7dd..80f26100 100644 --- a/lualib/httpd/http.lua +++ b/lualib/httpd/http.lua @@ -1,6 +1,15 @@ +local json = require "json" +local json_encode = json.encode + +local crypt = require "crypt" +local hmac_sha256 = crypt.hmac_sha256 +local base64encode = crypt.base64encode +local base64urlencode = crypt.base64urlencode + local type = type local assert = assert local find = string.find +local concat = table.concat local http = {} --[[ @@ -14,6 +23,24 @@ function http.ok() return 200 end +-- http base authorization +function http.basic_authorization(username, password) + return "Authorization", "Basic " .. base64encode(username .. ":" .. password) +end + +-- Json Web Token +function http.jwt(secret, payload) + local content = {nil, nil, nil} + -- header + content[#content + 1] = base64urlencode(json_encode{ alg = "HS256", typ = "JWT" }) + -- payload + content[#content + 1] = base64urlencode(payload) + -- signature + content[#content + 1] = hmac_sha256(secret, concat(content, "."), true) + -- result. + return "Authorization", "Bearer " .. concat(content, ".") +end + -- 重定向 function http.redirect(domain, code) assert(type(domain) == 'string' and domain ~= '' and find(domain, '^http[s]?://.+'), '重定向必须给出一个字符串类型的域名(http[s]://domain)') From ce0275318febf9f4a5d278f600f27fb175085c26 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 21 Oct 2019 08:21:37 +0800 Subject: [PATCH 408/956] =?UTF-8?q?httpc=E5=BA=93=E5=A2=9E=E5=8A=A0basic?= =?UTF-8?q?=20auth=E6=9E=84=E5=BB=BA=E6=96=B9=E6=B3=95=E4=B8=8Ejwt?= =?UTF-8?q?=E6=9E=84=E5=BB=BA=E6=96=B9=E6=B3=95.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/class.lua | 10 ++++++++++ lualib/httpc/init.lua | 4 ++++ lualib/httpc/protocol.lua | 30 ++++++++++++++++++++++++++++-- lualib/httpd/http.lua | 28 ---------------------------- 4 files changed, 42 insertions(+), 30 deletions(-) diff --git a/lualib/httpc/class.lua b/lualib/httpc/class.lua index 5f15a13a..8e6f06fa 100644 --- a/lualib/httpc/class.lua +++ b/lualib/httpc/class.lua @@ -16,6 +16,8 @@ local build_json_req = protocol.build_json_req local build_file_req = protocol.build_file_req local build_put_req = protocol.build_put_req local build_delete_req = protocol.build_delete_req +local build_jwt = protocol.build_jwt +local build_basic_authorization = protocol.build_basic_authorization local type = type local assert = assert @@ -48,6 +50,14 @@ function httpc:ctor (opt) self.timeout = opt.timeout or 15 end +function httpc:jwt(...) + return build_jwt(...) +end + +function httpc:basic_authorization( ... ) + return build_basic_authorization(...) +end + -- get 请求 function httpc:get (domain, headers, args, timeout) local opt, err = splite_protocol(domain) diff --git a/lualib/httpc/init.lua b/lualib/httpc/init.lua index d01ab372..23849dd4 100644 --- a/lualib/httpc/init.lua +++ b/lualib/httpc/init.lua @@ -21,6 +21,8 @@ local build_json_req = protocol.build_json_req local build_file_req = protocol.build_file_req local build_put_req = protocol.build_put_req local build_delete_req = protocol.build_delete_req +local build_jwt = protocol.build_jwt +local build_basic_authorization = protocol.build_basic_authorization local type = type local assert = assert @@ -282,4 +284,6 @@ return { file = file, put = put, multi_request = multi_request, + jwt = build_jwt, + basic_authorization = build_basic_authorization, } diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua index b6b63e39..82b50510 100644 --- a/lualib/httpc/protocol.lua +++ b/lualib/httpc/protocol.lua @@ -1,7 +1,13 @@ local tcp = require "internal.TCP" -local url = require "url" -local url_encode = url.encode +local json = require "json" +local json_encode = json.encode + +local crypt = require "crypt" +local url_encode = crypt.urlencode +local hmac_sha256 = crypt.hmac_sha256 +local base64encode = crypt.base64encode +local base64urlencode = crypt.base64urlencode local HTTP_PARSER = require "protocol.http.parser" local FILEMIME = require "protocol.http.mime" @@ -389,6 +395,24 @@ local function build_put_req (opt) return concat(request, CRLF) .. CRLF2 .. ( type(opt.body) == "string" and opt.body or '' ) end +-- http base authorization +local function build_basic_authorization(username, password) + return "Authorization", "Basic " .. base64encode(username .. ":" .. password) +end + +-- Json Web Token +local function build_jwt(secret, payload) + local content = {nil, nil, nil} + -- header + content[#content + 1] = base64urlencode(json_encode{ alg = "HS256", typ = "JWT" }) + -- payload + content[#content + 1] = base64urlencode(payload) + -- signature + content[#content + 1] = hmac_sha256(secret, concat(content, "."), true) + -- result. + return "Authorization", "Bearer " .. concat(content, ".") +end + return { sock_new = sock_new, sock_recv = sock_recv, @@ -402,4 +426,6 @@ return { build_file_req = build_file_req, build_put_req = build_put_req, build_delete_req = build_delete_req, + build_jwt = build_jwt, + build_basic_authorization = build_basic_authorization, } diff --git a/lualib/httpd/http.lua b/lualib/httpd/http.lua index 80f26100..efdb2edd 100644 --- a/lualib/httpd/http.lua +++ b/lualib/httpd/http.lua @@ -1,15 +1,6 @@ -local json = require "json" -local json_encode = json.encode - -local crypt = require "crypt" -local hmac_sha256 = crypt.hmac_sha256 -local base64encode = crypt.base64encode -local base64urlencode = crypt.base64urlencode - local type = type local assert = assert local find = string.find -local concat = table.concat local http = {} --[[ @@ -17,30 +8,11 @@ local http = {} 如果使用者在函数内直接写入状态码, 请确保您非常清楚before函数的实现. --]] - -- 成功 function http.ok() return 200 end --- http base authorization -function http.basic_authorization(username, password) - return "Authorization", "Basic " .. base64encode(username .. ":" .. password) -end - --- Json Web Token -function http.jwt(secret, payload) - local content = {nil, nil, nil} - -- header - content[#content + 1] = base64urlencode(json_encode{ alg = "HS256", typ = "JWT" }) - -- payload - content[#content + 1] = base64urlencode(payload) - -- signature - content[#content + 1] = hmac_sha256(secret, concat(content, "."), true) - -- result. - return "Authorization", "Bearer " .. concat(content, ".") -end - -- 重定向 function http.redirect(domain, code) assert(type(domain) == 'string' and domain ~= '' and find(domain, '^http[s]?://.+'), '重定向必须给出一个字符串类型的域名(http[s]://domain)') From 38badb883a4f66de0c5386087a321e183ac457e0 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 21 Oct 2019 08:21:55 +0800 Subject: [PATCH 409/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B5=8B=E8=AF=95htt?= =?UTF-8?q?pc=20auth=E7=9B=B8=E5=85=B3=E6=B5=8B=E8=AF=95=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_http_auth.lua | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 script/test_http_auth.lua diff --git a/script/test_http_auth.lua b/script/test_http_auth.lua new file mode 100644 index 00000000..176bb80c --- /dev/null +++ b/script/test_http_auth.lua @@ -0,0 +1,19 @@ +-- 从httpc库导入并且使用 +local httpc = require "httpc" + +local basic_key, basic_value = httpc.basic_authorization("myusername", "mypassword") +print(basic_key, basic_value) + +local jwt_header_key, jwt_header_value = httpc.jwt("mysecret", [[{"key1":"value1","key2":"value2"}]]) +print(jwt_header_key, jwt_header_value) + + +-- 从httpc类库中导入并且使用 +local httpc_cls = require "httpc.class" +local hc = httpc_cls:new {} + +local basic_key, basic_value = hc:basic_authorization("myusername", "mypassword") +print(basic_key, basic_value) + +local jwt_header_key, jwt_header_value = hc:jwt("mysecret", [[{"key1":"value1","key2":"value2"}]]) +print(jwt_header_key, jwt_header_value) \ No newline at end of file From 62dbec57e375488714b671f7ebed4cdca4555f38 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 22 Oct 2019 04:51:09 +0800 Subject: [PATCH 410/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0qrcanvas,=20=E6=96=B9?= =?UTF-8?q?=E4=BE=BF=E4=B8=80=E4=BA=9B=E7=89=B9=E6=AE=8Aqrcode=E5=9B=BE?= =?UTF-8?q?=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/js/qrcanvas.min.js | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 static/js/qrcanvas.min.js diff --git a/static/js/qrcanvas.min.js b/static/js/qrcanvas.min.js new file mode 100644 index 00000000..3bca0f4e --- /dev/null +++ b/static/js/qrcanvas.min.js @@ -0,0 +1,2 @@ +/*! qrcanvas v3.0.4 | ISC License */ +!function(t,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):r((t=t||self).qrcanvas={})}(this,function(t){"use strict";var r,f=(function(t,r){var e=function(){var i=function(t,r){var u=t,a=p[r],l=null,s=0,n=null,f=[],h={},c=function(t,r){l=function(t){for(var r=new Array(t),e=0;e>e&1);l[Math.floor(e/3)][e%3+s-8-3]=n}for(e=0;e<18;e+=1){n=!t&&1==(r>>e&1);l[e%3+s-8-3][Math.floor(e/3)]=n}},v=function(t,r){for(var e=a<<3|r,n=y.getBCHTypeInfo(e),o=0;o<15;o+=1){var i=!t&&1==(n>>o&1);o<6?l[o][8]=i:o<8?l[o+1][8]=i:l[s-15+o][8]=i}for(o=0;o<15;o+=1){i=!t&&1==(n>>o&1);o<8?l[8][s-o-1]=i:o<9?l[8][15-o-1+1]=i:l[8][15-o-1]=i}l[s-8][8]=!t},d=function(t,r){for(var e=-1,n=s-1,o=7,i=0,a=y.getMaskFunction(r),u=s-1;0>>o&1)),a(n,u-f)&&(c=!c),l[n][u-f]=c,-1==(o-=1)&&(i+=1,o=7)}if((n+=e)<0||s<=n){n-=e,e=-e;break}}},w=function(t,r,e){for(var n=x.getRSBlocks(t,r),o=B(),i=0;i8*u)throw"code length overflow. ("+o.getLengthInBits()+">"+8*u+")";for(o.getLengthInBits()+4<=8*u&&o.put(0,4);o.getLengthInBits()%8!=0;)o.putBit(!1);for(;!(o.getLengthInBits()>=8*u||(o.put(236,8),o.getLengthInBits()>=8*u));)o.put(17,8);return function(t,r){for(var e=0,n=0,o=0,i=new Array(r.length),a=new Array(r.length),u=0;u',e+="";for(var n=0;n";for(var o=0;o';e+=""}return e+="",e+=""},h.createSvgTag=function(t,r){var e={};"object"==typeof arguments[0]&&(t=(e=arguments[0]).cellSize,r=e.margin),t=t||2,r=void 0===r?4*t:r;var n,o,i,a,u=h.getModuleCount()*t+2*r,f="";for(a="l"+t+",0 0,"+t+" -"+t+",0 0,-"+t+"z ",f+='>>8),r.push(255&o)):r.push(a)}}return r}};var r,t,e,a=1,u=2,o=4,f=8,p={L:1,M:0,Q:3,H:2},n=0,c=1,l=2,s=3,g=4,h=5,v=6,d=7,y=(r=[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],e=function(t){for(var r=0;0!=t;)r+=1,t>>>=1;return r},(t={}).getBCHTypeInfo=function(t){for(var r=t<<10;0<=e(r)-e(1335);)r^=1335<>>7-t%8&1)},put:function(t,r){for(var e=0;e>>r-e-1&1))},getLengthInBits:function(){return n},putBit:function(t){var r=Math.floor(n/8);e.length<=r&&e.push(0),t&&(e[r]|=128>>>n%8),n+=1}};return o},b=function(t){var r=a,n=t,e={getMode:function(){return r},getLength:function(t){return n.length},write:function(t){for(var r=n,e=0;e+2>>8&255)+(255&n),t.put(n,13),e+=2}if(e>>8)},writeBytes:function(t,r,e){r=r||0,e=e||t.length;for(var n=0;n=e.length){if(0==i)return-1;throw"unexpected end of file./"+i}var t=e.charAt(n);if(n+=1,"="==t)return i=0,-1;t.match(/^\s$/)||(o=o<<6|a(t.charCodeAt(0)),i+=6)}var r=o>>>i-8&255;return i-=8,r}},a=function(t){if(65<=t&&t<=90)return t-65;if(97<=t&&t<=122)return t-97+26;if(48<=t&&t<=57)return t-48+52;if(43==t)return 62;if(47==t)return 63;throw"c:"+t};return r},z=function(t,r,e){for(var n,o,i,a,v,u,f,d,c=(i=n=t,a=o=r,v=new Array(n*o),u={setPixel:function(t,r,e){v[r*i+t]=e},write:function(t){t.writeString("GIF87a"),t.writeShort(i),t.writeShort(a),t.writeByte(128),t.writeByte(0),t.writeByte(0),t.writeByte(0),t.writeByte(0),t.writeByte(0),t.writeByte(255),t.writeByte(255),t.writeByte(255),t.writeString(","),t.writeShort(0),t.writeShort(0),t.writeShort(i),t.writeShort(a),t.writeByte(0);var r=f(2);t.writeByte(2);for(var e=0;255>>r!=0)throw"length over";for(;8<=u+r;)a.writeByte(255&(t<>>=8-u,u=f=0;f|=t<>>w-6),w-=6},C.flush=function(){if(0>6,128|63&n):n<55296||57344<=n?r.push(224|n>>12,128|n>>6&63,128|63&n):(e++,n=65536+((1023&n)<<10|1023&t.charCodeAt(e)),r.push(240|n>>18,128|n>>12&63,128|n>>6&63,128|63&n))}return r}(t)},t.exports=e}(r={exports:{}},r.exports),r.exports),A="#000",M="#fff",e=[],n=function(){throw new Error("Not implemented")},L={createCanvas:n,isCanvas:n,isDrawable:n,getCanvas:D,cacheCanvas:function(){e.push.apply(e,arguments)},drawCanvas:function(t,r,e){void 0===e&&(e={});var n=e,o=n.cellSize,i=n.context,a=n.clear,u=void 0===a||a,f=t.width,c=t.height,l=[r],s=i||t.getContext("2d");u&&s.clearRect(0,0,f,c);s.globalCompositeOperation="source-over";for(;l.length;){var g=l.shift();if(Array.isArray(g))l=g.concat(l);else if(g){var h=void 0,v=("col"in(h=L.isDrawable(g)?{image:g}:"string"==typeof g?{style:g}:g)?h.col*o:h.x)||0,d=("row"in h?h.row*o:h.y)||0;v<0&&(v+=f),d<0&&(d+=f);var w=("cols"in h?h.cols*o:h.w)||f,p=("rows"in h?h.rows*o:h.h)||f;h.image?s.drawImage(h.image,v,d,w,p):(s.fillStyle=h.style||"black",s.fillRect(v,d,w,p))}}return t},drawText:function(t,r){void 0===r&&(r={});var c,e=r||{},n=e.fontSize,o=void 0===n?64:n,i=e.fontStyle,a=void 0===i?"":i,u=e.fontFamily,f=void 0===u?"Cursive":u,l=e.color,s=void 0===l?null:l,g=e.pad,h=void 0===g?8:g,v=e.padColor,d=void 0===v?M:v,w=e.mode,p=void 0===w?1:w,y=D(),C=y.getContext("2d");if(d){C.fillStyle=d,C.fillRect(0,0,1,1);var m=C.getImageData(0,0,1,1);(c=m.data)[3]||(c=null)}var k=o+2*h,x=[a,o+"px",f].filter(Boolean).join(" "),B=function(){C.textAlign="center",C.textBaseline="middle",C.font=x};B();var b=Math.ceil(C.measureText(t).width)+2*h;y.width=b,y.height=k,B();var S=function(){C.fillStyle=s||A,C.fillText(t,b/2,k/2)};1===p?(C.fillStyle=d,C.fillRect(0,0,b,k),S()):(S(),c&&function(){for(var t=C.getImageData(0,0,b,k),i=t.data,a=b*k,u=[],f=0,r=function(t){var e=[],n={};u[f]=e;var r=u[f=1-f];if(!r){r=[];for(var o=0;o Date: Wed, 23 Oct 2019 05:04:43 +0800 Subject: [PATCH 411/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=85=BE=E8=AE=AF/?= =?UTF-8?q?=E7=99=BE=E5=BA=A6/=E9=AB=98=E5=BE=B7=E5=AE=9A=E4=BD=8D?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1SDK?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/cloud/location/amap.lua | 71 +++++++++++++++++++ lualib/cloud/location/baidu.lua | 87 +++++++++++++++++++++++ lualib/cloud/location/tencent.lua | 111 +++++++++++++++++++++++++++++ lualib/cloud/tencent/location.lua | 112 +----------------------------- 4 files changed, 270 insertions(+), 111 deletions(-) create mode 100644 lualib/cloud/location/amap.lua create mode 100644 lualib/cloud/location/baidu.lua create mode 100644 lualib/cloud/location/tencent.lua diff --git a/lualib/cloud/location/amap.lua b/lualib/cloud/location/amap.lua new file mode 100644 index 00000000..6ccad098 --- /dev/null +++ b/lualib/cloud/location/amap.lua @@ -0,0 +1,71 @@ +local httpc = require "httpc" +local crypt = require "crypt" +local md5 = crypt.md5 +local urlencode = crypt.urlencode + +local pairs = pairs +local ipairs = ipairs +local sort = table.sort +local concat = table.concat + +--[[ + + 官网: https://lbs.amap.com + + 文档: https://lbs.amap.com/api/webservice/summary + +]] + +local amap = { __Version__ = 0.1, host = "https://restapi.amap.com" } + +-- sn签名 +local function sign(app_key, secret_key, opt) + local keys = {"key"} + for key, value in pairs(opt) do + keys[#keys+1] = key + end + sort(keys) + local args = {} + for _, key in ipairs(keys) do + if key == "key" then + args[#args+1] = "key=" .. app_key + else + args[#args+1] = key .. '=' .. urlencode(opt[key]) + end + end + args[#args+1] = "sig=" .. md5(concat(args, "&"), true) .. secret_key + return concat(args, "&") +end + +-- IP定位 +function amap.ip(key, secret_key, ip) + return httpc.get(amap.host .. "/v3/ip?" .. sign(key, secret_key, { ip = ip, output = "JSON"})) +end + +-- 天气预报 +function amap.weather(key, secret_key, city, extensions) + return httpc.get(amap.host .. "/v3/weather/weatherInfo?" .. sign(key, secret_key, { output = "JSON", city = city, extensions = extensions})) +end + +-- 坐标转换 +function amap.convert(key, secret_key, locations, coordsys) + return httpc.get(amap.host .. "/v3/assistant/coordinate/convert?" .. sign(key, secret_key, { output = "JSON", locations = locations, coordsys = coordsys })) +end + +-- 地理编码 +function amap.geo(key, secret_key, opt) + return httpc.get(amap.host .. "/v3/geocode/geo?" .. sign(key, secret_key, opt)) +end + +-- 行政区域查询 +function amap.district(key, secret_key, opt) + return httpc.get(amap.host .. "/v3/config/district?" .. sign(key, secret_key, opt)) +end + +-- 输入提示 +function amap.tips(key, secret_key, opt) + return httpc.get(amap.host .. "/v3/assistant/inputtips?" .. sign(key, secret_key, opt)) +end + + +return amap \ No newline at end of file diff --git a/lualib/cloud/location/baidu.lua b/lualib/cloud/location/baidu.lua new file mode 100644 index 00000000..dd8086d3 --- /dev/null +++ b/lualib/cloud/location/baidu.lua @@ -0,0 +1,87 @@ +local httpc = require "httpc" +local crypt = require "crypt" +local md5 = crypt.md5 +local urlencode = crypt.urlencode + +local pairs = pairs +local ipairs = ipairs +local sort = table.sort +local concat = table.concat + +--[[ + 官网: https://lbsyun.baidu.com + 文档地址: https://lbsyun.baidu.com/index.php?title=webapi +]] + +local baidu = { __Version__ = 0.1, host = "https://api.map.baidu.com" } + +local function sign(query_str, ak, sk, opt) + local keys = {} + for key, _ in pairs(opt) do + keys[#keys+1] = key + end + sort(keys) + local args = {} + for index, key in ipairs(keys) do + args[#args+1] = key .. "=" .. urlencode(opt[key]) + end + args[#args+1] = "ak=" .. ak + args[#args+1] = "sn=" .. md5(urlencode(query_str .. concat(args, "&") ..sk), true) + return concat(args, "&") +end + +-- 智能硬件定位 +function baidu.locapi(ak, sk, opt) + local path = "/locapi/v2?" + return httpc.get(baidu.host .. path .. sign(path, ak, sk, opt)) +end + +-- 实时路况 +function baidu.road(ak, sk, opt) + local path = "/traffic/v1/road?" + return httpc.get(baidu.host .. path .. sign(path, ak, sk, opt)) +end + +-- 坐标附近上车点 +function baidu.parking(ak, sk, opt) + local path = "/parking/search?" + return httpc.get(baidu.host .. path .. sign(path, ak, sk, opt)) +end + +-- 坐标系转换 +function baidu.convert(ak, sk, opt) + local path = "/geoconv/v1/?" + return httpc.get(baidu.host .. path .. sign(path, ak, sk, opt)) +end + +-- 位置时区 +function baidu.timezone(ak, sk, opt) + local path = "/timezone/v1?" + return httpc.get(baidu.host .. path .. sign(path, ak, sk, opt)) +end + +-- 位置检索 +function baidu.geosearch(ak, sk, opt) + local path = "/place/v2/search?" + return httpc.get(baidu.host .. path .. sign(path, ak, sk, opt)) +end + +-- 位置IP +function baidu.geoip(ak, sk, opt) + local path = "/location/ip?" + return httpc.get(baidu.host .. path .. sign(path, ak, sk, opt)) +end + +-- 坐标检索 +function baidu.geosuggestion(ak, sk, opt) + local path = "/place/v2/suggestion?" + return httpc.get(baidu.host .. path .. sign(path, ak, sk, opt)) +end + +-- 坐标编码 +function baidu.geo(ak, sk, opt) + local path = "/geocoding/v3/?" + return httpc.get(baidu.host .. path .. sign(path, ak, sk, opt)) +end + +return baidu \ No newline at end of file diff --git a/lualib/cloud/location/tencent.lua b/lualib/cloud/location/tencent.lua new file mode 100644 index 00000000..2803b2e0 --- /dev/null +++ b/lualib/cloud/location/tencent.lua @@ -0,0 +1,111 @@ +local httpc = require "httpc" +local crypt = require "crypt" + +local type = type +local pairs = pairs +local ipairs = ipairs +local assert = assert + +--[[ + 文档地址: https://lbs.qq.com/webservice_v1/index.html + 目前所有API接口仅支持SN码签名校验, 请自行在腾讯位置服务端`后台管理`->`key管理`中进行设置获取key并且生成sn码. + 所有接口数据均返回原生http code与json数据, 请开发者自行进行接口判断与json decode. +]] + +local function sign(sn, path, opt) + local key_sorts = {} + for key, value in pairs(opt) do + key_sorts[#key_sorts+1] = key + end + table.sort(key_sorts) + local args = {} + local signs = {} + for index, key in ipairs(key_sorts) do + signs[#signs+1] = key .. '=' .. opt[key] + args[#args+1] = {key, opt[key]} + end + local sig = crypt.md5(path .. '?' .. table.concat(signs, "&") .. sn, true) + args[#args+1] = {"sig", sig} + return args +end + +local tencent = { __Version__ = 0.1 } + +-- IP定位 +--[[ + ip : IP地址 +]] +function tencent.getIpLocation (accesskey, sn, ip) + return httpc.get("https://apis.map.qq.com/ws/location/v1/ip", nil, sign(sn, "/ws/location/v1/ip", { + ip = ip, key = accesskey + })) +end + +-- 获取行政规划区列表 +function tencent.getDistrictList (accesskey, sn) + return httpc.get("https://apis.map.qq.com/ws/district/v1/list", nil, sign(sn, "/ws/district/v1/list", {key = accesskey})) +end + +-- 获取指定行政规划区 +--[[ + id: 父级行政区划ID,缺省时则返回最顶级行政区划 +]] +function tencent.getDistrictChildren (accesskey, sn, id) + return httpc.get("https://apis.map.qq.com/ws/district/v1/getchildren", nil, sign(sn, "/ws/district/v1/getchildren", { + id = id, key = accesskey + })) +end + +-- 关键词猜测(补全) +--[[ + keyword: 搜索/联想/补全关键词 + region: 范围, 如: 广州 + region_fix : 是否固定范围. + location: 不支持 + page_index: 当前是第几页 + page_size: 每页返回数量 +]] +function tencent.searchSuggestion (accesskey, sn, keyword, region, region_fix, page_index, page_size) + return httpc.get("https://apis.map.qq.com/ws/place/v1/suggestion", nil, sign(sn, "/ws/place/v1/suggestion", { + key = accesskey, keyword = keyword, + page_index = page_index or 1, + page_size = page_size or 10, + region = region or '', + region_fix = region_fix or 1, + })) +end + +function tencent.searchPlace (accesskey, sn, keyword, boundary, page_index, page_size, order) + return httpc.get("https://apis.map.qq.com/ws/place/v1/search", nil, sign(sn, "/ws/place/v1/search", { + key = accesskey, + keyword = keyword, + boundary = boundary, + page_index = page_index, + page_size = page_size, + order = order, + })) +end + +-- 关键词搜索行政规划区 +--[[ + keyword: 行政区关键词 +]] +function tencent.searchDistrict (accesskey, sn, keyword) + return httpc.get("https://apis.map.qq.com/ws/district/v1/search", nil, sign(sn, "/ws/district/v1/search", { + key = accesskey, keyword = keyword + })) +end + +-- 距离计算(一对多) +--[[ + mode: driving 驾车, wakling 步行 + from: 起始经纬度 + to: 目的经纬度 +]] +function tencent.getDistance (accesskey, sn, opt) + return httpc.get("https://apis.map.qq.com/ws/distance/v1/", nil, sign(sn, "/ws/distance/v1/", { + key = accesskey, to = opt.to, from = opt.from, mode = opt.mode, + })) +end + +return tencent diff --git a/lualib/cloud/tencent/location.lua b/lualib/cloud/tencent/location.lua index 27843f15..6af4210b 100644 --- a/lualib/cloud/tencent/location.lua +++ b/lualib/cloud/tencent/location.lua @@ -1,111 +1 @@ -local httpc = require "httpc" -local crypt = require "crypt" - -local type = type -local pairs = pairs -local ipairs = ipairs -local assert = assert - ---[[ - 文档地址: https://lbs.qq.com/webservice_v1/index.html - 目前所有API接口仅支持SN码签名校验, 请自行在腾讯位置服务端`后台管理`->`key管理`中进行设置获取key并且生成sn码. - 所有接口数据均返回原生http code与json数据, 请开发者自行进行接口判断与json decode. -]] - -local function sign(sn, path, opt) - local key_sorts = {} - for key, value in pairs(opt) do - key_sorts[#key_sorts+1] = key - end - table.sort(key_sorts) - local args = {} - local signs = {} - for index, key in ipairs(key_sorts) do - signs[#signs+1] = key .. '=' .. opt[key] - args[#args+1] = {key, opt[key]} - end - local sig = crypt.md5(path .. '?' .. table.concat(signs, "&") .. sn, true) - args[#args+1] = {"sig", sig} - return args -end - -local Location = { __Version__ = 0.1 } - --- IP定位 ---[[ - ip : IP地址 -]] -function Location.getIpLocation (accesskey, sn, ip) - return httpc.get("https://apis.map.qq.com/ws/location/v1/ip", nil, sign(sn, "/ws/location/v1/ip", { - ip = ip, key = accesskey - })) -end - --- 获取行政规划区列表 -function Location.getDistrictList (accesskey, sn) - return httpc.get("https://apis.map.qq.com/ws/district/v1/list", nil, sign(sn, "/ws/district/v1/list", {key = accesskey})) -end - --- 获取指定行政规划区 ---[[ - id: 父级行政区划ID,缺省时则返回最顶级行政区划 -]] -function Location.getDistrictChildren (accesskey, sn, id) - return httpc.get("https://apis.map.qq.com/ws/district/v1/getchildren", nil, sign(sn, "/ws/district/v1/getchildren", { - id = id, key = accesskey - })) -end - --- 关键词猜测(补全) ---[[ - keyword: 搜索/联想/补全关键词 - region: 范围, 如: 广州 - region_fix : 是否固定范围. - location: 不支持 - page_index: 当前是第几页 - page_size: 每页返回数量 -]] -function Location.searchSuggestion (accesskey, sn, keyword, region, region_fix, page_index, page_size) - return httpc.get("https://apis.map.qq.com/ws/place/v1/suggestion", nil, sign(sn, "/ws/place/v1/suggestion", { - key = accesskey, keyword = keyword, - page_index = page_index or 1, - page_size = page_size or 10, - region = region or '', - region_fix = region_fix or 1, - })) -end - -function Location.searchPlace (accesskey, sn, keyword, boundary, page_index, page_size, order) - return httpc.get("https://apis.map.qq.com/ws/place/v1/search", nil, sign(sn, "/ws/place/v1/search", { - key = accesskey, - keyword = keyword, - boundary = boundary, - page_index = page_index, - page_size = page_size, - order = order, - })) -end - --- 关键词搜索行政规划区 ---[[ - keyword: 行政区关键词 -]] -function Location.searchDistrict (accesskey, sn, keyword) - return httpc.get("https://apis.map.qq.com/ws/district/v1/search", nil, sign(sn, "/ws/district/v1/search", { - key = accesskey, keyword = keyword - })) -end - --- 距离计算(一对多) ---[[ - mode: driving 驾车, wakling 步行 - from: 起始经纬度 - to: 目的经纬度 -]] -function Location.getDistance (accesskey, sn, opt) - return httpc.get("https://apis.map.qq.com/ws/distance/v1/", nil, sign(sn, "/ws/distance/v1/", { - key = accesskey, to = opt.to, from = opt.from, mode = opt.mode, - })) -end - -return Location +return require "cloud.location.tencent" From a66aba19e8cdebfc0ae34d87ff95ad340e87f6ed Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 23 Oct 2019 05:05:08 +0800 Subject: [PATCH 412/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=AE=9A=E4=BD=8D?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E6=B5=8B=E8=AF=95=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_location.lua | 81 ++++++++++++++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 12 deletions(-) diff --git a/script/test_location.lua b/script/test_location.lua index 8f93d843..5f79c3ea 100644 --- a/script/test_location.lua +++ b/script/test_location.lua @@ -1,32 +1,89 @@ local location = require "cloud.tencent.location" --[[ - 使用详情请参考: lualib/cloud/tencent/location.lua + 腾讯定位服务 + 使用详情请参考: lualib/cloud/location/tencent.lua ]] -local accesskey = "Your_Access_key" +-- local accesskey = "Your_Access_key" +-- local secretkey = "Your_Secret_Key" -local sn = "Your_SN_Key" - --- local code, ret = location.getIpLocation(accesskey, sn, '8.8.8.8') +-- local code, ret = location.getIpLocation(accesskey, secretkey, '8.8.8.8') -- print(code, ret) --- local code, ret = location.getDistrictList(accesskey, sn) +-- local code, ret = location.getDistrictList(accesskey, secretkey) -- print(code, ret) --- local code, ret = location.getDistrictChildren(accesskey, sn, 110000) +-- local code, ret = location.getDistrictChildren(accesskey, secretkey, 110000) -- print(code, ret) --- local code, ret = location.searchDistrict(accesskey, sn, "香格里拉") +-- local code, ret = location.searchDistrict(accesskey, secretkey, "香格里拉") -- print(code, ret) --- local code, ret = location.searchSuggestion(accesskey, sn, "盐津铺子", "广州", 1) +-- local code, ret = location.searchSuggestion(accesskey, secretkey, "盐津铺子", "广州", 1) -- print(code, ret) --- local code, ret = location.getDistance(accesskey, sn, { --- mode = "walking", from = "39.071510,117.190091", to="39.840177,116.463318" +-- local code, ret = location.getDistance(accesskey, secretkey, { +-- mode = "walking", from = "39.071510,117.190091", to="39.840177,116.463318" -- }) -- print(code, ret) --- local code, ret = location.searchPlace(accesskey, sn, "长沙", "region(湖南,0)") +-- local code, ret = location.searchPlace(accesskey, secretkey, "长沙", "region(湖南,0)") +-- print(code, ret) + + +local amap = require "cloud.location.amap" + +--[[ + 高德定位服务 + 使用详情请参考: lualib/cloud/location/amap.lua +]] + +-- local accesskey = "Your_Access_key" +-- local secretkey = "Your_Secret_Key" + +-- local code, ret = amap.ip(accesskey, secretkey, "114.114.114.114") +-- print(code, ret) + +-- local code, ret = amap.weather(accesskey, secretkey, "110101") +-- print(code, ret) + +-- local code, ret = amap.tips(accesskey, secretkey, { keywords = "三环" }) +-- print(code, ret) + +-- local code, ret = amap.district(accesskey, secretkey, { keywords = "天马山"}) +-- print(code, ret) + +-- local code, ret = amap.convert(accesskey, secretkey, "116.481499,39.990475|116.481499,39.990375", "gps") +-- print(code, ret) + +local baidu = require "cloud.location.baidu" + +--[[ + 百度定位服务 + 使用详情请参考: lualib/cloud/location/tencent.lua +]] + +-- local accesskey = "Your_Access_key" +-- local secretkey = "Your_Secret_Key" + +-- local code, ret = baidu.geosuggestion(accesskey, secretkey, { query = "天安门", region = "北京市", output = "json"}) +-- print(code, ret) + +-- local code, ret = baidu.geosearch(accesskey, secretkey, { query = "天安门", region = "北京市", output = "json"}) +-- print(code, ret) + +-- local code, ret = baidu.geoip(accesskey, secretkey, { ip = "114.114.114.114"}) +-- print(code, ret) + +-- local code, ret = baidu.timezone(accesskey, secretkey, { location = "39.934,116.387", timestamp = os.time()}) +-- print(code, ret) + +-- local code, ret = baidu.convert(accesskey, secretkey, { coords = "114.21892734521,29.575429778924", from = 1, to = 5}) +-- print(code, ret) + +-- local code, ret = baidu.parking(accesskey, secretkey, { location = "116.313064,40.048541", coordtype = "bd09ll"}) +-- print(code, ret) + +-- local code, ret = baidu.road(accesskey, secretkey, { road_name = "天河路", city = "广州市"}) -- print(code, ret) From d7b1f91d496a391b9d3a1462f751b56d084b2157 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 26 Oct 2019 20:55:06 +0800 Subject: [PATCH 413/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9html=E9=94=99?= =?UTF-8?q?=E5=88=AB=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/html/system/role/role.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/admin/html/system/role/role.html b/lualib/admin/html/system/role/role.html index 8423e910..9f0faef3 100644 --- a/lualib/admin/html/system/role/role.html +++ b/lualib/admin/html/system/role/role.html @@ -40,7 +40,7 @@ {*locale['dashboard.menu.role_manage.table.reflush']*} - +
                  From bf86bfaedf10c2781a75695e60b4672c03c689be Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 27 Oct 2019 18:11:13 +0800 Subject: [PATCH 414/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9view.template?= =?UTF-8?q?=E7=9A=84=E4=B8=80=E4=BA=9B=E5=88=B7=E6=96=B0=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/view.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lualib/admin/view.lua b/lualib/admin/view.lua index a8fb31ac..380fc0ef 100644 --- a/lualib/admin/view.lua +++ b/lualib/admin/view.lua @@ -68,6 +68,12 @@ function view.get_cdn () return config.cdn end -view.template = template.compile +-- 模板渲染 +function view.template( ... ) + if not config.cache then + template.cache = {} + end + return template.compile(...) +end return view From 5056d2eb565e283f889e529fb3a54668463b7cc1 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 31 Oct 2019 03:17:46 +0800 Subject: [PATCH 415/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0admin=E5=BA=93?= =?UTF-8?q?=E6=B8=B2=E6=9F=93dashboard=E7=9A=84=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/http/dashboard.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/lualib/admin/http/dashboard.lua b/lualib/admin/http/dashboard.lua index 85bf50ba..a3359723 100644 --- a/lualib/admin/http/dashboard.lua +++ b/lualib/admin/http/dashboard.lua @@ -126,6 +126,7 @@ function dashboard.render(content) logo = config.dashboard, menus = get_menus(db, {is_admin = user.is_admin, roles = user.roles}), headers = get_headers(db), + token = user.token, username = user.name, is_admin = user.is_admin, logout = config.dashboard .."?logout=true", From 248ed478f5abb71d23ac0f66f19b4728674d92a7 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 31 Oct 2019 06:41:59 +0800 Subject: [PATCH 416/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=85=BE=E8=AE=AFAI?= =?UTF-8?q?=E5=B9=B3=E5=8F=B0=E9=83=A8=E5=88=86=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/cloud/tencent/ai.lua | 66 +++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 lualib/cloud/tencent/ai.lua diff --git a/lualib/cloud/tencent/ai.lua b/lualib/cloud/tencent/ai.lua new file mode 100644 index 00000000..869b6f34 --- /dev/null +++ b/lualib/cloud/tencent/ai.lua @@ -0,0 +1,66 @@ +local httpc = require "httpc" +local crypt = require "crypt" +local md5 = crypt.md5 +local urlencode = crypt.urlencode + +local modf = math.modf +local now = require"sys".now +local time = os.time +local fmt = string.format + +local sort = table.sort +local concat = table.concat + + +local function sign(AppID, AppKey, opt) + opt["app_id"] = AppID + opt["time_stamp"] = time() + opt['nonce_str'] = fmt("%x", modf(now() * 100)) + opt["sign"] = "" + local keys = {} + for k, v in pairs(opt) do + keys[#keys+1] = k + end + sort(keys) + local args = {} + local sign_list = {} + for _, key in ipairs(keys) do + local k, v = key, opt[key] + if v ~= '' then + args[#args+1] = {k, v} + sign_list[#sign_list+1] = k .. '=' .. urlencode(v) + end + end + sign_list[#sign_list+1] = "app_key=" .. AppKey + args[#args+1] = {"sign", md5(concat(sign_list, "&"), true):upper()} + return args +end + +local ai = { __Version__ = 0.1, host = "https://api.ai.qq.com"} + +-- 智能闲聊 +function ai.chat(AppID, AppKey, session, text) + return httpc.post(ai.host .. "/fcgi-bin/nlp/nlp_textchat", nil, sign(AppID, AppKey, { session = session, question = text })) +end + +-- 文本转换为语音 +function ai.text_to_voice(AppID, AppKey, opt) + return httpc.post(ai.host .. "/fcgi-bin/aai/aai_tts", nil, sign(AppID, AppKey, opt)) +end + +-- 语种识别 +function ai.text_detect(AppID, AppKey, force, langs, text) + return httpc.post(ai.host .. "/fcgi-bin/nlp/nlp_textdetect", nil, sign(AppID, AppKey, { force = force or 0, candidate_langs = langs or "zh|en|jp|kr", text = text })) +end + +-- 文本翻译 +function ai.text_translate(AppID, AppKey, lang_code, text) + return httpc.post(ai.host .. "/fcgi-bin/nlp/nlp_texttrans", nil, sign(AppID, AppKey, { type = lang_code, text = text })) +end + +-- 语音翻译 +function ai.voice_translate(AppID, AppKey, lang_code, text) + return httpc.post(ai.host .. "/fcgi-bin/nlp/nlp_texttrans", nil, sign(AppID, AppKey, { type = lang_code, text = text })) +end + +return ai \ No newline at end of file From b2736219ed4d47b8c38670dd70794f38c4ddc710 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 31 Oct 2019 07:14:11 +0800 Subject: [PATCH 417/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0print-js=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/css/print.min.css | 1 + static/js/print.min.js | 1 + 2 files changed, 2 insertions(+) create mode 100644 static/css/print.min.css create mode 100644 static/js/print.min.js diff --git a/static/css/print.min.css b/static/css/print.min.css new file mode 100644 index 00000000..78412d78 --- /dev/null +++ b/static/css/print.min.css @@ -0,0 +1 @@ +.printModal{font-family:sans-serif;display:flex;text-align:center;font-weight:300;font-size:30px;left:0;top:0;position:absolute;color:#0460b5;width:100%;height:100%;background-color:hsla(0,0%,100%,.91)}.printClose{position:absolute;right:10px;top:10px}.printClose:before{content:"\00D7";font-family:Helvetica Neue,sans-serif;font-weight:100;line-height:1px;padding-top:.5em;display:block;font-size:2em;text-indent:1px;overflow:hidden;height:1.25em;width:1.25em;text-align:center;cursor:pointer} \ No newline at end of file diff --git a/static/js/print.min.js b/static/js/print.min.js new file mode 100644 index 00000000..91be90e5 --- /dev/null +++ b/static/js/print.min.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.printJS=t():e.printJS=t()}(window,function(){return function(n){var r={};function o(e){if(r[e])return r[e].exports;var t=r[e]={i:e,l:!1,exports:{}};return n[e].call(t.exports,t,t.exports,o),t.l=!0,t.exports}return o.m=n,o.c=r,o.d=function(e,t,n){o.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},o.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.t=function(t,e){if(1&e&&(t=o(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(o.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)o.d(n,r,function(e){return t[e]}.bind(null,r));return n},o.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(t,"a",t),t},o.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},o.p="",o(o.s=4)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r,o=n(2),i=(r=o)&&r.__esModule?r:{default:r},a=n(1);var l={send:function(r,e){document.getElementsByTagName("body")[0].appendChild(e);var o=document.getElementById(r.frameId);o.onload=function(){if("pdf"!==r.type){var e=o.contentWindow||o.contentDocument;if(e.document&&(e=e.document),e.body.appendChild(r.printableElement),"pdf"!==r.type&&r.style){var t=document.createElement("style");t.innerHTML=r.style,e.head.appendChild(t)}var n=e.getElementsByTagName("img");0'+e+"
                  "},t.capitalizePrint=function(e){return e.charAt(0).toUpperCase()+e.slice(1)},t.collectStyles=function(e,t){var n=document.defaultView||window,r="",o=n.getComputedStyle(e,"");return Object.keys(o).map(function(e){(-1!==t.targetStyles.indexOf("*")||-1!==t.targetStyle.indexOf(o[e])||function(e,t){for(var n=0;n]*>(.*?)").test(e)}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r={isFirefox:function(){return"undefined"!=typeof InstallTrigger},isIE:function(){return-1!==navigator.userAgent.indexOf("MSIE")||!!document.documentMode},isEdge:function(){return!r.isIE()&&!!window.StyleMedia},isChrome:function(){return!!(0"+t.documentTitle+"",t.css&&(Array.isArray(t.css)||(t.css=[t.css]),t.css.forEach(function(e){o.srcdoc+=''})),o.srcdoc+=""),t.type){case"pdf":if(a.default.isFirefox()||a.default.isEdge()||a.default.isIE())try{if(console.info("PrintJS currently doesn't support PDF printing in Firefox, Internet Explorer and Edge."),!0===t.onBrowserIncompatible())window.open(t.fallbackPrintable,"_blank").focus(),t.onPdfOpen&&t.onPdfOpen()}catch(e){t.onError(e)}finally{t.showModal&&l.default.close(),t.onLoadingEnd&&t.onLoadingEnd()}else d.default.print(t,o);break;case"image":f.default.print(t,o);break;case"html":u.default.print(t,o);break;case"raw-html":c.default.print(t,o);break;case"json":s.default.print(t,o)}}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r,o=n(0),i=(r=o)&&r.__esModule?r:{default:r},a=n(1);function l(e,t,n){var r=new window.Blob([n],{type:"application/pdf"});r=window.URL.createObjectURL(r),t.setAttribute("src",r),i.default.send(e,t)}t.default={print:function(e,t){if(e.base64){var n=Uint8Array.from(atob(e.printable),function(e){return e.charCodeAt(0)});l(e,t,n)}else{e.printable=/^(blob|http)/i.test(e.printable)?e.printable:window.location.origin+("/"!==e.printable.charAt(0)?"/"+e.printable:e.printable);var r=new window.XMLHttpRequest;r.responseType="arraybuffer",r.addEventListener("load",function(){if(-1===[200,201].indexOf(r.status))return(0,a.cleanUp)(e),void e.onError(r.statusText);l(e,t,r.response)}),r.open("GET",e.printable,!0),r.send()}}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r,f=n(1),o=n(0),i=(r=o)&&r.__esModule?r:{default:r};t.default={print:function(e,t){var n=document.getElementById(e.printable);n?(e.printableElement=function e(t,n){var r=t.cloneNode();var o=!0;var i=!1;var a=void 0;try{for(var l,d=t.childNodes[Symbol.iterator]();!(o=(l=d.next()).done);o=!0){var u=l.value;if(-1===n.ignoreElements.indexOf(u.id)){var c=e(u,n);r.appendChild(c)}}}catch(e){i=!0,a=e}finally{try{!o&&d.return&&d.return()}finally{if(i)throw a}}n.scanStyles&&1===t.nodeType&&r.setAttribute("style",(0,f.collectStyles)(t,n));switch(t.tagName){case"SELECT":r.value=t.value;break;case"CANVAS":r.getContext("2d").drawImage(t,0,0)}return r}(n,e),e.header&&(0,f.addHeader)(e.printableElement,e),i.default.send(e,t)):window.console.error("Invalid HTML element id: "+e.printable)}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r,o=n(0),i=(r=o)&&r.__esModule?r:{default:r};t.default={print:function(e,t){e.printableElement=document.createElement("div"),e.printableElement.setAttribute("style","width:100%"),e.printableElement.innerHTML=e.printable,i.default.send(e,t)}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r,o=n(1),i=n(0),a=(r=i)&&r.__esModule?r:{default:r};t.default={print:function(r,e){r.printable.constructor!==Array&&(r.printable=[r.printable]),r.printableElement=document.createElement("div"),r.printable.forEach(function(e){var t=document.createElement("img");t.setAttribute("style",r.imageStyle),t.src=e;var n=document.createElement("div");n.appendChild(t),r.printableElement.appendChild(n)}),r.header&&(0,o.addHeader)(r.printableElement,r),a.default.send(r,e)}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r,o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},c=n(1),i=n(0),a=(r=i)&&r.__esModule?r:{default:r};t.default={print:function(t,e){if("object"!==o(t.printable))throw new Error("Invalid javascript data object (JSON).");if("boolean"!=typeof t.repeatTableHeader)throw new Error("Invalid value for repeatTableHeader attribute (JSON).");if(!t.properties||!Array.isArray(t.properties))throw new Error("Invalid properties array for your JSON data.");t.properties=t.properties.map(function(e){return{field:"object"===(void 0===e?"undefined":o(e))?e.field:e,displayName:"object"===(void 0===e?"undefined":o(e))?e.displayName:e,columnSize:"object"===(void 0===e?"undefined":o(e))&&e.columnSize?e.columnSize+";":100/t.properties.length+"%;"}}),t.printableElement=document.createElement("div"),t.header&&(0,c.addHeader)(t.printableElement,t),t.printableElement.innerHTML+=function(e){var t=e.printable,n=e.properties,r='';e.repeatTableHeader&&(r+="");r+="";for(var o=0;o'+(0,c.capitalizePrint)(n[o].displayName)+"";r+="",e.repeatTableHeader&&(r+="");r+="";for(var i=0;i";for(var a=0;a'+l+""}r+=""}return r+="
                  "}(t),a.default.send(t,e)}}}]).default}); \ No newline at end of file From bba0d4d7b56bde1a60ea1b9e13e71a025b014563 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 3 Nov 2019 14:38:23 +0800 Subject: [PATCH 418/956] =?UTF-8?q?=E4=BC=98=E5=8C=96httpd=E8=B7=AF?= =?UTF-8?q?=E7=94=B1=E6=9F=A5=E6=89=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Router.lua | 83 ++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 43 deletions(-) diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index c8915d6c..6defab5b 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -6,7 +6,6 @@ local url_decode = url.decode local new_tab = require("sys").new_tab -local math = math local string = string local split = string.sub local find = string.find @@ -16,13 +15,10 @@ local spliter = string.gsub local type = type local next = next -local assert = assert local ipairs = ipairs local tonumber = tonumber local tostring = tostring local io_open = io.open -local concat = table.concat -local toint = math.tointeger local Router = { API = 1, @@ -35,13 +31,7 @@ local routes = {} -- 存储路由 local static = {} -- 静态文件路由 -local typ = { - int = toint, - float = tonumber, - string = tostring, -} - --- 分割路径后进行hex, 得到key后一次查表即可完成 +-- 主要用作分割路径判断. local function hex_route(route) local tab = new_tab(32, 0) for r in splite(route, '/([^/%?]+)') do @@ -50,6 +40,11 @@ local function hex_route(route) return tab end +-- 主要用作分割hash路由查找 +local function to_route(route) + return spliter(route, "/", "") +end + -- 检查是路径回退是否超出静态文件根目录 local function check_path_deep (paths) -- 判断是否/.. @@ -94,46 +89,48 @@ end local load_file local function registery_router (route, class, route_type) - routes[concat(hex_route(route))] = {class = class, type = route_type} + routes[to_route(route)] = {class = class, type = route_type} end local function find_route (method, path) - local tab = hex_route(split(path, 1, (find(path, '?') or 0) - 1)) - local hex = concat(tab) - local t = routes[hex] - if t then - return t.class, t.type - end - local prefix, type = static.prefix, static.type - if not prefix and not type then - return - end - -- 非GET/HEAD方法不查找静态文件 - if method ~= 'GET' and method ~= 'HEAD' then - return - end - -- 凡是找到'../'并且检查路径回退已经超出静态文件根目录返回404 - if find(path, '/../', 1, true) and check_path_deep(tab) then - return - end - load_file = load_file or function (path) - local filepath = prefix..url_decode(path) - -- 使用r+测试是否可读可写; 如果filepath是目录则无法被打开, 但单独的r模式可以. - local f, error = io_open(filepath, 'r+') - if not f then - return - end - local body_len = f:seek("end") - f:close() - return body_len, filepath, match(path, '.+%.([%a]+)') - end - return load_file, type + path = split(path, 1, (find(path, '?') or 0) - 1) + local t = routes[to_route(path)] + if t then + return t.class, t.type + end + local prefix, typ = static.prefix, static.type + if not prefix and not typ then + return + end + -- 非GET/HEAD方法不查找静态文件 + if method ~= 'GET' and method ~= 'HEAD' then + return + end + local tab = hex_route(path) + -- 凡是找到'../'并且检查路径回退已经超出静态文件根目录返回404 + if find(path, '/../', 1, true) and check_path_deep(tab) then + return + end + if not load_file then + load_file = function (path) + local filepath = prefix .. url_decode(path) + -- 使用r+测试是否可读可写; 如果filepath是目录则无法被打开, 但单独的r模式可以. + local f, error = io_open(filepath, 'r+') + if not f then + return + end + local body_len = f:seek("end") + f:close() + return body_len, filepath, match(path, '.+%.([%a]+)') + end + end + return load_file, typ end -- 查找路由 function Router.find(method, path) -- 凡是不以'/'开头的path都返回404 - if not find(path, '^(/)') then + if not find(path, '^/') then return end return find_route(method, path) From 13f7d3a3e22a99377875c11b3b86fd391ad99d24 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 4 Nov 2019 05:33:13 +0800 Subject: [PATCH 419/956] =?UTF-8?q?=E7=AE=80=E5=8C=96httpd=E8=B7=AF?= =?UTF-8?q?=E7=94=B1=E6=9F=A5=E6=89=BE=E4=B8=8E=E8=B7=AF=E5=BE=84=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E7=9A=84=E7=9B=B8=E5=85=B3=E4=BB=A3=E7=A0=81,=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B3=A8=E5=86=8Cutf8=E4=B8=AD=E6=96=87?= =?UTF-8?q?=E8=B7=AF=E7=94=B1.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Router.lua | 71 ++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index 6defab5b..f036dc9e 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -3,10 +3,12 @@ local Log = log:new({dump = true, path = 'httpd-Router'}) local url = require "url" local url_decode = url.decode +-- local url_encode = url.encode local new_tab = require("sys").new_tab local string = string +local byte = string.byte local split = string.sub local find = string.find local match = string.match @@ -20,6 +22,11 @@ local tonumber = tonumber local tostring = tostring local io_open = io.open +local slash = '\x2f' -- '/' +local slash2 = '\x2f\x2f' -- '//' +local point = '\x2e' -- '.' +local point2 = '\x2e\x2e' -- '..' + local Router = { API = 1, USE = 2, @@ -34,7 +41,7 @@ local static = {} -- 静态文件路由 -- 主要用作分割路径判断. local function hex_route(route) local tab = new_tab(32, 0) - for r in splite(route, '/([^/%?]+)') do + for r in splite(route, '/([^ /%?]+)') do tab[#tab + 1] = r end return tab @@ -42,35 +49,25 @@ end -- 主要用作分割hash路由查找 local function to_route(route) - return spliter(route, "/", "") + return spliter(route, slash, '') end -- 检查是路径回退是否超出静态文件根目录 local function check_path_deep (paths) - -- 判断是否/.. - local head = paths[1] - if head == '..' then - return true - end - -- 判断是否/./.. - local second = paths[2] - if second == '..' and head == '.' then - return true - end - -- 结尾是特殊文件[夹]直接返回. - local tail = paths[#paths] - if tail == '.' or tail == '..' then - return true - end + -- 检查是否合法路径. + local head, tail = paths[1], paths[#paths] + if head == point2 or tail == point or tail == point2 then + return true + end local deep = 1 - for index, path in ipairs(paths) do - if path == '..' then - deep = deep - 1 - elseif path == '.' then - deep = deep + 0 - else - deep = deep + 1 - end + for _, path in ipairs(paths) do + if path ~= point then + if path == point2 then + deep = deep - 1 + else + deep = deep + 1 + end + end if deep <= 0 then return true end @@ -79,11 +76,11 @@ local function check_path_deep (paths) end local function registery_static (prefix, route_type) - if not next(static) then - static.prefix = prefix - static.type = route_type - end - return + if next(static) then + return + end + static.prefix = prefix + static.type = route_type end local load_file @@ -93,7 +90,7 @@ local function registery_router (route, class, route_type) end local function find_route (method, path) - path = split(path, 1, (find(path, '?') or 0) - 1) + path = url_decode(split(path, 1, (find(path, '?') or 0) - 1)) local t = routes[to_route(path)] if t then return t.class, t.type @@ -108,18 +105,18 @@ local function find_route (method, path) end local tab = hex_route(path) -- 凡是找到'../'并且检查路径回退已经超出静态文件根目录返回404 - if find(path, '/../', 1, true) and check_path_deep(tab) then + if check_path_deep(tab) then return end if not load_file then load_file = function (path) local filepath = prefix .. url_decode(path) -- 使用r+测试是否可读可写; 如果filepath是目录则无法被打开, 但单独的r模式可以. - local f, error = io_open(filepath, 'r+') + local f, _ = io_open(filepath, 'r+') if not f then return end - local body_len = f:seek("end") + local body_len = f:seek('end') f:close() return body_len, filepath, match(path, '.+%.([%a]+)') end @@ -130,7 +127,7 @@ end -- 查找路由 function Router.find(method, path) -- 凡是不以'/'开头的path都返回404 - if not find(path, '^/') then + if byte(path) ~= byte(slash) then return end return find_route(method, path) @@ -146,10 +143,10 @@ function Router.registery(route, class, route_type) if type(route) ~= 'string' or route == '' then -- 过滤错误的路由输入 return Log:WARN('Please Do not add empty string in route registery method :)') end - if find(route, '//') then -- 不允许出现路由出现[//] + if find(route, slash2) then -- 不允许出现路由出现[//] return Log:WARN('Please Do not add [//] in route registery method :)') end - if find(route, '^/%[%w+:.+%]$') then -- 不允许顶层路由注册rest模式. + if find(route, '^/%[%w+:.+%]$') then -- 不允许路由注册rest模式. return Log:WARN('Please Do not add [/[type:key] in root route :)]') end return registery_router(route, class, route_type) From adfa5b98ca8efd0d1955e6a9c83a081f6de424f6 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 4 Nov 2019 05:33:43 +0800 Subject: [PATCH 420/956] =?UTF-8?q?=E5=AE=8C=E5=96=84Keep-Alive=E5=A4=B4?= =?UTF-8?q?=E9=83=A8=E7=9A=84=E4=B8=80=E4=BA=9B=E7=A1=AC=E7=BC=96=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index d870a0d9..0c87f0ca 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -268,7 +268,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) local buffers = {} local ttl = http.ttl local server = http.__server - local timeout = http.__timeout or 30 + local timeout = http.__timeout or 0 local cookie = http.__cookie local cookie_secure = http.__cookie_secure local before_func = http._before_func @@ -468,7 +468,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) end header[#header+1] = Connection if Connection == 'Connection: keep-alive' then - header[#header+1] = "Keep-Alive: timeout="..timeout + header[#header+1] = "Keep-Alive: timeout="..(timeout <= 0 and 86400 or timeout)..', max='..(1 << 12) end if typ == HTTP_PROTOCOL.API or typ == HTTP_PROTOCOL.USE then if typ == HTTP_PROTOCOL.API then From 9aa18839a67e8f74240a430968688b7e8d98dd5d Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 4 Nov 2019 07:28:02 +0800 Subject: [PATCH 421/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AE=9A=E6=97=B6?= =?UTF-8?q?=E5=99=A8=E7=9B=B8=E5=85=B3=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/Timer.lua | 178 +++++++++++++++++--------------------- 1 file changed, 80 insertions(+), 98 deletions(-) diff --git a/lualib/internal/Timer.lua b/lualib/internal/Timer.lua index 5fb7bd3c..8aa12d22 100644 --- a/lualib/internal/Timer.lua +++ b/lualib/internal/Timer.lua @@ -12,7 +12,10 @@ local co_spwan = co.spwan local co_wakeup = co.wakeup local co_self = co.self -local insert = table.insert +local co_self_ex = coroutine.running +local co_wait_ex = coroutine.yield +local co_wakeup_ex = coroutine.resume + local remove = table.remove local Timer = {} @@ -21,123 +24,102 @@ local TIMER_LIST = {} -- 内部函数防止被误用 local function Timer_new() - if #TIMER_LIST > 0 then - return remove(TIMER_LIST) - end - return ti_new() + if #TIMER_LIST > 0 then + return remove(TIMER_LIST) + end + return ti_new() end local function Timer_release(t) - ti_stop(t) - insert(TIMER_LIST, t) + ti_stop(t) + TIMER_LIST[#TIMER_LIST+1] = t end -function Timer.count( ... ) - return #TIMER_LIST +local function Timer_stop(self) + if self and not self.stoped then + self.stoped = true + if self.t then + Timer_release(self.t) + self.t = nil + end + Timer[self] = nil + end +end + +function Timer.count() + return #TIMER_LIST end -- 超时器 -- function Timer.timeout(timeout, cb) - if type(timeout) ~= 'number' or timeout <= 0 then - return - end - if type(cb) ~= 'function' then + if type(timeout) ~= 'number' or timeout <= 0 then + return + end + if type(cb) ~= 'function' then + return + end + local t = Timer_new() + if not t then + return + end + local once = {stoped = false, timeout = timeout, stop = Timer_stop, t = t, co = nil} + once.co = co_new(function( ... ) + if once.stoped then return end - local t = Timer_new() - if not t then - return - end - local timer = {STOP = false} - timer.stop = function (...) - if timer.STOP then - return - end - Timer_release(t) - timer.STOP = true - timer.co = nil - Timer[timer] = nil - end - timer.co = co_new(function (...) - if timer.STOP then - return - end - Timer_release(t) - co_spwan(cb) - if timer.STOP then - return - end - Timer[timer] = nil - timer.STOP = true - timer.co = nil - end) - Timer[timer] = timer - ti_start(t, timeout, timer.co) - return timer + co_spwan(cb) + Timer_stop(once) + end) + Timer[once] = once + ti_start(t, timeout, once.co) + return once end -- 循环定时器 -- function Timer.at(repeats, cb) - if type(repeats) ~= 'number' or repeats <= 0 then - return - end - if type(cb) ~= 'function' then - return - end - local t = Timer_new() - if not t then + if type(repeats) ~= 'number' or repeats <= 0 then + return + end + if type(cb) ~= 'function' then + return + end + local t = Timer_new() + if not t then + return + end + local at = {stoped = false, repeats = repeats, stop = Timer_stop, t = t, co = nil} + at.co = co_new(function( ... ) + while 1 do + if at.stoped then return + end + co_spwan(cb) + co_wait_ex() end - local timer = { STOP = false } - timer.stop = function (...) - if timer.STOP then - return - end - Timer_release(t) - timer.STOP = true - timer.co = nil - Timer[timer] = nil - end - timer.co = co_new(function () - local co_wait = coroutine.yield - while 1 do - if timer.STOP then - return - end - co_spwan(cb) - if timer.STOP then - return - end - co_wait() - end - end) - Timer[timer] = timer - ti_start(t, repeats, timer.co) - return timer + end) + Timer[at] = at + ti_start(t, repeats, at.co) + return at end -- 休眠 -- -function Timer.sleep(repeats) - if type(repeats) ~= 'number' or repeats <= 0 then - return - end - local t = Timer_new() - if not t then - return - end - local timer = {} - timer.current_co = co_self() - timer.co = co_new(function (...) - local current_co = timer.current_co - Timer[timer] = nil - timer.current_co = nil - timer.co = nil - Timer_release(t) - return co_wakeup(current_co) - end) - Timer[timer] = timer - ti_start(t, repeats, timer.co) - return co_wait() +function Timer.sleep(timeout) + if type(timeout) ~= 'number' or timeout <= 0 then + return + end + local t = Timer_new() + if not t then + return + end + local sleep = {stoped = false, stop = Timer_stop, t = t } + local co = co_self() + sleep.co = co_new(function (...) + Timer_stop(sleep) + return co_wakeup(co) + end) + Timer[sleep] = sleep + ti_start(t, timeout, sleep.co) + return co_wait() end return Timer From a22dce1114c33daa13860444c3f1f0948474c2ea Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 4 Nov 2019 14:22:00 +0800 Subject: [PATCH 422/956] =?UTF-8?q?=E4=BC=98=E5=8C=96Timer=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/Timer.lua | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lualib/internal/Timer.lua b/lualib/internal/Timer.lua index 8aa12d22..14d34e93 100644 --- a/lualib/internal/Timer.lua +++ b/lualib/internal/Timer.lua @@ -35,6 +35,11 @@ local function Timer_release(t) TIMER_LIST[#TIMER_LIST+1] = t end +local function Timer_start(self) + Timer[self] = self + ti_start(self.t, self.timeout or self.repeats, self.co) +end + local function Timer_stop(self) if self and not self.stoped then self.stoped = true @@ -70,8 +75,7 @@ function Timer.timeout(timeout, cb) co_spwan(cb) Timer_stop(once) end) - Timer[once] = once - ti_start(t, timeout, once.co) + Timer_start(once) return once end @@ -97,8 +101,7 @@ function Timer.at(repeats, cb) co_wait_ex() end end) - Timer[at] = at - ti_start(t, repeats, at.co) + Timer_start(at) return at end @@ -111,14 +114,13 @@ function Timer.sleep(timeout) if not t then return end - local sleep = {stoped = false, stop = Timer_stop, t = t } + local sleep = {stoped = false, timeout = timeout, t = t, co = nil } local co = co_self() sleep.co = co_new(function (...) Timer_stop(sleep) return co_wakeup(co) end) - Timer[sleep] = sleep - ti_start(t, timeout, sleep.co) + Timer_start(sleep) return co_wait() end From 56854a7987d73e5c2d21683c20ade91bae131031 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 5 Nov 2019 08:40:59 +0800 Subject: [PATCH 423/956] =?UTF-8?q?=E5=90=AF=E5=8A=A8=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96=E9=A2=84=E5=88=86=E9=85=8D=E5=86=85=E5=AD=98,=20?= =?UTF-8?q?=E6=8F=90=E5=8D=87=E8=BF=90=E8=A1=8C=E6=95=88=E7=8E=87.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/Co.lua | 7 ++++--- lualib/internal/TCP.lua | 4 +++- lualib/internal/Timer.lua | 6 ++++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lualib/internal/Co.lua b/lualib/internal/Co.lua index d7b8c8b2..fec55507 100644 --- a/lualib/internal/Co.lua +++ b/lualib/internal/Co.lua @@ -1,4 +1,5 @@ local task = require "task" +local new_tab = require("sys").new_tab local task_new = task.new local task_stop = task.stop @@ -18,12 +19,12 @@ local error = error local insert = table.insert local remove = table.remove -local cos = {} +local cos = new_tab(0, 1 << 10) local main_co = co_self() local main_task = task_new() -local TASK_POOL = {} +local TASK_POOL = new_tab(1 << 10, 0) local function task_pop() if #TASK_POOL > 0 then @@ -36,7 +37,7 @@ local function task_push(task) return insert(TASK_POOL, task) end -local CO_POOL = {} +local CO_POOL = new_tab(1 << 10, 0) local function co_pop(func) if #CO_POOL > 0 then diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index ba85a779..00b927fc 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -1,5 +1,7 @@ local class = require "class" +local new_tab = require("sys").new_tab + local log = require "logging" local Log = log:new({ dump = true, path = 'internal-TCP' }) @@ -42,7 +44,7 @@ local tcp_new_server_fd = tcp.new_server_fd local EVENT_READ = 0x01 local EVENT_WRITE = 0x02 -local POOL = {} +local POOL = new_tab(1 << 10, 0) local function tcp_pop() if #POOL > 0 then return remove(POOL) diff --git a/lualib/internal/Timer.lua b/lualib/internal/Timer.lua index 14d34e93..fdc4c579 100644 --- a/lualib/internal/Timer.lua +++ b/lualib/internal/Timer.lua @@ -1,6 +1,8 @@ local co = require "internal.Co" local ti = require "timer" +local new_tab = require("sys").new_tab + local type = type local ti_new = ti.new local ti_start = ti.start @@ -18,9 +20,9 @@ local co_wakeup_ex = coroutine.resume local remove = table.remove -local Timer = {} +local Timer = new_tab(0, 1 << 10) -local TIMER_LIST = {} +local TIMER_LIST = new_tab(1 << 10, 0) -- 内部函数防止被误用 local function Timer_new() From c66c65eb8af05ef222f79e473ffbfee473e05841 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 5 Nov 2019 08:42:06 +0800 Subject: [PATCH 424/956] =?UTF-8?q?=E4=BC=98=E5=8C=96HTTP=E5=A4=B4?= =?UTF-8?q?=E9=83=A8=E7=9A=84X-ForWarded-For=E5=A4=84=E7=90=86=E6=96=B9?= =?UTF-8?q?=E6=B3=95.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index 0c87f0ca..31646668 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -51,6 +51,7 @@ local concat = table.concat local CRLF = '\x0d\x0a' local CRLF2 = '\x0d\x0a\x0d\x0a' local RE_CRLF2 = '[\x0d]?\x0a[\x0d]?\x0a' +local COMMA = '\x2c' local SERVER = 'cf web/0.1' @@ -107,7 +108,7 @@ local function HTTP_EXPIRES(timestamp) end local function PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEADER) - local content = {} + local content = new_tab(0, 8) if METHOD == "GET" then local spl_pos = find(PATH, '%?') if spl_pos and spl_pos < #PATH then @@ -184,21 +185,11 @@ local function PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEA return true, content end -local function X_Forwarded_FORMAT(tab) - local ip_list - if tab and type(tab) == 'string' then - for ip in splite(tab, '([^ ,;]+)') do - if not ip_list then - ip_list = {ip} - else - if ip ~= ip_list[#ip_list] then - ip_list[#ip_list+1] = ip - end - end - end - return concat(ip_list, ' -> ') +local function X_Forwarded_FORMAT(ip_list) + if find(ip_list, ',') then + return ip_list:gsub(COMMA, "->") end - return tab + return ip_list end -- 一些错误返回 local function ERROR_RESPONSE(http, code, path, ip, forword, method, speed) @@ -393,7 +384,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) end end - local header = { } + local header = new_tab(16, 0) local ok, body, body_len, filepath, static, statucode if typ == HTTP_PROTOCOL.API or typ == HTTP_PROTOCOL.USE then From f1b8ce049c4a9d5493bc4c96763e4f612901caac Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 5 Nov 2019 09:26:56 +0800 Subject: [PATCH 425/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index dfb55a26..5e3ebbfb 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -99,11 +99,13 @@ void SETSOCKETOPT(int sockfd, int mode){ /* 开启IPV6与ipv4双栈 */ #ifdef IPV6_V6ONLY - int No = 0; - ret = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&No, sizeof(No)); - if (ret){ - LOG("ERROR", "IPV6_V6ONLY 关闭失败."); - return _exit(-1); + if (mode == SERVER) { + int No = 0; + ret = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&No, sizeof(No)); + if (ret){ + LOG("ERROR", "IPV6_V6ONLY 设置失败."); + return _exit(-1); + } } #endif @@ -236,7 +238,7 @@ IO_ACCEPT(CORE_P_ core_io *io, int revents){ LOG("INFO", strerror(errno)); return ; } - non_blocking(client); //在某些平台下, 这个socket是阻塞的. + SETSOCKETOPT(client, CLIENT); lua_State *co = (lua_State *) core_get_watcher_userdata(io); if (lua_status(co) == LUA_YIELD || lua_status(co) == LUA_OK){ char buf[INET6_ADDRSTRLEN]; From c6f34e912b51695b1cfa9f277d1a45266ab6b542 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 5 Nov 2019 09:27:01 +0800 Subject: [PATCH 426/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_sys.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core_sys.h b/src/core_sys.h index 70030c64..41b02d62 100644 --- a/src/core_sys.h +++ b/src/core_sys.h @@ -49,6 +49,8 @@ #define non_blocking(socket) (fcntl(socket, F_SETFL, fcntl(socket, F_GETFL, 0) | O_NONBLOCK)); +#define non_delay(socket) ({int Enable = 1; setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, &Enable, sizeof(Enable));}) + /* [datetime][level][file][function][line][具体打印内容] */ #define LOG(LEVEL, CONTENT) { \ time_t t = time(NULL); struct tm* lt = localtime(&t); \ From 7a0da014614142b666c9c02cae623978f527a4ad Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 5 Nov 2019 19:02:00 +0800 Subject: [PATCH 427/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=E5=A4=B4=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_sys.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core_sys.h b/src/core_sys.h index 41b02d62..80b66524 100644 --- a/src/core_sys.h +++ b/src/core_sys.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include From 104bf2ee34f321f3e54d917d8b9720178a686798 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 5 Nov 2019 21:21:48 +0800 Subject: [PATCH 428/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0unix=20domain=20socke?= =?UTF-8?q?t=20listen=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 179 ++++++++++++++++++++++++++++++++++------ lualib/internal/TCP.lua | 51 +++++++++--- 2 files changed, 191 insertions(+), 39 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 5e3ebbfb..de5b7728 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -1,12 +1,13 @@ #define LUA_LIB +#include "../../src/core.h" #include #include #include -#include "../../src/core.h" -#define SERVER 0 -#define CLIENT 1 +#define None (-1) +#define SERVER (0) +#define CLIENT (1) static inline void SETSOCKETOPT(int sockfd, int mode){ @@ -49,10 +50,12 @@ void SETSOCKETOPT(int sockfd, int mode){ /* 开启 TCP keepalive */ #ifdef SO_KEEPALIVE - ret = setsockopt(sockfd, IPPROTO_TCP, SO_KEEPALIVE, &Enable , sizeof(Enable)); - if (ret){ - LOG("ERROR", "SO_KEEPALIVE 设置失败."); - return _exit(-1); + if (mode != None){ + ret = setsockopt(sockfd, IPPROTO_TCP, SO_KEEPALIVE, &Enable , sizeof(Enable)); + if (ret){ + LOG("ERROR", "SO_KEEPALIVE 设置失败."); + return _exit(-1); + } } #endif @@ -99,7 +102,7 @@ void SETSOCKETOPT(int sockfd, int mode){ /* 开启IPV6与ipv4双栈 */ #ifdef IPV6_V6ONLY - if (mode == SERVER) { + if (mode != None) { int No = 0; ret = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&No, sizeof(No)); if (ret){ @@ -117,9 +120,10 @@ create_server_fd(int port, int backlog){ errno = 0; /* 建立 TCP Server Socket */ int sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); - if (0 >= sockfd) + if (0 >= sockfd){ + LOG("DEBUG", strerror(errno)); return -1; - + } /* socket option set */ SETSOCKETOPT(sockfd, SERVER); @@ -153,7 +157,10 @@ create_client_fd(const char *ipaddr, int port){ errno = 0; /* 建立 TCP Client Socket */ int sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); - if (0 >= sockfd) return -1; + if (0 >= sockfd) { + LOG("DEBUG", strerror(errno)); + return -1; + } /* socket option set */ SETSOCKETOPT(sockfd, CLIENT); @@ -179,6 +186,41 @@ create_client_fd(const char *ipaddr, int port){ return sockfd; } +static int +create_unixsock_fd(const char* path, size_t path_len, int backlog) { + errno = 0; + int sockfd = socket(AF_LOCAL, SOCK_STREAM, IPPROTO_IP); + if (0 >= sockfd){ + LOG("DEBUG", strerror(errno)); + return -1; + } + + struct sockaddr_un UN; + memset(&UN, 0x0, sizeof(UN)); + + UN.sun_family = AF_LOCAL; + memmove(UN.sun_path, path, path_len); + + non_blocking(sockfd); + + /* 绑定套接字失败 */ + int bind_success = bind(sockfd, (struct sockaddr *)&UN, sizeof(UN)); + if (0 > bind_success) { + LOG("DEBUG", strerror(errno)); + close(sockfd); + return -1; + } + + /* 监听套接字失败 */ + int listen_success = listen(sockfd, backlog); + if (0 > listen_success) { + LOG("DEBUG", strerror(errno)); + close(sockfd); + return -1; + } + + return sockfd; +} static void TCP_IO_CB(CORE_P_ core_io *io, int revents) { @@ -227,6 +269,12 @@ static void /* 接受链接 */ IO_ACCEPT(CORE_P_ core_io *io, int revents){ if (revents & EV_READ){ + lua_State *co = (lua_State *) core_get_watcher_userdata(io); + int status = lua_status(co); + if (status != LUA_YIELD && status != LUA_OK) { + LOG("ERROR", "accept get a invalid lua vm."); + return ; + } for(;;) { errno = 0; struct sockaddr_in6 SA; @@ -238,24 +286,54 @@ IO_ACCEPT(CORE_P_ core_io *io, int revents){ LOG("INFO", strerror(errno)); return ; } - SETSOCKETOPT(client, CLIENT); - lua_State *co = (lua_State *) core_get_watcher_userdata(io); - if (lua_status(co) == LUA_YIELD || lua_status(co) == LUA_OK){ - char buf[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &SA.sin6_addr, buf, INET6_ADDRSTRLEN); - lua_pushinteger(co, client); - lua_pushlstring(co, buf, strlen(buf)); - int status = CO_RESUME(co, NULL, lua_status(co) == LUA_YIELD ? lua_gettop(co) : lua_gettop(co) - 1); - if (status != LUA_YIELD && status != LUA_OK) { - LOG("ERROR", lua_tostring(co, -1)); - LOG("ERROR", "Error Lua Accept Method"); - core_io_stop(CORE_LOOP_ io); - } - } + SETSOCKETOPT(client, None); + char buf[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &SA.sin6_addr, buf, INET6_ADDRSTRLEN); + lua_pushinteger(co, client); + lua_pushlstring(co, buf, strlen(buf)); + status = CO_RESUME(co, NULL, status == LUA_YIELD ? lua_gettop(co) : lua_gettop(co) - 1); + if (status != LUA_YIELD && status != LUA_OK) { + LOG("ERROR", lua_tostring(co, -1)); + LOG("ERROR", "Error Lua Accept Method"); + core_io_stop(CORE_LOOP_ io); + } } } } +static void +IO_ACCEPT_EX(CORE_P_ core_io *io, int revents) { + if (revents & EV_READ){ + lua_State *co = (lua_State *) core_get_watcher_userdata(io); + int status = lua_status(co); + if (status != LUA_YIELD && status != LUA_OK) { + LOG("ERROR", "accept_ex get a invalid lua vm."); + return ; + } + for (;;) { + errno = 0; + struct sockaddr_un UN; + socklen_t slen = sizeof(UN); + memset(&UN, 0x0, slen); + int client = accept(io->fd, (struct sockaddr *)&UN, &slen); + if (0 >= client) { + if (errno != EWOULDBLOCK) + LOG("INFO", strerror(errno)); + return ; + } + non_blocking(client); + lua_pushinteger(co, client); + // lua_pushlstring(co, UN.sun_path, strlen(UN.sun_path)); // unix domain path + status = CO_RESUME(co, NULL, status == LUA_YIELD ? lua_gettop(co) : lua_gettop(co) - 1); + if (status != LUA_YIELD && status != LUA_OK) { + LOG("ERROR", lua_tostring(co, -1)); + LOG("ERROR", "Error Lua Accept Method"); + core_io_stop(CORE_LOOP_ io); + } + } + } +} + struct io_sendfile { uint32_t offset; uint32_t fd; @@ -508,6 +586,32 @@ new_client_fd(lua_State *L){ return 1; } +static int +new_unixsock_fd(lua_State *L) { + size_t size = 0; + const char* path = luaL_checklstring(L, 1, &size); + if (!path) + return 0; + + /* 传递rm为非nil与false值, 删除已经存在的文件 */ + int rm = lua_toboolean(L, 2); + + /* 文件存在或无法删除的情况下都将创建unixsock失败 */ + if (!access(path, F_OK)) { + if (!rm || unlink(path)) + return 0; + } + + int backlog = luaL_checkinteger(L, 3); + + int fd = create_unixsock_fd(path, size, 0 >= backlog ? 128 : backlog); + if (fd <= 0) + return 0; + + lua_pushinteger(L, fd); + return 1; +} + static int tcp_listen(lua_State *L){ core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); @@ -527,7 +631,28 @@ tcp_listen(lua_State *L){ core_io_start(CORE_LOOP_ io); - return 0; + return 1; +} + +static int +tcp_listen_ex(lua_State *L) { + core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); + if (!io) return 0; + + /* socket文件描述符 */ + int fd = lua_tointeger(L, 2); + if (0 >= fd) return 0; + /* 回调协程 */ + lua_State *co = lua_tothread(L, 3); + if (!co) return 0; + + core_set_watcher_userdata(io, co); + + core_io_init(io, IO_ACCEPT_EX, fd, EV_READ); + + core_io_start(CORE_LOOP_ io); + + return 1; } static int @@ -701,6 +826,7 @@ luaopen_tcp(lua_State *L){ {"start", tcp_start}, {"close", tcp_close}, {"listen", tcp_listen}, + {"listen_ex", tcp_listen_ex}, {"connect", tcp_connect}, {"ssl_connect", tcp_sslconnect}, {"new", tcp_new}, @@ -708,6 +834,7 @@ luaopen_tcp(lua_State *L){ {"free_ssl", ssl_free}, {"new_server_fd", new_server_fd}, {"new_client_fd", new_client_fd}, + {"new_unixsock_fd", new_unixsock_fd}, {"sendfile", tcp_sendfile}, {NULL, NULL} }; diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index 00b927fc..d3e612c7 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -35,10 +35,12 @@ local tcp_sslread = tcp.ssl_read local tcp_write = tcp.write local tcp_ssl_write = tcp.ssl_write local tcp_listen = tcp.listen +local tcp_listen_ex = tcp.listen_ex local tcp_sendfile = tcp.sendfile local tcp_new_client_fd = tcp.new_client_fd local tcp_new_server_fd = tcp.new_server_fd +local tcp_new_unixsock_fd = tcp.new_unixsock_fd local EVENT_READ = 0x01 @@ -271,20 +273,43 @@ function TCP:ssl_recv(bytes) end function TCP:listen(ip, port, cb) - self.LISTEN_IO = tcp_pop() - self.fd = tcp_new_server_fd(ip, port, self._backlog) - if not self.fd then - return nil, "Listen port failed. Please check if the port is already occupied." + self.LISTEN_IO = tcp_pop() + self.fd = tcp_new_server_fd(ip, port, self._backlog or 128) + if not self.fd then + return nil, "Listen port failed. Please check if the port is already occupied." + end + if type(cb) ~= 'function' then + return nil, "Listen_ex function was invalid." + end + self.co = co_new(function (fd, ipaddr) + while 1 do + if fd and ipaddr then + co_spwan(cb, fd, ipaddr) + fd, ipaddr = co_wait() + end end - self.co = co_new(function (fd, ipaddr) - while 1 do - if fd and ipaddr then - co_spwan(cb, fd, ipaddr) - fd, ipaddr = co_wait() - end - end - end) - return true, tcp_listen(self.LISTEN_IO, self.fd, self.co) + end) + return true, tcp_listen(self.LISTEN_IO, self.fd, self.co) +end + +function TCP:listen_ex(unix_domain_path, removed, cb) + self.LISTEN_EX_IO = tcp_pop() + self.ufd = tcp_new_unixsock_fd(unix_domain_path, removed or true, self._backlog or 128) + if not self.ufd then + return nil, "Listen_ex unix domain socket failed. Please check the domain_path was exists and access." + end + if type(cb) ~= 'function' then + return nil, "Listen_ex function was invalid." + end + self.co = co_new(function (fd) + while 1 do + if fd then + co_spwan(cb, fd, "127.0.0.1") + fd = co_wait() + end + end + end) + return true, tcp_listen_ex(self.LISTEN_EX_IO, self.ufd, self.co) end function TCP:connect(domain, port) From 0bedda02d4abb6b077f29c30657ec0bf4da6e79e Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 5 Nov 2019 22:13:46 +0800 Subject: [PATCH 429/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/TCP.lua | 115 +++++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 55 deletions(-) diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index d3e612c7..e724c88c 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -281,7 +281,7 @@ function TCP:listen(ip, port, cb) if type(cb) ~= 'function' then return nil, "Listen_ex function was invalid." end - self.co = co_new(function (fd, ipaddr) + self.listen_co = co_new(function (fd, ipaddr) while 1 do if fd and ipaddr then co_spwan(cb, fd, ipaddr) @@ -289,7 +289,7 @@ function TCP:listen(ip, port, cb) end end end) - return true, tcp_listen(self.LISTEN_IO, self.fd, self.co) + return true, tcp_listen(self.LISTEN_IO, self.fd, self.listen_co) end function TCP:listen_ex(unix_domain_path, removed, cb) @@ -301,7 +301,7 @@ function TCP:listen_ex(unix_domain_path, removed, cb) if type(cb) ~= 'function' then return nil, "Listen_ex function was invalid." end - self.co = co_new(function (fd) + self.listen_ex_co = co_new(function (fd) while 1 do if fd then co_spwan(cb, fd, "127.0.0.1") @@ -309,7 +309,7 @@ function TCP:listen_ex(unix_domain_path, removed, cb) end end end) - return true, tcp_listen_ex(self.LISTEN_EX_IO, self.ufd, self.co) + return true, tcp_listen_ex(self.LISTEN_EX_IO, self.ufd, self.listen_ex_co) end function TCP:connect(domain, port) @@ -407,67 +407,72 @@ end function TCP:close() - if self.timer then - self.timer:stop() - self.timer = nil - end + if self.timer then + self.timer:stop() + self.timer = nil + end - if self.READ_IO then - tcp_stop(self.READ_IO) - tcp_push(self.READ_IO) - self.READ_IO = nil - self.read_co = nil - end + if self.READ_IO then + tcp_stop(self.READ_IO) + tcp_push(self.READ_IO) + self.READ_IO = nil + self.read_co = nil + end - if self.SEND_IO then - tcp_stop(self.SEND_IO) - tcp_push(self.SEND_IO) - self.SEND_IO = nil - self.send_co = nil - self.sendfile_co = nil - end + if self.SEND_IO then + tcp_stop(self.SEND_IO) + tcp_push(self.SEND_IO) + self.SEND_IO = nil + self.send_co = nil + self.sendfile_co = nil + end - if self.CONNECT_IO then - tcp_stop(self.CONNECT_IO) - tcp_push(self.CONNECT_IO) - self.CONNECT_IO = nil - self.connect_co = nil - end + if self.CONNECT_IO then + tcp_stop(self.CONNECT_IO) + tcp_push(self.CONNECT_IO) + self.CONNECT_IO = nil + self.connect_co = nil + end - if self.connect_current_co then - co_wakeup(self.connect_current_co) - self.connect_current_co = nil - end + if self.connect_current_co then + co_wakeup(self.connect_current_co) + self.connect_current_co = nil + end - if self.send_current_co then - co_wakeup(self.send_current_co) - self.send_current_co = nil - end + if self.send_current_co then + co_wakeup(self.send_current_co) + self.send_current_co = nil + end - if self.read_current_co then - co_wakeup(self.read_current_co) - self.read_current_co = nil - end + if self.read_current_co then + co_wakeup(self.read_current_co) + self.read_current_co = nil + end - if self.sendfile_current_co then - co_wakeup(self.sendfile_current_co) - self.sendfile_current_co = nil - end + if self.sendfile_current_co then + co_wakeup(self.sendfile_current_co) + self.sendfile_current_co = nil + end - if self._timeout then - self._timeout = nil - end + if self._timeout then + self._timeout = nil + end - if self.ssl and self.ssl_ctx then - tcp_free_ssl(self.ssl_ctx, self.ssl) - self.ssl_ctx = nil - self.ssl = nil - end + if self.ssl and self.ssl_ctx then + tcp_free_ssl(self.ssl_ctx, self.ssl) + self.ssl_ctx = nil + self.ssl = nil + end - if self.fd then - tcp_close(self.fd) - self.fd = nil - end + if self.fd then + tcp_close(self.fd) + self.fd = nil + end + + if self.ufd then + tcp_close(self.ufd) + self.fd = nil + end end From fd3182a5ae2b090bedad491b6336621540c7ab7d Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 5 Nov 2019 22:14:28 +0800 Subject: [PATCH 430/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0httpd=20=E7=9B=91?= =?UTF-8?q?=E5=90=ACunix=20domain=20socket=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/init.lua | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua index 6b81de87..2161dcbd 100644 --- a/lualib/httpd/init.lua +++ b/lualib/httpd/init.lua @@ -155,31 +155,42 @@ end -- 监听请求 function httpd:listen(ip, port, backlog) - if type(ip) == 'string' and type(port) == 'number' then - if io.type(io.output()) == 'file' then - io_write(fmt('\27[32m[%s] [INFO]\27[0m httpd正在监听: %s:%s \n', os_date("%Y/%m/%d %H:%M:%S"), "0.0.0.0", port)) - end - if self.logging then - self.logging:dump(fmt('[%s] [INFO] httpd正在监听: %s:%s\n', os_date("%Y/%m/%d %H:%M:%S"), "0.0.0.0", port)) - end + assert(type(ip) == 'string' and toint(port), "httpd error: invalid ip or port") + if io.type(io.output()) == 'file' then + io_write(fmt('\27[32m[%s] [INFO]\27[0m httpd listen: %s:%s \n', os_date("%Y/%m/%d %H:%M:%S"), "0.0.0.0", port)) end - if type(backlog) == 'number' then - self.sock:set_backlog(toint(backlog)) + if self.logging then + self.logging:dump(fmt('[%s] [INFO] httpd listen: %s:%s\n', os_date("%Y/%m/%d %H:%M:%S"), "0.0.0.0", port)) end - local ok, err = self.sock:listen(ip or "0.0.0.0", toint(port), function (fd, ipaddr) + self.sock:set_backlog(toint(backlog)) + return self.sock:listen(ip or "0.0.0.0", toint(port), function (fd, ipaddr) return EVENT_DISPATCH(fd, match(ipaddr, '^::[f]+:(.+)') or ipaddr, self) end) - return assert(ok and self, err) +end + +-- 监听unixsock +function httpd:listenx(unix_domain_path, backlog) + assert(type(unix_domain_path) == 'string' and unix_domain_path ~= '', "httpd error: invalid unix domain path") + if io.type(io.output()) == 'file' then + io_write(fmt('\27[32m[%s] [INFO]\27[0m httpd listen: %s \n', os_date("%Y/%m/%d %H:%M:%S"), unix_domain_path)) + end + if self.logging then + self.logging:dump(fmt('[%s] [INFO] httpd listen: %s\n', os_date("%Y/%m/%d %H:%M:%S"), unix_domain_path)) + end + self.sock:set_backlog(toint(backlog)) + return self.sock:listen_ex(unix_domain_path, true, function (fd, ipaddr) + return EVENT_DISPATCH(fd, match(ipaddr, '^::[f]+:(.+)') or ipaddr, self) + end) end -- 正确的运行方式 function httpd:run() if io.type(io.output()) == 'file' then self.output = true - io_write(fmt('\27[32m[%s] [INFO]\27[0m httpd正在运行Web Server服务...\n', os_date("%Y/%m/%d %H:%M:%S"))) + io_write(fmt('\27[32m[%s] [INFO]\27[0m httpd Web Server Running...\n', os_date("%Y/%m/%d %H:%M:%S"))) end if self.logging then - self.logging:dump(fmt('[%s] [INFO] httpd正在运行Web Server服务...\n', os_date("%Y/%m/%d %H:%M:%S"))) + self.logging:dump(fmt('[%s] [INFO] httpd Web Server Running...\n', os_date("%Y/%m/%d %H:%M:%S"))) end return cf.wait() end From 7560d51f321249c74f5b68cbe4bd892e22adc8e0 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 5 Nov 2019 22:26:33 +0800 Subject: [PATCH 431/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0httpd=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/init.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua index 2161dcbd..5cbebbc2 100644 --- a/lualib/httpd/init.lua +++ b/lualib/httpd/init.lua @@ -163,9 +163,9 @@ function httpd:listen(ip, port, backlog) self.logging:dump(fmt('[%s] [INFO] httpd listen: %s:%s\n', os_date("%Y/%m/%d %H:%M:%S"), "0.0.0.0", port)) end self.sock:set_backlog(toint(backlog)) - return self.sock:listen(ip or "0.0.0.0", toint(port), function (fd, ipaddr) + return assert(self.sock:listen(ip or "0.0.0.0", toint(port), function (fd, ipaddr) return EVENT_DISPATCH(fd, match(ipaddr, '^::[f]+:(.+)') or ipaddr, self) - end) + end)) end -- 监听unixsock @@ -178,9 +178,9 @@ function httpd:listenx(unix_domain_path, backlog) self.logging:dump(fmt('[%s] [INFO] httpd listen: %s\n', os_date("%Y/%m/%d %H:%M:%S"), unix_domain_path)) end self.sock:set_backlog(toint(backlog)) - return self.sock:listen_ex(unix_domain_path, true, function (fd, ipaddr) + return assert(self.sock:listen_ex(unix_domain_path, true, function (fd, ipaddr) return EVENT_DISPATCH(fd, match(ipaddr, '^::[f]+:(.+)') or ipaddr, self) - end) + end)) end -- 正确的运行方式 From 3cffe73df5f9eeb5a8b8d57716a4b60d05c82e5a Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 5 Nov 2019 22:27:23 +0800 Subject: [PATCH 432/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/main.lua | 3 ++- script/test_cfadmin.lua | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/script/main.lua b/script/main.lua index 840c62c9..91df4ed0 100644 --- a/script/main.lua +++ b/script/main.lua @@ -99,7 +99,8 @@ end) app:static('static') -- httpd监听端口 -app:listen("0.0.0.0", 8080) +app:listen("0.0.0.0", 8080, 128) +-- app:listenx("cfadmin.sock", 128) -- 运行 app:run() diff --git a/script/test_cfadmin.lua b/script/test_cfadmin.lua index 731a876e..e661c913 100644 --- a/script/test_cfadmin.lua +++ b/script/test_cfadmin.lua @@ -98,7 +98,8 @@ end) app:static('static') -- httpd监听端口 -app:listen("0.0.0.0", 8080) +app:listen("0.0.0.0", 8080, 128) +-- app:listenx("cfadmin.sock", 128) -- 运行 app:run() From 25cdec6c371c109157933e3840bdf1405e6229d7 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 6 Nov 2019 17:48:01 +0800 Subject: [PATCH 433/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0zlib=E4=BE=9D?= =?UTF-8?q?=E8=B5=96,=E5=AE=8C=E5=96=84httpc=E7=9A=84http=20gzip=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/Makefile | 3 + luaclib/src/lz/Makefile | 20 +++++++ luaclib/src/lz/lzlib.c | 115 ++++++++++++++++++++++++++++++++++++++ lualib/httpc/protocol.lua | 45 +++++++++++---- 4 files changed, 171 insertions(+), 12 deletions(-) create mode 100644 luaclib/src/lz/Makefile create mode 100644 luaclib/src/lz/lzlib.c diff --git a/luaclib/Makefile b/luaclib/Makefile index 62c18e92..1fa65955 100644 --- a/luaclib/Makefile +++ b/luaclib/Makefile @@ -30,6 +30,9 @@ internal : 3part : @echo "********** Third party modules **********" + @echo "CC - lz" + @$(MAKE) -C src/lz build + @echo "CC - lfs" @$(MAKE) -C src/lfs build diff --git a/luaclib/src/lz/Makefile b/luaclib/src/lz/Makefile new file mode 100644 index 00000000..f9b57d62 --- /dev/null +++ b/luaclib/src/lz/Makefile @@ -0,0 +1,20 @@ +.PHONY : build rebuild clean + +default : + @echo "=======================================" + @echo "Please use 'make build' command to build it.." + @echo "Please use 'make rebuild' command to build it.." + @echo "Please use 'make clean' command to clean all." + @echo "=======================================" + +CC = cc + +INCLUDES += -I../../../src -I/usr/local/include +LIBS = -L../ -L../../../ -L/usr/local/lib + +CFLAGS = -O3 -Wall -shared -fPIC +DLL = -lcore -llua -lz + +build: + @$(CC) -o lz.so lzlib.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) + @mv *.so ../../ diff --git a/luaclib/src/lz/lzlib.c b/luaclib/src/lz/lzlib.c new file mode 100644 index 00000000..86f166bc --- /dev/null +++ b/luaclib/src/lz/lzlib.c @@ -0,0 +1,115 @@ +#define LUA_LIB + +#include "../../../src/core.h" +#include + + +static inline +void stream_init(z_stream *z) { + memset(z, 0x0, sizeof(*z)); + z->zalloc = Z_NULL; + z->zfree = Z_NULL; + z->opaque = Z_NULL; +} + +// static int lcompress(lua_State *L) { +// return 1; +// } + +// static int luncompress(lua_State *L) { +// return 1; +// } + +static int lgzip_compress(lua_State *L) { + size_t in_size = 0; + const char* in = luaL_checklstring(L, 1, &in_size); + if (in_size <= 0) + return 0; + + z_stream z; + stream_init(&z); + + int ok = deflateInit2(&z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + 16, 8, Z_DEFAULT_STRATEGY); + if (Z_OK != ok) + return 0; + + z.next_in = (uint8_t *)in; + z.avail_in = in_size; + + luaL_Buffer B; + z.avail_out = deflateBound(&z, in_size); + z.next_out = (uint8_t *)luaL_buffinitsize(L, &B, z.avail_out); + memset(z.next_out, 0x0, z.avail_out); + + int ret = deflate(&z, Z_FINISH); + if (ret != Z_STREAM_END) { + luaL_pushresultsize(&B, 0); + deflateEnd(&z); + return 0; + } + if (deflateEnd(&z) != Z_OK){ + luaL_pushresultsize(&B, 0); + return 0; + } + luaL_pushresultsize(&B, z.total_out); + return 1; +} + +static int lgzip_uncompress(lua_State *L) { + size_t in_size = 0; + const char* in = luaL_checklstring(L, 1, &in_size); + if (in_size <= 0) + return 0; + + z_stream z; + stream_init(&z); + + int ok = inflateInit2(&z, MAX_WBITS + 16); + if (Z_OK != ok) + return 0; + + size_t out_size = in_size << 1; + int top = lua_gettop(L); + + for(;;) { + luaL_Buffer B; + z.next_in = (uint8_t *)in; + z.avail_in = in_size; + z.avail_out = out_size; + z.next_out = (uint8_t *)luaL_buffinitsize(L, &B, out_size); + memset(z.next_out, 0x0, z.avail_out); + + int ret = inflate(&z, Z_FINISH); + if (ret == Z_STREAM_END) { + int ok = inflateEnd(&z); + if (ok != Z_OK) + return 0; + luaL_pushresultsize(&B, z.total_out); + break; + } + if (ret != Z_BUF_ERROR) { + luaL_pushresultsize(&B, 0); + inflateEnd(&z); + return 0; + } + inflateReset(&z); + out_size <<= 1; + luaL_pushresultsize(&B, 0); + lua_settop(L, top); + } + return 1; +} + +LUAMOD_API int +luaopen_lz(lua_State *L){ + luaL_checkversion(L); + luaL_Reg zlib_libs[] = { + // {"compress", lcompress}, + // {"uncompress", luncompress}, + {"gzcompress", lgzip_compress}, + {"gzuncompress", lgzip_uncompress}, + {NULL, NULL} + }; + luaL_newlib(L, zlib_libs); + return 1; +} \ No newline at end of file diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua index 82b50510..2471c7fd 100644 --- a/lualib/httpc/protocol.lua +++ b/lualib/httpc/protocol.lua @@ -1,5 +1,9 @@ local tcp = require "internal.TCP" +local lz = require"lz" +local uncompress = lz.uncompress +local gzuncompress = lz.gzuncompress + local json = require "json" local json_encode = json.encode @@ -142,7 +146,7 @@ local function httpc_response(sock, SSL) local content = {} local times = 0 while 1 do - local data, len = sock_recv(sock, SSL, 65535) + local data, len = sock_recv(sock, SSL, 1024) if not data then return nil, SSL.." A peer of remote server close this connection." end @@ -154,6 +158,7 @@ local function httpc_response(sock, SSL) if not CODE or not HEADER then return nil, SSL.." can't resolvable protocol." end + local Content_Encoding = HEADER['Content-Encoding'] or HEADER['Content-encoding'] local Content_Length = toint(HEADER['Content-Length'] or HEADER['Content-length'] or HEADER['content-length']) local chunked = HEADER['Transfer-Encoding'] or HEADER['Transfer-encoding'] or HEADER['transfer-encoding'] if not chunked and not Content_Length then @@ -164,7 +169,11 @@ local function httpc_response(sock, SSL) end if Content_Length then if (#DATA - posB) == Content_Length then - return CODE, split(DATA, posB + 1, #DATA) + local res = split(DATA, posB + 1, #DATA) + if Content_Encoding == "gzip" then + res = gzuncompress(res) + end + return CODE, res end local content = {split(DATA, posB + 1, #DATA)} local Len = #content[1] @@ -176,7 +185,11 @@ local function httpc_response(sock, SSL) insert(content, data) Len = Len + len if Len >= Content_Length then - return CODE, concat(content) + local res = concat(content) + if Content_Encoding == "gzip" then + res = gzuncompress(res) + end + return CODE, res end end end @@ -190,7 +203,11 @@ local function httpc_response(sock, SSL) end if data then local Pos = find(data, CRLF..(0)..CRLF2) - return CODE, split(data, 1, Pos and Pos - #CRLF2 - 1 or -1) + local res = split(data, 1, Pos and Pos - #CRLF2 - 1 or -1) + if Content_Encoding == "gzip" then + res = gzuncompress(res) + end + return CODE, res end insert(content, buf) end @@ -206,9 +223,13 @@ local function httpc_response(sock, SSL) end if data then local Pos = find(data, CRLF..(0)..CRLF2) - return CODE, split(data, 1, Pos and Pos - #CRLF2 - 1 or -1) + local res = split(data, 1, Pos and Pos - #CRLF2 - 1 or -1) + if Content_Encoding == "gzip" then + res = gzuncompress(res) + end + return CODE, res end - end + end end end end @@ -220,7 +241,7 @@ local function build_get_req (opt) fmt("GET %s HTTP/1.1", opt.path), fmt("Host: %s", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port), 'Accept: */*', - 'Accept-Encoding: identity', + 'Accept-Encoding: gzip, identity', fmt("Connection: keep-alive"), fmt("User-Agent: %s", opt.server), } @@ -247,7 +268,7 @@ local function build_post_req (opt) fmt("POST %s HTTP/1.1\r\n", opt.path), fmt("Host: %s\r\n", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port), 'Accept: */*\r\n', - 'Accept-Encoding: identity\r\n', + 'Accept-Encoding: gzip, identity\r\n', 'Connection: keep-alive\r\n', fmt("User-Agent: %s\r\n", opt.server), } @@ -279,7 +300,7 @@ local function build_delete_req (opt) fmt("Host: %s", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port), fmt("User-Agent: %s", opt.server), 'Accept: */*', - 'Accept-Encoding: identity', + 'Accept-Encoding: gzip, identity', "Connection: keep-alive", } if type(opt.headers) == "table" then @@ -299,7 +320,7 @@ local function build_json_req (opt) fmt("POST %s HTTP/1.1", opt.path), fmt("Host: %s", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port), 'Accept: */*', - 'Accept-Encoding: identity', + 'Accept-Encoding: gzip, identity', "Connection: keep-alive", fmt("User-Agent: %s", opt.server), } @@ -322,7 +343,7 @@ local function build_file_req (opt) fmt("POST %s HTTP/1.1\r\n", opt.path), fmt("Host: %s\r\n", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port), 'Accept: */*\r\n', - 'Accept-Encoding: identity\r\n', + 'Accept-Encoding: gzip, identity\r\n', fmt("Connection: keep-alive\r\n"), fmt("User-Agent: %s\r\n", opt.server), } @@ -378,7 +399,7 @@ local function build_put_req (opt) fmt("PUT %s HTTP/1.1", opt.path), fmt("Host: %s", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port), 'Accept: */*', - 'Accept-Encoding: identity', + 'Accept-Encoding: gzip, identity', fmt("Connection: keep-alive"), fmt("User-Agent: %s", opt.server), } From 0e979f0e55143fd8b777bc1886714aa31289dd7c Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 6 Nov 2019 18:07:35 +0800 Subject: [PATCH 434/956] =?UTF-8?q?=E4=B8=BAhttpd=E5=A2=9E=E5=8A=A0enable?= =?UTF-8?q?=20gzip=E5=8E=8B=E7=BC=A9=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/init.lua | 5 +++++ lualib/protocol/http/init.lua | 11 ++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua index 5cbebbc2..4bf7450a 100644 --- a/lualib/httpd/init.lua +++ b/lualib/httpd/init.lua @@ -108,6 +108,11 @@ function httpd:timeout(timeout) end end +-- 开启gzip压缩支持 +function httpd:enable_gzip() + self.__enable_gzip = true +end + -- 是否记录解析cookie function httpd:enable_cookie () self.__cookie = true diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index 31646668..583846b6 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -13,6 +13,9 @@ local DATE = require("sys").date local new_tab = require("sys").new_tab local insert = table.insert +local lz = require "lz" +local gzcompress = lz.gzcompress + local form = require "httpd.Form" local FILE_TYPE = form.FILE local ARGS_TYPE = form.ARGS @@ -261,6 +264,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) local server = http.__server local timeout = http.__timeout or 0 local cookie = http.__cookie + local enable_gzip = http.__enable_gzip local cookie_secure = http.__cookie_secure local before_func = http._before_func local max_path_size = http.__max_path_size @@ -472,7 +476,12 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) sock:send(ERROR_RESPONSE(http, 500, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, now() - start)) return sock:close() end - header[#header+1] = 'Content-Length: '.. #body + local accept_encoding = HEADER['Accept-Encoding'] + if enable_gzip and accept_encoding and find(accept_encoding, "gzip") then + header[#header+1] = 'Content-Encoding: gzip' + body = gzcompress(body) + end + header[#header+1] = 'Content-Length: ' .. #body header[#header+1] = 'Cache-Control: no-cache, no-store, must-revalidate' header[#header+1] = 'Cache-Control: no-cache' else From aa1785e7990fa2e65d12baed3c392da44e2b7d26 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 7 Nov 2019 00:59:16 +0800 Subject: [PATCH 435/956] =?UTF-8?q?=E5=88=A0=E9=99=A4=E4=B8=80=E4=B8=AAhtt?= =?UTF-8?q?p=20response=E5=86=97=E4=BD=99=E5=A4=B4=E9=83=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index 583846b6..e9ac93e8 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -483,7 +483,6 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) end header[#header+1] = 'Content-Length: ' .. #body header[#header+1] = 'Cache-Control: no-cache, no-store, must-revalidate' - header[#header+1] = 'Cache-Control: no-cache' else if ttl then header[#header+1] = HTTP_EXPIRES(time() + ttl) From 02ae74394bee23ede23835ea11794329cafef628 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 7 Nov 2019 01:44:59 +0800 Subject: [PATCH 436/956] =?UTF-8?q?=E4=BC=98=E5=8C=96httpc=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E7=9A=84=E5=8D=8F=E8=AE=AE=E6=9E=84=E9=80=A0=E6=96=B9?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/protocol.lua | 127 ++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 67 deletions(-) diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua index 2471c7fd..89833a91 100644 --- a/lualib/httpc/protocol.lua +++ b/lualib/httpc/protocol.lua @@ -4,6 +4,8 @@ local lz = require"lz" local uncompress = lz.uncompress local gzuncompress = lz.gzuncompress +local new_tab = require "sys".new_tab + local json = require "json" local json_encode = json.encode @@ -143,10 +145,10 @@ local function httpc_response(sock, SSL) end local VERSION, CODE, STATUS, HEADER, BODY local Content_Length - local content = {} + local content = new_tab(8, 0) local times = 0 while 1 do - local data, len = sock_recv(sock, SSL, 1024) + local data, len = sock_recv(sock, SSL, 4096) if not data then return nil, SSL.." A peer of remote server close this connection." end @@ -158,15 +160,12 @@ local function httpc_response(sock, SSL) if not CODE or not HEADER then return nil, SSL.." can't resolvable protocol." end - local Content_Encoding = HEADER['Content-Encoding'] or HEADER['Content-encoding'] - local Content_Length = toint(HEADER['Content-Length'] or HEADER['Content-length'] or HEADER['content-length']) - local chunked = HEADER['Transfer-Encoding'] or HEADER['Transfer-encoding'] or HEADER['transfer-encoding'] - if not chunked and not Content_Length then - return nil, "不支持的请求体解析方式:"..( - (HEADER['Content-Length'] or HEADER['Content-length'] or HEADER['content-length']) or - (HEADER['Transfer-Encoding'] or HEADER['Transfer-encoding'] or HEADER['transfer-encoding']) or - "未知的解析方式") - end + local Content_Encoding = HEADER['Content-Encoding'] or HEADER['content-encoding'] + local Content_Length = toint(HEADER['Content-Length'] or HEADER['content-length']) + local Chunked = HEADER['Transfer-Encoding'] or HEADER['transfer-encoding'] + if not Content_Length and not Chunked then + return nil, "Unsupported response body parsing." + end if Content_Length then if (#DATA - posB) == Content_Length then local res = split(DATA, posB + 1, #DATA) @@ -175,7 +174,8 @@ local function httpc_response(sock, SSL) end return CODE, res end - local content = {split(DATA, posB + 1, #DATA)} + local content = new_tab(8, 0) + content[#content+1] = split(DATA, posB + 1, #DATA) local Len = #content[1] while 1 do local data, len = sock_recv(sock, SSL, 65535) @@ -193,8 +193,8 @@ local function httpc_response(sock, SSL) end end end - if chunked and chunked == "chunked" then - local content = {} + if Chunked and Chunked == "chunked" then + local content = new_tab(8, 0) if #DATA > posB then local buf = split(DATA, posB + 1, #DATA) data, len = RESPONSE_CHUNKED_PARSER(buf) @@ -237,16 +237,15 @@ end local function build_get_req (opt) - local request = { - fmt("GET %s HTTP/1.1", opt.path), - fmt("Host: %s", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port), - 'Accept: */*', - 'Accept-Encoding: gzip, identity', - fmt("Connection: keep-alive"), - fmt("User-Agent: %s", opt.server), - } + local request = new_tab(16, 0) + insert(request, fmt("GET %s HTTP/1.1", opt.path)) + insert(request, fmt("Host: %s", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port)) + insert(request, fmt("User-Agent: %s", opt.server)) + insert(request, 'Accept: */*') + insert(request, 'Accept-Encoding: gzip, identity') + insert(request, 'Connection: keep-alive') if type(opt.args) == "table" then - local args = {} + local args = new_tab(8, 0) for _, arg in ipairs(opt.args) do assert(#arg == 2, "args need key[1]->value[2] (2 values and must be string)") insert(args, url_encode(arg[1])..'='..url_encode(arg[2])) @@ -264,14 +263,13 @@ local function build_get_req (opt) end local function build_post_req (opt) - local request = { - fmt("POST %s HTTP/1.1\r\n", opt.path), - fmt("Host: %s\r\n", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port), - 'Accept: */*\r\n', - 'Accept-Encoding: gzip, identity\r\n', - 'Connection: keep-alive\r\n', - fmt("User-Agent: %s\r\n", opt.server), - } + local request = new_tab(16, 0) + insert(request, fmt("POST %s HTTP/1.1\r\n", opt.path)) + insert(request, fmt("Host: %s\r\n", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port)) + insert(request, fmt("User-Agent: %s\r\n", opt.server)) + insert(request, 'Accept: */*\r\n') + insert(request, 'Accept-Encoding: gzip, identity\r\n') + insert(request, 'Connection: keep-alive\r\n') if type(opt.headers) == "table" then for _, header in ipairs(opt.headers) do assert(lower(header[1]) ~= 'content-length', "please don't give Content-Length") @@ -281,7 +279,7 @@ local function build_post_req (opt) end insert(request, CRLF) if type(opt.body) == "table" then - local body = {} + local body = new_tab(8, 0) for _, item in ipairs(opt.body) do assert(#item == 2, "if BODY is TABLE, BODY need key[1]->value[2] (2 values)") insert(body, url_encode(item[1])..'='..url_encode(item[2])) @@ -295,14 +293,13 @@ local function build_post_req (opt) end local function build_delete_req (opt) - local request = { - fmt("DELETE %s HTTP/1.1", opt.path), - fmt("Host: %s", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port), - fmt("User-Agent: %s", opt.server), - 'Accept: */*', - 'Accept-Encoding: gzip, identity', - "Connection: keep-alive", - } + local request = new_tab(16, 0) + insert(request, fmt("DELETE %s HTTP/1.1", opt.path)) + insert(request, fmt("Host: %s", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port)) + insert(request, fmt("User-Agent: %s", opt.server)) + insert(request, 'Accept: */*') + insert(request, 'Accept-Encoding: gzip, identity') + insert(request, 'Connection: keep-alive') if type(opt.headers) == "table" then for _, header in ipairs(opt.headers) do assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") @@ -316,14 +313,13 @@ local function build_delete_req (opt) end local function build_json_req (opt) - local request = { - fmt("POST %s HTTP/1.1", opt.path), - fmt("Host: %s", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port), - 'Accept: */*', - 'Accept-Encoding: gzip, identity', - "Connection: keep-alive", - fmt("User-Agent: %s", opt.server), - } + local request = new_tab(16, 0) + insert(request, fmt("POST %s HTTP/1.1", opt.path)) + insert(request, fmt("Host: %s", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port)) + insert(request, fmt("User-Agent: %s", opt.server)) + insert(request, 'Accept: */*') + insert(request, 'Accept-Encoding: gzip, identity') + insert(request, 'Connection: keep-alive') if type(opt.headers) == "table" then for _, header in ipairs(opt.headers) do assert(lower(header[1]) ~= 'content-length', "please don't give Content-Length") @@ -339,15 +335,13 @@ local function build_json_req (opt) end local function build_file_req (opt) - local request = { - fmt("POST %s HTTP/1.1\r\n", opt.path), - fmt("Host: %s\r\n", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port), - 'Accept: */*\r\n', - 'Accept-Encoding: gzip, identity\r\n', - fmt("Connection: keep-alive\r\n"), - fmt("User-Agent: %s\r\n", opt.server), - } - + local request = new_tab(16, 0) + insert(request, fmt("POST %s HTTP/1.1\r\n", opt.path)) + insert(request, fmt("Host: %s\r\n", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port)) + insert(request, fmt("User-Agent: %s\r\n", opt.server)) + insert(request, 'Accept: */*\r\n') + insert(request, 'Accept-Encoding: gzip, identity\r\n') + insert(request, 'Connection: keep-alive\r\n') if type(opt.headers) == "table" then for _, header in ipairs(opt.headers) do assert(lower(header[1]) ~= 'content-length', "please don't give Content-Length") @@ -361,7 +355,7 @@ local function build_file_req (opt) local boundary_start = fmt("------CFWebService%d", bd) local boundary_end = fmt("------CFWebService%d--", bd) insert(request, fmt('Content-Type: multipart/form-data; boundary=----CFWebService%s\r\n', bd)) - local body = {} + local body = new_tab(16, 0) local cd = 'Content-Disposition: form-data; %s' local ct = 'Content-Type: %s' for index, file in ipairs(opt.files) do @@ -395,14 +389,13 @@ local function build_file_req (opt) end local function build_put_req (opt) - local request = { - fmt("PUT %s HTTP/1.1", opt.path), - fmt("Host: %s", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port), - 'Accept: */*', - 'Accept-Encoding: gzip, identity', - fmt("Connection: keep-alive"), - fmt("User-Agent: %s", opt.server), - } + local request = new_tab(16, 0) + insert(request, fmt("PUT %s HTTP/1.1", opt.path)) + insert(request, fmt("Host: %s", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port)) + insert(request, fmt("User-Agent: %s", opt.server)) + insert(request, 'Accept: */*') + insert(request, 'Accept-Encoding: gzip, identity') + insert(request, 'Connection: keep-alive') if type(opt.headers) == "table" then for _, header in ipairs(opt.headers) do assert(lower(header[1]) ~= 'content-length', "please don't give Content-Length") @@ -423,7 +416,7 @@ end -- Json Web Token local function build_jwt(secret, payload) - local content = {nil, nil, nil} + local content = new_tab(3, 0) -- header content[#content + 1] = base64urlencode(json_encode{ alg = "HS256", typ = "JWT" }) -- payload From 096e3fa687cc724966ae06d0d17db2e6dd33b990 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 7 Nov 2019 02:05:20 +0800 Subject: [PATCH 437/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=84=8F=E5=A4=96=E7=9A=84=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/mysql.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua index ba091bfb..afdc68c7 100644 --- a/lualib/protocol/mysql.lua +++ b/lualib/protocol/mysql.lua @@ -60,6 +60,8 @@ local CHARSET_MAP = { gb18030 = 248 } +local class = require "class" + local MySQL = class("MySQL") local STATE_CONNECTED = 1 From 5062d1fff0fa6f7e56d66eb4bf7dce875dfe37ee Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 7 Nov 2019 02:11:15 +0800 Subject: [PATCH 438/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AAcla?= =?UTF-8?q?ss=E5=BC=95=E7=94=A8=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/class/init.lua | 50 +++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/lualib/class/init.lua b/lualib/class/init.lua index 8b882bae..de896b1c 100644 --- a/lualib/class/init.lua +++ b/lualib/class/init.lua @@ -1,30 +1,30 @@ local type = type local setmetatable = setmetatable +local new_tab = require "sys".new_tab + -- 一个精简版的类实现 -function class(cls_name) - local cls = { __name = cls_name } - cls.__index = cls - cls.__call = function (cls, ...) - local call = cls[cls_name] - if call then - return call(cls, ...) - end - return +return function (cls_name) + local cls = {} + cls.__name = cls_name + cls.__index = cls + cls.__call = function (c, ...) + local call = c[cls_name] + if type(call) ~= 'function' then + return end - cls.new = function (c, ...) - if cls ~= c then - return print("Please use ':' to create new object :)") - end - local t = {} - local ctor = c.ctor - if type(ctor) ~= 'function' then - print("Can't find ctor to init.") - else - ctor(t, ...) - end - return setmetatable(t, cls) + return call(c, ...) + end + cls.new = function (c, ...) + if cls ~= c then + return print("Please use ':' to create new object :)") end - return cls -end - -return class + local obj = new_tab(0, 16) + local ctor = cls.ctor + if not ctor then + return print("Can't find ctor to init.") + end + ctor(obj, ...) + return setmetatable(obj, cls) + end + return cls +end \ No newline at end of file From d27f76e530e9943c33acdf9ab877c1dc58892c2b Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 7 Nov 2019 02:11:38 +0800 Subject: [PATCH 439/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AAlog?= =?UTF-8?q?ging=E5=BA=93=E7=9A=84=E5=BC=95=E7=94=A8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/logging/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua index b64221f9..c9d05e71 100644 --- a/lualib/logging/init.lua +++ b/lualib/logging/init.lua @@ -106,7 +106,7 @@ local function fmt(...) end -- 格式化日志 -function FMT (where, level, ...) +local function FMT (where, level, ...) return concat({ fmt_Y_m_d_H_M_S(), where, level, ':', fmt(...), '\n'}, ' ') end From 6535ebf587487df5fb373948d6a08f7d47444663 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 8 Nov 2019 11:18:36 +0800 Subject: [PATCH 440/956] =?UTF-8?q?=E4=BC=98=E5=8C=96http=5Fparser?= =?UTF-8?q?=E7=9A=84=E8=A7=A3=E6=9E=90=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lhttpparser/lhttpparser.c | 70 +++++++++++++-------------- 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/luaclib/src/lhttpparser/lhttpparser.c b/luaclib/src/lhttpparser/lhttpparser.c index 9aad968e..aee428ad 100644 --- a/luaclib/src/lhttpparser/lhttpparser.c +++ b/luaclib/src/lhttpparser/lhttpparser.c @@ -7,57 +7,53 @@ static int lparser_response_chunked(lua_State *L){ size_t buf_len; const char* data = luaL_checklstring(L, 1, &buf_len); - char* buf = xmalloc(buf_len); - if (!buf){ - LOG("ERROR", "Alloca memory falt."); - return 0; - } - memmove(buf, data, buf_len); - struct phr_chunked_decoder decoder = { .consume_trailer = 1 }; + luaL_Buffer B; + char *buf = luaL_buffinitsize(L, &B, buf_len); + luaL_addlstring(&B, data, buf_len); + struct phr_chunked_decoder decoder = { .consume_trailer = 1 }; int last = phr_decode_chunked(&decoder, buf, &buf_len); if (0 > last) { + luaL_pushresultsize(&B, 0); lua_pushnil(L); lua_pushinteger(L, last); - xfree(buf); return 2; } lua_pushlstring(L, buf, buf_len); - xfree(buf); return 1; } static int lparser_http_request(lua_State *L){ - size_t buf_len; - const char* buf = luaL_checklstring(L, 1, &buf_len); - - int minor_version, ret, i; - const char *method; - const char *path; - size_t method_len, path_len, num_headers; - - struct phr_header headers[128]; - memset(headers, 0x0, sizeof(headers)); - - num_headers = sizeof(headers) / sizeof(headers[0]); - ret = phr_parse_request(buf, buf_len, &method, &method_len, &path, &path_len, &minor_version, headers, &num_headers, 0); - if (0 > ret) return 0; - - lua_pushlstring(L, method, method_len); // METHOD - lua_pushlstring(L, path, path_len); // PATH - lua_pushnumber(L, minor_version > 0 ? 1.1 : 1.0); // VERSION - - lua_createtable(L, 0, 128); - for (i = 0; i < 128; i++){ - if (!headers[i].name || !headers[i].value) - break; - lua_pushlstring(L, headers[i].name, headers[i].name_len); - lua_pushlstring(L, headers[i].value, headers[i].value_len); - lua_rawset(L, lua_gettop(L) - 2); - } - return 4; + size_t buf_len; + const char* buf = luaL_checklstring(L, 1, &buf_len); + + int minor_version, ret, i; + const char *method; + const char *path; + size_t method_len, path_len, num_headers; + + struct phr_header headers[128]; + memset(headers, 0x0, sizeof(headers)); + + num_headers = sizeof(headers) / sizeof(headers[0]); + ret = phr_parse_request(buf, buf_len, &method, &method_len, &path, &path_len, &minor_version, headers, &num_headers, 0); + if (0 > ret) return 0; + + lua_pushlstring(L, method, method_len); // METHOD + lua_pushlstring(L, path, path_len); // PATH + lua_pushnumber(L, minor_version > 0 ? 1.1 : 1.0); // VERSION + + lua_createtable(L, 0, 128); + for (i = 0; i < 128; i++){ + if (!headers[i].name || !headers[i].value) + break; + lua_pushlstring(L, headers[i].name, headers[i].name_len); + lua_pushlstring(L, headers[i].value, headers[i].value_len); + lua_rawset(L, lua_gettop(L) - 2); + } + return 4; } static int From 41532d1b653262ecc9eb40ff676b88c77634ae7b Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 8 Nov 2019 19:06:49 +0800 Subject: [PATCH 441/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0ltcp.c=E7=9A=84?= =?UTF-8?q?=E4=B8=80=E4=BA=9Bhook=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index de5b7728..03a5dfb4 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -23,7 +23,7 @@ void SETSOCKETOPT(int sockfd, int mode){ #ifdef SO_REUSEADDR ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &Enable, sizeof(Enable)); if (ret) { - LOG("ERROR", "设置 SO_REUSEADDR 失败."); + LOG("ERROR", "Setting SO_REUSEADDR failed."); return _exit(-1); } #endif @@ -33,7 +33,7 @@ void SETSOCKETOPT(int sockfd, int mode){ if (mode == SERVER) { ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &Enable, sizeof(Enable)); if (ret) { - LOG("ERROR", "设置 SO_REUSEPORT 失败."); + LOG("ERROR", "Setting SO_REUSEPORT failed."); return _exit(-1); } } @@ -43,7 +43,7 @@ void SETSOCKETOPT(int sockfd, int mode){ #ifdef TCP_NODELAY ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &Enable, sizeof(Enable)); if (ret){ - LOG("ERROR", "TCP_NODELAY 设置失败."); + LOG("ERROR", "Setting TCP_NODELAY failed."); return _exit(-1); } #endif @@ -53,7 +53,7 @@ void SETSOCKETOPT(int sockfd, int mode){ if (mode != None){ ret = setsockopt(sockfd, IPPROTO_TCP, SO_KEEPALIVE, &Enable , sizeof(Enable)); if (ret){ - LOG("ERROR", "SO_KEEPALIVE 设置失败."); + LOG("ERROR", "Setting SO_KEEPALIVE failed."); return _exit(-1); } } @@ -64,7 +64,7 @@ void SETSOCKETOPT(int sockfd, int mode){ if (mode == SERVER) { ret = setsockopt(sockfd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &Enable, sizeof(Enable)); if (ret){ - LOG("ERROR", "TCP_DEFER_ACCEPT 设置失败."); + LOG("ERROR", "Setting TCP_DEFER_ACCEPT failed."); return _exit(-1); } } @@ -75,7 +75,7 @@ void SETSOCKETOPT(int sockfd, int mode){ int keepidle = 30; ret = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle , sizeof(keepidle)); if (ret){ - LOG("ERROR", "TCP_KEEPIDLE 设置失败."); + LOG("ERROR", "Setting TCP_KEEPIDLE failed."); return _exit(-1); } #endif @@ -85,7 +85,7 @@ void SETSOCKETOPT(int sockfd, int mode){ int keepcount = 3; ret = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, &keepcount , sizeof(keepcount)); if (ret){ - LOG("ERROR", "TCP_KEEPCNT 设置失败."); + LOG("ERROR", "Setting TCP_KEEPCNT failed."); return _exit(-1); } #endif @@ -95,7 +95,7 @@ void SETSOCKETOPT(int sockfd, int mode){ int keepinterval = 5; ret = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, &keepinterval , sizeof(keepinterval)); if (ret){ - LOG("ERROR", "TCP_KEEPINTVL 设置失败."); + LOG("ERROR", "Setting TCP_KEEPINTVL failed."); return _exit(-1); } #endif @@ -106,7 +106,7 @@ void SETSOCKETOPT(int sockfd, int mode){ int No = 0; ret = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&No, sizeof(No)); if (ret){ - LOG("ERROR", "IPV6_V6ONLY 设置失败."); + LOG("ERROR", "Setting IPV6_V6ONLY failed."); return _exit(-1); } } @@ -158,7 +158,7 @@ create_client_fd(const char *ipaddr, int port){ /* 建立 TCP Client Socket */ int sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); if (0 >= sockfd) { - LOG("DEBUG", strerror(errno)); + LOG("ERROR", strerror(errno)); return -1; } @@ -191,7 +191,7 @@ create_unixsock_fd(const char* path, size_t path_len, int backlog) { errno = 0; int sockfd = socket(AF_LOCAL, SOCK_STREAM, IPPROTO_IP); if (0 >= sockfd){ - LOG("DEBUG", strerror(errno)); + LOG("ERROR", strerror(errno)); return -1; } @@ -206,7 +206,7 @@ create_unixsock_fd(const char* path, size_t path_len, int backlog) { /* 绑定套接字失败 */ int bind_success = bind(sockfd, (struct sockaddr *)&UN, sizeof(UN)); if (0 > bind_success) { - LOG("DEBUG", strerror(errno)); + LOG("ERROR", strerror(errno)); close(sockfd); return -1; } @@ -214,7 +214,7 @@ create_unixsock_fd(const char* path, size_t path_len, int backlog) { /* 监听套接字失败 */ int listen_success = listen(sockfd, backlog); if (0 > listen_success) { - LOG("DEBUG", strerror(errno)); + LOG("ERROR", strerror(errno)); close(sockfd); return -1; } @@ -283,7 +283,7 @@ IO_ACCEPT(CORE_P_ core_io *io, int revents){ int client = accept(io->fd, (struct sockaddr*)&SA, &slen); if (0 >= client) { if (errno != EWOULDBLOCK) - LOG("INFO", strerror(errno)); + LOG("ERROR", strerror(errno)); return ; } SETSOCKETOPT(client, None); @@ -807,8 +807,8 @@ luaopen_tcp(lua_State *L){ SSL_library_init(); SSL_load_error_strings(); // ERR_load_crypto_strings(); - // CRYPTO_set_mem_functions(xmalloc, xrealloc, xfree); // OpenSSL_add_ssl_algorithms(); + CRYPTO_set_mem_functions(xmalloc, xrealloc, xfree); /* 添加SSL支持 */ luaL_newmetatable(L, "__TCP__"); lua_pushstring (L, "__index"); From f50071e6f895ce656a4f4faa2578eaed2dc2d088 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 9 Nov 2019 23:35:25 +0800 Subject: [PATCH 442/956] =?UTF-8?q?=E4=BC=98=E5=8C=96redis=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE=E7=9A=84=E4=B8=80=E4=BA=9B=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/redis.lua | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lualib/protocol/redis.lua b/lualib/protocol/redis.lua index 2e2df8f6..37f01974 100644 --- a/lualib/protocol/redis.lua +++ b/lualib/protocol/redis.lua @@ -3,6 +3,8 @@ local class = require "class" local Co = require "internal.Co" local tcp = require "internal.TCP" +local new_tab = require "sys".new_tab + local Log = log:new({ dump = true, path = 'protocol-redis' }) local co_spwan = Co.spwan @@ -39,8 +41,7 @@ local function read_response(sock) break end end - local firstchar = byte(result) - return redcmd[firstchar](sock, sub(result, 2)) + return redcmd[byte(result)](sock, sub(result, 2)) end redcmd[36] = function(sock, data) -- '$' @@ -70,9 +71,9 @@ redcmd[42] = function(sock, data) -- '*' if n < 0 then return true, nil end - local bulk = {} + local bulk = new_tab(n, 0) local noerr = true - for i = 1,n do + for i = 1, n do local ok, v = read_response(sock) if not ok then noerr = false @@ -85,7 +86,8 @@ end -- 格式化命令为redis protocol local function CMD(...) local tab = {...} - local lines = { "*"..#tab } + local lines = new_tab(#tab, 0) + lines[#lines+1] = "*"..#tab for index = 1, #tab do lines[#lines+1] = "$"..#tostring(tab[index]) lines[#lines+1] = tab[index] @@ -128,8 +130,8 @@ function redis:ctor(opt) self.sock = tcp:new() self.host = opt.host self.port = opt.port - self.db = opt.db self.auth = opt.auth + self.db = opt.db end function redis:connect() @@ -222,9 +224,9 @@ function redis:pipeline(opt) end local max_read_times = #cmds if max_read_times > 0 then - local rets = {} local sock = self.sock sock:send(concat(cmds)) + local rets = new_tab(max_read_times, 0) for i = 1, max_read_times do rets[#rets+1] = {read_response(sock)} end @@ -236,6 +238,7 @@ end function redis:close() if self.sock then self.sock:close() + self.sock = nil end end From 454134fc7b4603496fd7bd65e000b25ff9d060b5 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 9 Nov 2019 23:40:15 +0800 Subject: [PATCH 443/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index e9ac93e8..1189d10c 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -9,7 +9,7 @@ local crypt = require "crypt" local sha1 = crypt.sha1 local base64encode = crypt.base64encode local now = sys.now -local DATE = require("sys").date +local DATE = os.date local new_tab = require("sys").new_tab local insert = table.insert From 3f5ab3534c9d29b8c17aab445e11839adeb665d0 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 9 Nov 2019 23:40:33 +0800 Subject: [PATCH 444/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/parser.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lualib/protocol/http/parser.lua b/lualib/protocol/http/parser.lua index 589b8caa..4aad85b4 100644 --- a/lualib/protocol/http/parser.lua +++ b/lualib/protocol/http/parser.lua @@ -3,6 +3,8 @@ local PARSER_HTTP_REQUEST = httpparser.parser_http_request local PARSER_HTTP_RESPONSE = httpparser.parser_http_response local RESPONSE_CHUNKED_PARSER = httpparser.parser_response_chunked +local pcall = pcall + local http_parser = {} function http_parser.PARSER_HTTP_REQUEST (buffer) From a85b27c7c040471bb81f152dc91f965cc777b82c Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 9 Nov 2019 23:57:44 +0800 Subject: [PATCH 445/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E8=BE=93=E5=87=BA=E6=89=93=E5=8D=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/logging/init.lua | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua index c9d05e71..7de9a414 100644 --- a/lualib/logging/init.lua +++ b/lualib/logging/init.lua @@ -3,10 +3,10 @@ local cf = require "cf" local class = require "class" -local os_date = require("sys").date +local new_tab = require "sys".new_tab +local now = require "sys".now -local system = require "system" -local now = system.now +local os_date = os.date local type = type local select = select @@ -55,7 +55,7 @@ end -- 格式化 local function table_format(t) - local tab = {} + local tab = new_tab(16, 0) while 1 do local mt = getmetatable(t) for key, value in pairs(t) do @@ -86,10 +86,10 @@ local function table_format(t) return concat({'{', concat(tab, ', '), '}'}) end -local function fmt(...) +local function info_fmt(...) local args = {...} local index, len = 1, select('#', ...) - local tab = {} + local tab = new_tab(16, 0) while 1 do local arg = args[index] if type(arg) == 'table' then @@ -107,7 +107,7 @@ end -- 格式化日志 local function FMT (where, level, ...) - return concat({ fmt_Y_m_d_H_M_S(), where, level, ':', fmt(...), '\n'}, ' ') + return concat({ fmt_Y_m_d_H_M_S(), where, level, ':', info_fmt(...), '\n'}, ' ') end local Log = class("Log") @@ -173,7 +173,7 @@ function Log:dump(log) end local file, err = io_open(LOG_FOLDER..self.path..'_'..today..'.log', 'a') if not file then - return io_type(io.output()) == 'file' and io_write('打开文件失败.'..(err or '')..'\n') + return io_type(io.output()) == 'file' and io_write('打开文件失败: '..(('['..err..']') or '')..'\n') end self.file, self.today = file, today file:setvbuf("line") @@ -181,7 +181,7 @@ function Log:dump(log) if not self.file then local file, err = io_open(LOG_FOLDER..self.path..'_'..today..'.log', 'a') if not file then - return io_type(io.output()) == 'file' and io_write('打开文件失败.'..(err or '')..'\n') + return io_type(io.output()) == 'file' and io_write('打开文件失败: '..(('['..err..']') or '')..'\n') end file:setvbuf("line") self.file = file From 1e13a2a0e7a769c839afd7f431ace4fc59b89a66 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 10 Nov 2019 00:07:05 +0800 Subject: [PATCH 446/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/crypt/init.lua | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index 9866d990..32958b70 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -1,11 +1,6 @@ local CRYPT = require "lcrypt" local new_tab = require("sys").new_tab -local fmt = string.format -local byte = string.byte -local match = string.match -local concat = table.concat - local md5 = CRYPT.md5 local hmac64 = CRYPT.hmac64 local hmac_md5 = CRYPT.hmac_md5 @@ -159,14 +154,6 @@ function crypt.hmac64_md5 (key, text, hex) return hash end -function crypt.base64encode (...) - return base64encode(...) -end - -function crypt.base64decode (...) - return base64decode(...) -end - function crypt.base64urlencode(data) return base64encode(data):gsub('+', '-'):gsub('/', '_') end @@ -175,6 +162,14 @@ function crypt.base64urldecode(data) return base64decode(data:gsub('-', '+'):gsub('_', '/')) end +function crypt.base64encode (...) + return base64encode(...) +end + +function crypt.base64decode (...) + return base64decode(...) +end + function crypt.hexencode (...) return hexencode(...) end From b294a58d76d65cec608a5521893287466cab5707 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 10 Nov 2019 15:56:59 +0800 Subject: [PATCH 447/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=97=A0=E6=95=88?= =?UTF-8?q?=E7=9A=84cookie=E5=8F=AF=E8=83=BD=E4=BC=9A=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Cookie.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lualib/httpd/Cookie.lua b/lualib/httpd/Cookie.lua index 2ea60263..eefc5002 100644 --- a/lualib/httpd/Cookie.lua +++ b/lualib/httpd/Cookie.lua @@ -7,6 +7,7 @@ local hexencode = crypt.hexencode local hexdecode = crypt.hexdecode local type = type +local pcall = pcall local assert = assert local ipairs = ipairs local os_date = os.date @@ -19,12 +20,16 @@ local secure = 'http://github.com/candymi/core_framework' -- 加密Cookie Value local function encode_value (value) - return hexencode(xor_str(value, secure)):upper() + return xor_str(value, secure, true):upper() end -- 解密Cookie Value local function decode_value (value) - return xor_str(hexdecode(value:lower()), secure) + local ok, msg = pcall(hexdecode, value:lower()) + if not ok then + return msg + end + return xor_str(msg, secure) end -- 当前协程注册的cookie From 6ee1ca375037f96d766f1dba1882ba4359fa59b1 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 10 Nov 2019 16:07:25 +0800 Subject: [PATCH 448/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lhttpparser/lhttpparser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/luaclib/src/lhttpparser/lhttpparser.c b/luaclib/src/lhttpparser/lhttpparser.c index aee428ad..ed952352 100644 --- a/luaclib/src/lhttpparser/lhttpparser.c +++ b/luaclib/src/lhttpparser/lhttpparser.c @@ -20,7 +20,7 @@ lparser_response_chunked(lua_State *L){ lua_pushinteger(L, last); return 2; } - lua_pushlstring(L, buf, buf_len); + luaL_pushresultsize(&B, buf_len); return 1; } From e58f5b6a9922ed97b068213726b57341f8fa5877 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 10 Nov 2019 16:57:53 +0800 Subject: [PATCH 449/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lhttpparser/lhttpparser.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/luaclib/src/lhttpparser/lhttpparser.c b/luaclib/src/lhttpparser/lhttpparser.c index ed952352..41e8b63a 100644 --- a/luaclib/src/lhttpparser/lhttpparser.c +++ b/luaclib/src/lhttpparser/lhttpparser.c @@ -3,6 +3,8 @@ #include "../../../src/core.h" #include "httpparser.h" +#define MAX_HEADER (128) + static int lparser_response_chunked(lua_State *L){ size_t buf_len; @@ -34,10 +36,10 @@ lparser_http_request(lua_State *L){ const char *path; size_t method_len, path_len, num_headers; - struct phr_header headers[128]; - memset(headers, 0x0, sizeof(headers)); + struct phr_header headers[MAX_HEADER]; + memset(headers, 0x0, sizeof(struct phr_header) * MAX_HEADER); - num_headers = sizeof(headers) / sizeof(headers[0]); + num_headers = MAX_HEADER; ret = phr_parse_request(buf, buf_len, &method, &method_len, &path, &path_len, &minor_version, headers, &num_headers, 0); if (0 > ret) return 0; @@ -45,8 +47,8 @@ lparser_http_request(lua_State *L){ lua_pushlstring(L, path, path_len); // PATH lua_pushnumber(L, minor_version > 0 ? 1.1 : 1.0); // VERSION - lua_createtable(L, 0, 128); - for (i = 0; i < 128; i++){ + lua_createtable(L, 0, MAX_HEADER); + for (i = 0; i < MAX_HEADER; i++){ if (!headers[i].name || !headers[i].value) break; lua_pushlstring(L, headers[i].name, headers[i].name_len); @@ -65,10 +67,10 @@ lparser_http_response(lua_State *L){ size_t msg_len, num_headers; const char* msg; - struct phr_header headers[128]; - memset(headers, 0x0, sizeof(headers)); + struct phr_header headers[MAX_HEADER]; + memset(headers, 0x0, sizeof(struct phr_header) * MAX_HEADER); - num_headers = sizeof(headers) / sizeof(headers[0]); + num_headers = MAX_HEADER; ret = phr_parse_response(buf, buf_len, &minor_version, &status, &msg, &msg_len, headers, &num_headers, 0); if (0 > ret) return 0; @@ -76,8 +78,8 @@ lparser_http_response(lua_State *L){ lua_pushinteger(L, status); // STATUS CODE lua_pushlstring(L, msg, msg_len); // STATUS MSG - lua_createtable(L, 0, 128); - for (i = 0; i < 128; i++){ + lua_createtable(L, 0, MAX_HEADER); + for (i = 0; i < MAX_HEADER; i++){ if (!headers[i].name || !headers[i].value) break; lua_pushlstring(L, headers[i].name, headers[i].name_len); From 5277b44257f12e68d40de481c2c7048a1a69ddf9 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 12 Nov 2019 00:39:59 +0800 Subject: [PATCH 450/956] =?UTF-8?q?=E4=BC=98=E5=8C=96db=E4=B8=8ECache?= =?UTF-8?q?=E5=8D=8F=E7=A8=8B=E7=9A=84=E6=8F=92=E5=85=A5=E6=95=88=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/Cache/init.lua | 4 ++-- lualib/DB/init.lua | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lualib/Cache/init.lua b/lualib/Cache/init.lua index 71258a1a..5a72de5e 100644 --- a/lualib/Cache/init.lua +++ b/lualib/Cache/init.lua @@ -67,7 +67,7 @@ end -- 加入到连接池内 local function add_cache(self, cache) - insert(self.cache_pool, 1, cache) + insert(self.cache_pool, cache) end -- 从连接池内取出一个cache对象 @@ -85,7 +85,7 @@ end -- 加入到协程池内 local function add_wait(self, co) - insert(self.co_pool, 1, co) + insert(self.co_pool, co) end -- 弹出一个等待协程 diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index ee8059cb..0afb7745 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -59,7 +59,7 @@ local function DB_CREATE (opt) end local function add_wait(self, co) - insert(self.co_pool, 1, co) + insert(self.co_pool, co) end local function pop_wait(self) @@ -67,7 +67,7 @@ local function pop_wait(self) end local function add_db(self, db) - insert(self.db_pool, 1, db) + insert(self.db_pool, db) end -- 负责创建连接/加入等待队列 From d9c6339c1a8ce14e72976df6e99b0d409607d341 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 12 Nov 2019 12:22:32 +0800 Subject: [PATCH 451/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0view.home=E6=96=B9?= =?UTF-8?q?=E6=B3=95,=20=E5=AE=8C=E5=96=84=E7=9A=84=E9=A6=96=E9=A1=B5?= =?UTF-8?q?=E8=B7=AF=E7=94=B1=E6=B3=A8=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/view.lua | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lualib/admin/view.lua b/lualib/admin/view.lua index 380fc0ef..24157bed 100644 --- a/lualib/admin/view.lua +++ b/lualib/admin/view.lua @@ -58,6 +58,26 @@ function view.api (path, f) end) end +-- 首页 +function view.home(path, f) + assert(type(path) == 'string', 'view use path failed.') + assert(type(f) == 'function', 'view use handle failed.') + local db, app = config.db, config.app + config.home = path or config.home + return app:use(path, function (content) + local token = Cookie.getCookie("CFTOKEN") + if not token then + return false, config.login_render + end + local info = user_token.token_to_userinfo(db, token) + if not info then + return utils.redirect(config.login_render) + end + local ok, res = pcall(f, httpctx:new{content = content}, db) + return res + end) +end + -- 获取当前用户语言表 function view.get_locale () return utils.get_locale(Cookie.getCookie("CFLANG")) From d4f1f8f5699051038fd28f865dcfce6d08b09feb Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 12 Nov 2019 18:42:03 +0800 Subject: [PATCH 452/956] =?UTF-8?q?admin.view=E5=A2=9E=E5=8A=A0db=E4=B8=8E?= =?UTF-8?q?app=E6=96=AD=E8=A8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/view.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lualib/admin/view.lua b/lualib/admin/view.lua index 24157bed..23f2cbd0 100644 --- a/lualib/admin/view.lua +++ b/lualib/admin/view.lua @@ -34,6 +34,7 @@ function view.use (path, f) assert(type(path) == 'string', 'view use path failed.') assert(type(f) == 'function', 'view use handle failed.') local db, app = config.db, config.app + assert(db and app, "view.use need db session and http context.") return app:use(path, function (content) local ok, url = verify_permission(content, db) if not ok then @@ -52,6 +53,7 @@ function view.api (path, f) assert(type(path) == 'string', 'view api path failed.') assert(type(f) == 'function', 'view api handle failed.') local db, app = config.db, config.app + assert(db and app, "view.api need db session and http context.") return app:api(path, function (content) local ok, res = pcall(f, httpctx:new{content = content}, db) return res @@ -62,8 +64,9 @@ end function view.home(path, f) assert(type(path) == 'string', 'view use path failed.') assert(type(f) == 'function', 'view use handle failed.') + config.home = path local db, app = config.db, config.app - config.home = path or config.home + assert(db and app, "view.home need db session and http context.") return app:use(path, function (content) local token = Cookie.getCookie("CFTOKEN") if not token then From d173e36318556aeb468d300da520bbb8b1d07f16 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 15 Nov 2019 16:59:38 +0800 Subject: [PATCH 453/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AADB?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E8=B6=85=E6=97=B6=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/DB/init.lua | 1 + lualib/protocol/mysql.lua | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index 0afb7745..3bab9f21 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -55,6 +55,7 @@ local function DB_CREATE (opt) assert(db:query(stmt), "["..stmt.."] 预编译失败.") end end + db:set_timeout(0) return db end diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua index afdc68c7..28dff364 100644 --- a/lualib/protocol/mysql.lua +++ b/lualib/protocol/mysql.lua @@ -403,7 +403,7 @@ end function MySQL.set_timeout(self, timeout) - return self.sock:timeout(timeout) + self.sock._timeout = timeout end From 57f4fc3d1b4b389afdca8c21876e277af1d19e1f Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 15 Nov 2019 17:15:37 +0800 Subject: [PATCH 454/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8DCache=E8=B6=85?= =?UTF-8?q?=E6=97=B6=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/Cache/init.lua | 4 +++- lualib/protocol/redis.lua | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lualib/Cache/init.lua b/lualib/Cache/init.lua index 5a72de5e..0e9f3a88 100644 --- a/lualib/Cache/init.lua +++ b/lualib/Cache/init.lua @@ -47,9 +47,11 @@ local function CREATE_CACHE(opt) local rds while 1 do rds = redis:new(opt) + rds:set_timeout(3) local ok, err = rds:connect() if ok then - break + rds:set_timeout(0) + break end Log:WARN('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接") rds:close() diff --git a/lualib/protocol/redis.lua b/lualib/protocol/redis.lua index 37f01974..e4cfb34c 100644 --- a/lualib/protocol/redis.lua +++ b/lualib/protocol/redis.lua @@ -150,6 +150,10 @@ function redis:connect() return true end +function redis:set_timeout(timeout) + self.sock._timeout = timeout +end + -- 订阅 function redis:psubscribe(pattern, func) local sock = self.sock From cd4840747169b629acc313de508d8550064a89d6 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 15 Nov 2019 17:42:35 +0800 Subject: [PATCH 455/956] =?UTF-8?q?=E4=BC=98=E5=8C=96DB=E6=96=AD=E7=BA=BF?= =?UTF-8?q?=E9=87=8D=E8=BF=9E=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/DB/init.lua | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index 3bab9f21..dc096fd3 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -35,27 +35,27 @@ local function DB_CREATE (opt) local times = 1 local db while 1 do - db = mysql:new() - db:set_timeout(3) - local connect, err = db:connect(opt) - if connect then - break + db = mysql:new() + db:set_timeout(3) + local connect, err = db:connect(opt) + if connect then + if not opt.INITIALIZATION then -- 设置连接超时时间 + assert(db:query(fmt('SET GLOBAL wait_timeout=%s', WAIT_TIMEOUT)), "SET GLOBAL wait_timeout faild.") + assert(db:query(fmt('SET GLOBAL interactive_timeout=%s', WAIT_TIMEOUT)), "SET GLOBAL interactive_timeout faild.") end - Log:WARN('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接") - db:close() - times = times + 1 - timer.sleep(3) - end - if not opt.INITIALIZATION then -- 设置连接超时时间 - db:query(fmt('SET GLOBAL wait_timeout=%s', WAIT_TIMEOUT)) - db:query(fmt('SET GLOBAL interactive_timeout=%s', WAIT_TIMEOUT)) - end - if opt.stmts then - for rkey, stmt in pairs(opt.stmts) do - assert(db:query(stmt), "["..stmt.."] 预编译失败.") + if opt.stmts then + for rkey, stmt in pairs(opt.stmts) do + assert(db:query(stmt), "["..stmt.."] 预编译失败.") + end + end + db:set_timeout(0) + break end + Log:WARN('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接") + db:close() + times = times + 1 + timer.sleep(3) end - db:set_timeout(0) return db end From 36f9b2b6652d0d3da01a4ff0313748d64be7e2ee Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 15 Nov 2019 17:46:01 +0800 Subject: [PATCH 456/956] =?UTF-8?q?=E4=BC=98=E5=8C=96Cache=E6=96=AD?= =?UTF-8?q?=E7=BA=BF=E9=87=8D=E8=BF=9E=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/Cache/init.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lualib/Cache/init.lua b/lualib/Cache/init.lua index 0e9f3a88..c707f577 100644 --- a/lualib/Cache/init.lua +++ b/lualib/Cache/init.lua @@ -50,6 +50,12 @@ local function CREATE_CACHE(opt) rds:set_timeout(3) local ok, err = rds:connect() if ok then + if not opt.INITIALIZATION then + local ok, ret = rds:cmd("CONFIG", "GET", "TIMEOUT") + if ret[2] ~= '0' then + assert(rds:cmd("CONFIG SET", "TIMEOUT", "0"), "SET TIMEOUT faild.") + end + end rds:set_timeout(0) break end @@ -58,12 +64,6 @@ local function CREATE_CACHE(opt) times = times + 1 timer.sleep(3) end - if not opt.INITIALIZATION then - local ok, ret = rds:cmd("CONFIG", "GET", "TIMEOUT") - if ret[2] ~= '0' then - rds:cmd("CONFIG SET", "TIMEOUT", "0") - end - end return rds end From 747f7353c5ac5ccecb19a31e94f3c8f1840d626d Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 20 Nov 2019 16:04:50 +0800 Subject: [PATCH 457/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=B8=AAhtt?= =?UTF-8?q?p=E5=8E=8B=E7=BC=A9=E7=9A=84=E7=89=B9=E6=AE=8A=E6=83=85?= =?UTF-8?q?=E5=86=B5=E5=A4=84=E7=90=86.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index 1189d10c..2abe7caf 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -478,8 +478,11 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) end local accept_encoding = HEADER['Accept-Encoding'] if enable_gzip and accept_encoding and find(accept_encoding, "gzip") then - header[#header+1] = 'Content-Encoding: gzip' - body = gzcompress(body) + local compress_body = gzcompress(body) + if compress_body then + header[#header+1] = 'Content-Encoding: gzip' + body = compress_body + end end header[#header+1] = 'Content-Length: ' .. #body header[#header+1] = 'Cache-Control: no-cache, no-store, must-revalidate' From 1d77f95713b8d3685eb696874bbadc1743d75c63 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 23 Nov 2019 18:21:19 +0800 Subject: [PATCH 458/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/protocol.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua index 89833a91..b60b1d7d 100644 --- a/lualib/httpc/protocol.lua +++ b/lualib/httpc/protocol.lua @@ -170,7 +170,7 @@ local function httpc_response(sock, SSL) if (#DATA - posB) == Content_Length then local res = split(DATA, posB + 1, #DATA) if Content_Encoding == "gzip" then - res = gzuncompress(res) + res = gzuncompress(res) or res end return CODE, res end @@ -187,7 +187,7 @@ local function httpc_response(sock, SSL) if Len >= Content_Length then local res = concat(content) if Content_Encoding == "gzip" then - res = gzuncompress(res) + res = gzuncompress(res) or res end return CODE, res end @@ -205,7 +205,7 @@ local function httpc_response(sock, SSL) local Pos = find(data, CRLF..(0)..CRLF2) local res = split(data, 1, Pos and Pos - #CRLF2 - 1 or -1) if Content_Encoding == "gzip" then - res = gzuncompress(res) + res = gzuncompress(res) or res end return CODE, res end @@ -225,7 +225,7 @@ local function httpc_response(sock, SSL) local Pos = find(data, CRLF..(0)..CRLF2) local res = split(data, 1, Pos and Pos - #CRLF2 - 1 or -1) if Content_Encoding == "gzip" then - res = gzuncompress(res) + res = gzuncompress(res) or res end return CODE, res end From 12913fa9fc3b0ac80e70f8a6a245011b3a407433 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 24 Nov 2019 17:01:15 +0800 Subject: [PATCH 459/956] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=9D=83=E9=99=90?= =?UTF-8?q?=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/view.lua | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lualib/admin/view.lua b/lualib/admin/view.lua index 23f2cbd0..509bad9b 100644 --- a/lualib/admin/view.lua +++ b/lualib/admin/view.lua @@ -20,11 +20,16 @@ local function verify_permission (content, db) if not token then return false, config.login_render end + -- 无效token则需要登录 local info = user_token.token_to_userinfo(db, token) - if info and (info.is_admin == 1 or permission.user_have_menu_permission(db, info.id, get_path(content))) then - return true + if not info then + return false, config.login_render + end + -- 有效token需要验证访问权限 + if info.is_admin ~= 1 and permission.user_have_menu_permission(db, info.id, get_path(content)) then + return false, config.login_render end - return false, config.login_render + return true end local view = {} From fcda364ee09ca96e126fc24170afed24e66c1185 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 30 Nov 2019 10:26:00 +0800 Subject: [PATCH 460/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0302|301=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E7=A0=81=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/protocol.lua | 233 +++++++++++++++++++------------------- 1 file changed, 118 insertions(+), 115 deletions(-) diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua index b60b1d7d..abf941b2 100644 --- a/lualib/httpc/protocol.lua +++ b/lualib/httpc/protocol.lua @@ -48,71 +48,71 @@ local function sock_new () end local function sock_recv (sock, PROTOCOL, byte) - if PROTOCOL == 'https' then - local data, len = sock:ssl_recv(byte) - if data then - return data, len - end - end - if PROTOCOL == 'http' then - local data, len = sock:recv(byte) - if data then - return data, len - end - end - return nil, '服务端断开了连接' + if PROTOCOL == 'https' then + local data, len = sock:ssl_recv(byte) + if data then + return data, len + end + end + if PROTOCOL == 'http' then + local data, len = sock:recv(byte) + if data then + return data, len + end + end + return nil, '服务端断开了连接' end local function sock_send(sock, PROTOCOL, DATA) - if PROTOCOL == 'https' then - local ok = sock:ssl_send(DATA) - if ok then - return true - end - end + if PROTOCOL == 'https' then + local ok = sock:ssl_send(DATA) + if ok then + return true + end + end - if PROTOCOL == 'http' then - local ok = sock:send(DATA) - if ok then - return true - end - end - return nil, "httpc发送请求失败" + if PROTOCOL == 'http' then + local ok = sock:send(DATA) + if ok then + return true + end + end + return nil, "httpc发送请求失败" end local function sock_connect(sock, PROTOCOL, DOAMIN, PORT) - if PROTOCOL == 'https' then - local ok, err = sock:ssl_connect(DOAMIN, PORT) - if ok then - return true - end - end - if PROTOCOL == 'http' then - local ok, err = sock:connect(DOAMIN, PORT) - if ok then - return true - end - end - return nil, 'httpc连接失败.' + if PROTOCOL == 'https' then + local ok, err = sock:ssl_connect(DOAMIN, PORT) + if ok then + return true + end + end + if PROTOCOL == 'http' then + local ok, err = sock:connect(DOAMIN, PORT) + if ok then + return true + end + end + return nil, 'httpc连接失败.' end local function splite_protocol(domain) - if type(domain) ~= 'string' then - return nil, '1. 非法的域名' - end + if type(domain) ~= 'string' then + return nil, '1. 非法的域名' + end - local protocol, domain_port, path = match(domain, '^(http[s]?)://([^/]+)(.*)') - if not protocol or not domain_port or not path then - return nil, '2. 错误的url' - end + local protocol, domain_port, path = match(domain, '^(http[s]?)://([^/]+)(.*)') + if not protocol or not domain_port or not path then + return nil, '2. 错误的url' + end - if not path or path == '' then - return nil, "3. http无path需要以'/'结尾." - end + if not path or path == '' then + return nil, "3. http无path需要以'/'结尾." + end - local domain, port - if find(domain_port, ':') then - local _, Bracket_Pos = find(domain_port, '[%[%]]') + local domain, port + if find(domain_port, ':') then + local _, Bracket_Pos = find(domain_port, '[%[%]]') if Bracket_Pos then domain, port = match(domain_port, '%[(.+)%][:]?(%d*)') else @@ -123,79 +123,82 @@ local function splite_protocol(domain) end port = toint(port) if not port then - port = 80 - if protocol == 'https' then - port = 443 - end + port = 80 + if protocol == 'https' then + port = 443 + end end - else - domain, port = domain_port, protocol == 'https' and 443 or 80 - end - return { - protocol = protocol, - domain = domain, - port = port, - path = path, - } + else + domain, port = domain_port, protocol == 'https' and 443 or 80 + end + return { + protocol = protocol, + domain = domain, + port = port, + path = path, + } end local function httpc_response(sock, SSL) - if not sock then - return nil, "Can't used this method before other httpc method.." - end - local VERSION, CODE, STATUS, HEADER, BODY - local Content_Length - local content = new_tab(8, 0) - local times = 0 - while 1 do - local data, len = sock_recv(sock, SSL, 4096) - if not data then - return nil, SSL.." A peer of remote server close this connection." - end - insert(content, data) - local DATA = concat(content) - local posA, posB = find(DATA, CRLF2) - if posB then + if not sock then + return nil, "Can't used this method before other httpc method.." + end + local VERSION, CODE, STATUS, HEADER, BODY + local Content_Length + local content = new_tab(8, 0) + local times = 0 + while 1 do + local data, len = sock_recv(sock, SSL, 4096) + if not data then + return nil, SSL.." A peer of remote server close this connection." + end + insert(content, data) + local DATA = concat(content) + local posA, posB = find(DATA, CRLF2) + if posB then VERSION, CODE, STATUS, HEADER = PARSER_HTTP_RESPONSE(DATA) - if not CODE or not HEADER then - return nil, SSL.." can't resolvable protocol." - end + if not CODE or not HEADER then + return nil, SSL.." can't resolvable protocol." + end + if CODE == 302 or CODE == 301 then + return CODE, HEADER['Location'] or HEADER['location'] + end local Content_Encoding = HEADER['Content-Encoding'] or HEADER['content-encoding'] local Content_Length = toint(HEADER['Content-Length'] or HEADER['content-length']) local Chunked = HEADER['Transfer-Encoding'] or HEADER['transfer-encoding'] if not Content_Length and not Chunked then return nil, "Unsupported response body parsing." end - if Content_Length then - if (#DATA - posB) == Content_Length then + if Content_Length then + if (#DATA - posB) == Content_Length then local res = split(DATA, posB + 1, #DATA) if Content_Encoding == "gzip" then - res = gzuncompress(res) or res + res = gzuncompress(res) end - return CODE, res - end + return CODE, res + end local content = new_tab(8, 0) - content[#content+1] = split(DATA, posB + 1, #DATA) + content[#content+1] = split(DATA, posB + 1, #DATA) local Len = #content[1] - while 1 do - local data, len = sock_recv(sock, SSL, 65535) - if not data then - return nil, SSL.."[Content_Length] A peer of remote server close this connection." - end - insert(content, data) + while 1 do + local data, len = sock_recv(sock, SSL, 65535) + if not data then + return nil, SSL.."[Content_Length] A peer of remote server close this connection." + end + insert(content, data) Len = Len + len if Len >= Content_Length then local res = concat(content) if Content_Encoding == "gzip" then - res = gzuncompress(res) or res + res = gzuncompress(res) end return CODE, res end - end - end - if Chunked and Chunked == "chunked" then - local content = new_tab(8, 0) - if #DATA > posB then + end + end + if Chunked and Chunked == "chunked" then + local content = new_tab(8, 0) + if #DATA > posB then local buf = split(DATA, posB + 1, #DATA) data, len = RESPONSE_CHUNKED_PARSER(buf) if len == -1 then @@ -205,18 +208,18 @@ local function httpc_response(sock, SSL) local Pos = find(data, CRLF..(0)..CRLF2) local res = split(data, 1, Pos and Pos - #CRLF2 - 1 or -1) if Content_Encoding == "gzip" then - res = gzuncompress(res) or res + res = gzuncompress(res) end return CODE, res end insert(content, buf) - end - while 1 do - local data, len = sock_recv(sock, SSL, 65535) - if not data then - return CODE, SSL.."[chunked] A peer of remote server close this connection A." - end - insert(content, data) + end + while 1 do + local data, len = sock_recv(sock, SSL, 65535) + if not data then + return CODE, SSL.."[chunked] A peer of remote server close this connection A." + end + insert(content, data) local data, len = RESPONSE_CHUNKED_PARSER(concat(content)) if len == -1 then return nil, SSL.." 错误的http trunked. 2" @@ -225,14 +228,14 @@ local function httpc_response(sock, SSL) local Pos = find(data, CRLF..(0)..CRLF2) local res = split(data, 1, Pos and Pos - #CRLF2 - 1 or -1) if Content_Encoding == "gzip" then - res = gzuncompress(res) or res + res = gzuncompress(res) end return CODE, res end end - end - end - end + end + end + end end @@ -320,7 +323,7 @@ local function build_json_req (opt) insert(request, 'Accept: */*') insert(request, 'Accept-Encoding: gzip, identity') insert(request, 'Connection: keep-alive') - if type(opt.headers) == "table" then + if type(opt.headers) == "table" then for _, header in ipairs(opt.headers) do assert(lower(header[1]) ~= 'content-length', "please don't give Content-Length") assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") From 84a4e15b2bc63a70275f89ead1fc5cae01aa32e6 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 5 Dec 2019 09:43:08 +0800 Subject: [PATCH 461/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dssl=5Fsend=E5=8F=91?= =?UTF-8?q?=E9=80=81=E5=A4=A7=E6=95=B0=E6=8D=AE=E5=8C=85=E7=9A=84=E8=A1=8C?= =?UTF-8?q?=E4=B8=BA=E9=94=99=E8=AF=AF=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/TCP.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index e724c88c..5a05b0bb 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -153,7 +153,6 @@ function TCP:ssl_send(buf) if not wlen or wlen == #buf then return wlen == #buf end - buf = split(buf, wlen + 1, -1) self.SEND_IO = tcp_pop() local co = co_self() self.send_current_co = co_self() @@ -168,7 +167,6 @@ function TCP:ssl_send(buf) self.send_current_co = nil return co_wakeup(co, len == #buf) end - buf = split(buf, len + 1, -1) co_wait() end end) From 02ffcee6f22b880f2f6c0383e11cc75348e41cfe Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 8 Dec 2019 16:03:41 +0800 Subject: [PATCH 462/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E7=89=B9=E6=AE=8A=E6=83=85=E5=86=B5=E4=B8=8B=E7=9A=84segmentfa?= =?UTF-8?q?il?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lhttpparser/lhttpparser.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/luaclib/src/lhttpparser/lhttpparser.c b/luaclib/src/lhttpparser/lhttpparser.c index 41e8b63a..b2123d1a 100644 --- a/luaclib/src/lhttpparser/lhttpparser.c +++ b/luaclib/src/lhttpparser/lhttpparser.c @@ -17,12 +17,12 @@ lparser_response_chunked(lua_State *L){ struct phr_chunked_decoder decoder = { .consume_trailer = 1 }; int last = phr_decode_chunked(&decoder, buf, &buf_len); if (0 > last) { - luaL_pushresultsize(&B, 0); + luaL_pushresult(&B); lua_pushnil(L); lua_pushinteger(L, last); return 2; } - luaL_pushresultsize(&B, buf_len); + luaL_pushresult(&B); return 1; } From 6f04d64e263252987bd21b2b09e6d381f4b07fb6 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 8 Dec 2019 16:04:21 +0800 Subject: [PATCH 463/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0DB=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=E4=BF=9D=E6=8C=81=E7=9A=84=E8=AE=BE=E7=BD=AE=E6=96=B9=E5=BC=8F?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/DB/init.lua | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index dc096fd3..fb15874b 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -39,10 +39,8 @@ local function DB_CREATE (opt) db:set_timeout(3) local connect, err = db:connect(opt) if connect then - if not opt.INITIALIZATION then -- 设置连接超时时间 - assert(db:query(fmt('SET GLOBAL wait_timeout=%s', WAIT_TIMEOUT)), "SET GLOBAL wait_timeout faild.") - assert(db:query(fmt('SET GLOBAL interactive_timeout=%s', WAIT_TIMEOUT)), "SET GLOBAL interactive_timeout faild.") - end + assert(db:query(fmt('SET wait_timeout=%s', WAIT_TIMEOUT)), "SET wait_timeout faild.") + assert(db:query(fmt('SET interactive_timeout=%s', WAIT_TIMEOUT)), "SET interactive_timeout faild.") if opt.stmts then for rkey, stmt in pairs(opt.stmts) do assert(db:query(stmt), "["..stmt.."] 预编译失败.") From 6c91e06fa06f93de543dc8f90021ce3be361dd60 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sun, 8 Dec 2019 16:29:00 +0800 Subject: [PATCH 464/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=B8=80=E4=BA=9Badm?= =?UTF-8?q?in=E5=BA=93=E7=9A=84SQL=E6=8B=BC=E6=8E=A5=E6=96=B9=E5=BC=8F.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/db/token.lua | 4 ++-- lualib/admin/db/user.lua | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lualib/admin/db/token.lua b/lualib/admin/db/token.lua index ff13c997..baa43925 100644 --- a/lualib/admin/db/token.lua +++ b/lualib/admin/db/token.lua @@ -24,7 +24,7 @@ end -- Token 是否存在 function token.token_exists (db, token) - local ret, err = db:query(fmt([[SELECT uid, name, token FROM cfadmin_tokens WHERE token = '%s']], token)) + local ret, err = db:query(fmt([[SELECT uid, name, token FROM cfadmin_tokens WHERE token = '%s']], token:gsub("'", "\\'"))) if not ret or #ret == 0 then return end @@ -53,7 +53,7 @@ function token.token_to_userinfo (db, token) `cfadmin_tokens`.uid = `cfadmin_users`.id AND `cfadmin_roles`.id = `cfadmin_users`.role AND `cfadmin_tokens`.token = '%s' - LIMIT 1]], token))[1] + LIMIT 1]], token:gsub("'", "\\'")))[1] end return token diff --git a/lualib/admin/db/user.lua b/lualib/admin/db/user.lua index 68ba7c1c..9ea10681 100644 --- a/lualib/admin/db/user.lua +++ b/lualib/admin/db/user.lua @@ -69,9 +69,9 @@ end function user.user_exists (db, username, uid) local condition if username then - condition = fmt([[`cfadmin_users`.username = '%s']], username) + condition = fmt([[`cfadmin_users`.username = '%s']], username:gsub("'", "\\'")) elseif uid then - condition = fmt([[`cfadmin_users`.id = '%s']], uid) + condition = fmt([[`cfadmin_users`.id = '%s']], tostring(uid):gsub("'", "\\'")) else return end From 27299be8397a7da8cbf2eb40c5dd2e71e4188c38 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 10 Dec 2019 16:51:48 +0800 Subject: [PATCH 465/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=89=B9=E6=AE=8A?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E9=80=A0=E6=88=90=E7=9A=84=E8=BF=87=E6=BB=A4?= =?UTF-8?q?=E5=A4=B1=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/admin/init.lua b/lualib/admin/init.lua index b99ed72d..61597da8 100644 --- a/lualib/admin/init.lua +++ b/lualib/admin/init.lua @@ -70,7 +70,7 @@ function admin.init_page (app, db) -- 所有/api/admin路径下的接口都需要进行token header验权. app:before(function (content) local path = get_path(content) - if find(path, '^/api/admin/.+') then + if find(path, '^[/]+api[/]+admin[/]*.+') then -- 所有路由保持在这个路径之下会相对来说较为安全. local token = content['headers']['token'] or content['headers']['Token'] if not token then From 2f1e0ecb1a6dc4a149a83decaa2cc2095a532455 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Tue, 10 Dec 2019 22:55:43 +0800 Subject: [PATCH 466/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dadmin.view=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E6=B2=A1=E6=9C=89=E6=98=BE=E7=A4=BA=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E6=8F=90=E7=A4=BA=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/view.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lualib/admin/view.lua b/lualib/admin/view.lua index 509bad9b..832ca3d8 100644 --- a/lualib/admin/view.lua +++ b/lualib/admin/view.lua @@ -7,6 +7,7 @@ local httpctx = require "admin.httpctx" local user_token = require "admin.db.token" local permission = require "admin.db.permission" +local LOG = require "logging" local type = type local pcall = pcall @@ -49,6 +50,9 @@ function view.use (path, f) template.cache = {} end local ok, html = pcall(f, httpctx:new{content = content}, db) + if not ok then + LOG:ERROR(html) + end return html end) end @@ -61,6 +65,9 @@ function view.api (path, f) assert(db and app, "view.api need db session and http context.") return app:api(path, function (content) local ok, res = pcall(f, httpctx:new{content = content}, db) + if not ok then + LOG:ERROR(res) + end return res end) end @@ -82,6 +89,9 @@ function view.home(path, f) return utils.redirect(config.login_render) end local ok, res = pcall(f, httpctx:new{content = content}, db) + if not ok then + LOG:ERROR(res) + end return res end) end From c1d7cff007fb824902a8b1c196454d0e289284c7 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 14 Dec 2019 01:02:58 +0800 Subject: [PATCH 467/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0logging=E5=BA=93?= =?UTF-8?q?=E5=AD=97=E7=AC=A6=E4=B8=B2=E6=89=93=E5=8D=B0=E5=90=8E=E7=9A=84?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/logging/init.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua index 7de9a414..9810038a 100644 --- a/lualib/logging/init.lua +++ b/lualib/logging/init.lua @@ -95,7 +95,11 @@ local function info_fmt(...) if type(arg) == 'table' then tab[#tab+1] = table_format(arg) else - tab[#tab+1]= tostring(arg) + if type(arg) == 'string' then + tab[#tab+1]= '"' .. tostring(arg) .. '"' + else + tab[#tab+1]= tostring(arg) + end end if index >= len then break From b99d1d9f2a0ca15123608ed5a8c6764d0b2ea094 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 14 Dec 2019 01:06:16 +0800 Subject: [PATCH 468/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=86=85=E7=BD=AEsoc?= =?UTF-8?q?ket=E5=9C=A8=E5=86=99=E5=85=A5=E5=A4=A7=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E5=86=85=E5=AD=98=E5=8D=A0=E7=94=A8=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 34 +++++++++++++++++++--------------- lualib/internal/TCP.lua | 15 +++++++-------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 03a5dfb4..cc72e5eb 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -503,26 +503,28 @@ tcp_write(lua_State *L){ errno = 0; - int fd = lua_tointeger(L, 1); - if (0 >= fd) return 0; + size_t resp_len = 0; - const char *response = lua_tostring(L, 2); - if (!response) return 0; + int fd = lua_tointeger(L, 1); - int resp_len = lua_tointeger(L, 3); + const char *response = luaL_checklstring(L, 2, &resp_len); + if (!response) + return luaL_error(L, "tcp_write ERROR: attempt to write an empty string."); - do { + int offset = lua_tointeger(L, 3); - int wsize = write(fd, response, resp_len); + do { - if (wsize > 0) { lua_pushinteger(L, wsize); return 1; } + int wsize = write(fd, response + offset, resp_len - offset); - if (wsize < 0){ - if (errno == EINTR) continue; - if (errno == EWOULDBLOCK){ lua_pushinteger(L, 0); return 1;} - } + if (wsize > 0) { lua_pushinteger(L, wsize); return 1; } - } while (0); + if (wsize < 0){ + if (errno == EINTR) continue; + if (errno == EWOULDBLOCK){ lua_pushinteger(L, 0); return 1;} + } + + } while (0); return 0; } @@ -531,10 +533,12 @@ static int tcp_sslwrite(lua_State *L){ SSL *ssl = lua_touserdata(L, 1); - if (!ssl) return 0; + if (!ssl) + return 0; const char *response = lua_tostring(L, 2); - if (!response) return 0; + if (!response) + return luaL_error(L, "tcp_sslwrite ERROR: attempt to write an empty string."); int resp_len = lua_tointeger(L, 3); diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index 5a05b0bb..a443e5ba 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -109,31 +109,30 @@ end function TCP:send(buf) if self.ssl then Log:ERROR("Please use ssl_send method :)") - return + return nil, "Please use ssl_send method" end if type(buf) ~= 'string' or buf == '' then return end - local wlen = tcp_write(self.fd, buf, #buf) + local wlen = tcp_write(self.fd, buf, 0) if not wlen or wlen == #buf then return wlen == #buf end - buf = split(buf, wlen + 1, -1) local co = co_self() self.SEND_IO = tcp_pop() self.send_current_co = co_self() self.send_co = co_new(function ( ... ) while 1 do - local len = tcp_write(self.fd, buf, #buf) - if not len or len == #buf then + local len = tcp_write(self.fd, buf, wlen) + if not len or len + wlen == #buf then tcp_stop(self.SEND_IO) tcp_push(self.SEND_IO) self.SEND_IO = nil self.send_co = nil self.send_current_co = nil - return co_wakeup(co, len == #buf) + return co_wakeup(co, (len or 0) + wlen == #buf) end - buf = split(buf, len + 1, -1) + wlen = wlen + len co_wait() end end) @@ -144,7 +143,7 @@ end function TCP:ssl_send(buf) if not self.ssl then Log:ERROR("Please use send method :)") - return + return nil, "Please use send method :)" end if type(buf) ~= 'string' or buf == '' then return From 2b9a1b23eb9748d8d030d774268b7bf79b796b69 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 18 Dec 2019 10:03:14 +0800 Subject: [PATCH 469/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/db/view.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lualib/admin/db/view.lua b/lualib/admin/db/view.lua index 9e81cabb..50acb574 100644 --- a/lualib/admin/db/view.lua +++ b/lualib/admin/db/view.lua @@ -3,12 +3,12 @@ local view = {} -- 获取顶部栏 function view.get_headers (db) - return db:query([[ SELECT id, name, url FROM cfadmin_headers WHERE active = '1' ORDER BY id]]) + return db:query([[ SELECT id, name, url FROM cfadmin_headers WHERE active = '1' ORDER BY `id`]]) end -- 获取菜单栏 function view.get_menus (db, permission) - return db:query([[SELECT id, parent, name, url, icon, create_at, update_at FROM cfadmin_menus WHERE active = '1']]) + return db:query([[SELECT id, parent, name, url, icon, create_at, update_at FROM cfadmin_menus WHERE active = '1' ORDER BY `id`]]) end return view From 345d6b1682d3a322e7ff90e188ae700ea402882c Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 18 Dec 2019 10:03:27 +0800 Subject: [PATCH 470/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index aded50bd..c89e3b75 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,13 @@

                  +### 用户列表 + +
                    +
                  • +
                  • +
                  + ### 授权协议 (License agreement) [BSD LICENSE](https://github.com/CandyMi/core_framework/blob/master/LICENSE) From d66e89228ebbb7bada1c177ab2fe33f5b9f2efb4 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 18 Dec 2019 15:40:33 +0800 Subject: [PATCH 471/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c89e3b75..ac12a207 100644 --- a/README.md +++ b/README.md @@ -80,10 +80,10 @@ ### 用户列表 -
                    -
                  • -
                  • -
                  + |项目名称(Project Name)|项目图片(Project Logo)| + |:-:|:-:| + |商票易|![](https://raw.githubusercontent.com/wiki/CandyMi/core_framework/images/company-2.png)| + |爆款捕手|![](https://raw.githubusercontent.com/wiki/CandyMi/core_framework/images/company-1.png)| ### 授权协议 (License agreement) From 601faa7a5f61d62570404111d726c9cfb59a7269 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 19 Dec 2019 16:40:51 +0800 Subject: [PATCH 472/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0http=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E8=AF=B7=E6=B1=82=E5=8F=82=E6=95=B0=E7=9A=84=E4=B8=80?= =?UTF-8?q?=E4=BA=9B=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Form.lua | 4 ++-- lualib/protocol/http/init.lua | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lualib/httpd/Form.lua b/lualib/httpd/Form.lua index 0bb1fefa..949a7138 100644 --- a/lualib/httpd/Form.lua +++ b/lualib/httpd/Form.lua @@ -24,7 +24,7 @@ function form.get_args (path) end local s, e = find(path, '?'), #path if not s or e - s < 3 then - return + return end return form.urlencode(sub(path, s + 1, e)) end @@ -35,7 +35,7 @@ function form.urlencode(body) return end local ARGS = {} - for key, value in splite(body, "([^%?&]-)=([^%?&]+)") do + for key, value in splite(body, "([^&]-)=([^&]+)") do ARGS[urldecode(key)] = urldecode(value) end return ARGS diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index 2abe7caf..5a8ed2f7 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -21,6 +21,7 @@ local FILE_TYPE = form.FILE local ARGS_TYPE = form.ARGS local form_multipart = form.multipart local form_urlencode = form.urlencode +local form_argsencode = form.get_args local Cookie = require "httpd.Cookie" local clCookie = Cookie.clean -- 清理 @@ -115,7 +116,7 @@ local function PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEA if METHOD == "GET" then local spl_pos = find(PATH, '%?') if spl_pos and spl_pos < #PATH then - content['args'] = form_urlencode(PATH) + content['args'] = form_argsencode(PATH) end elseif METHOD == "POST" or METHOD == "PUT" then local body_len = toint(HEADER['Content-Length']) or toint(HEADER['Content-length']) From a96380965375e96df0ef038dd31258724f100d93 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 19 Dec 2019 16:41:13 +0800 Subject: [PATCH 473/956] =?UTF-8?q?=E8=A7=A3=E5=86=B3admin=E5=BA=93?= =?UTF-8?q?=E7=9A=84=E6=8E=92=E5=BA=8F=E9=94=99=E8=AF=AF=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/view.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lualib/admin/view.lua b/lualib/admin/view.lua index 832ca3d8..b280ff86 100644 --- a/lualib/admin/view.lua +++ b/lualib/admin/view.lua @@ -7,7 +7,8 @@ local httpctx = require "admin.httpctx" local user_token = require "admin.db.token" local permission = require "admin.db.permission" -local LOG = require "logging" +local log = require "logging" +local LOG = log:new { dump = true, path = "admin-view" } local type = type local pcall = pcall From ebe1f8ef4f0e0795e4554865b443e5ce326e6589 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 19 Dec 2019 16:41:38 +0800 Subject: [PATCH 474/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0crypt=E5=BA=93?= =?UTF-8?q?=E7=9A=84=E4=B8=80=E4=BA=9B=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/crypt/init.lua | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index 32958b70..fa29100b 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -106,8 +106,8 @@ function crypt.hmac_sha512 (key, text, hex) return hash end -function crypt.xor_str (str, sec, hex) - local hash = xor_str(str, sec) +function crypt.xor_str (key, text, hex) + local hash = xor_str(key, text) if hash and hex then return hexencode(hash) end @@ -178,12 +178,19 @@ function crypt.hexdecode (...) return hexdecode(...) end -function crypt.desencode (...) - return desencode(...) +function crypt.desencode (key, text, hex) + local hash = desencode(key, text) + if hash and hex then + return hexencode(hash) + end + return hash end -function crypt.desdecode (...) - return desdecode(...) +function crypt.desdecode (key, text, hex) + if hex then + text = hexdecode(text) + end + return desdecode(key, text) end function crypt.dhsecret (...) From e858af512d600865396b5f53cd4d711947813f41 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 30 Dec 2019 15:59:37 +0800 Subject: [PATCH 475/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dhttp=E8=AF=B7?= =?UTF-8?q?=E6=B1=82body=E5=8F=AF=E8=83=BD=E4=B8=8D=E5=AE=8C=E6=95=B4?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index 5a8ed2f7..22b5faf7 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -123,7 +123,7 @@ local function PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEA if body_len and body_len > 0 then local BODY = '' local RECV_BODY = true - local CRLF_START, CRLF_END = find(buffer, CRLF2) + local CRLF_START, CRLF_END = find(buffer, RE_CRLF2) if #buffer > CRLF_END then BODY = split(buffer, CRLF_END + 1, -1) if #BODY == body_len then @@ -133,6 +133,7 @@ local function PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEA if RECV_BODY then local buf_len = #BODY local buffers = body_len > 65535 and new_tab(ceil(body_len / 65535), 0) or {} + buffers[#buffers + 1] = BODY while 1 do local buf, len = sock:recv(65535) if not buf then From 062a61282e6c43ce50f99ad21f0d42288c205ebf Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 8 Jan 2020 16:19:58 +0800 Subject: [PATCH 476/956] =?UTF-8?q?websocket=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/TCP.lua | 158 ++++++++++++++------------- lualib/protocol/http/init.lua | 2 +- lualib/protocol/websocket/server.lua | 68 +++++++----- 3 files changed, 124 insertions(+), 104 deletions(-) diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index a443e5ba..1a9d1c2c 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -111,7 +111,7 @@ function TCP:send(buf) Log:ERROR("Please use ssl_send method :)") return nil, "Please use ssl_send method" end - if type(buf) ~= 'string' or buf == '' then + if not self.fd or type(buf) ~= 'string' or buf == '' then return end local wlen = tcp_write(self.fd, buf, 0) @@ -145,7 +145,7 @@ function TCP:ssl_send(buf) Log:ERROR("Please use send method :)") return nil, "Please use send method :)" end - if type(buf) ~= 'string' or buf == '' then + if not self.fd or type(buf) ~= 'string' or buf == '' then return end local wlen = tcp_ssl_write(self.ssl, buf, #buf) @@ -176,6 +176,9 @@ end function TCP:recv(bytes) if self.ssl then Log:ERROR("Please use ssl_recv method :)") + return nil, "Please use ssl_recv method :)" + end + if not self.fd then return end local data, len = tcp_read(self.fd, bytes) @@ -186,87 +189,90 @@ function TCP:recv(bytes) self.READ_IO = tcp_pop() self.read_current_co = co_self() self.read_co = co_new(function ( ... ) - local buf, len = tcp_read(self.fd, bytes) - if self.timer then - self.timer:stop() - self.timer = nil - end - tcp_push(self.READ_IO) - tcp_stop(self.READ_IO) - self.READ_IO = nil - self.read_co = nil - self.read_current_co =nil - if not buf then - return co_wakeup(co) - end - return co_wakeup(co, buf, len) + local buf, len = tcp_read(self.fd, bytes) + if self.timer then + self.timer:stop() + self.timer = nil + end + tcp_push(self.READ_IO) + tcp_stop(self.READ_IO) + self.READ_IO = nil + self.read_co = nil + self.read_current_co =nil + if not buf then + return co_wakeup(co) + end + return co_wakeup(co, buf, len) end) self.timer = ti_timeout(self._timeout, function ( ... ) - tcp_push(self.READ_IO) - tcp_stop(self.READ_IO) - self.timer = nil - self.read_co = nil - self.READ_IO = nil - self.read_current_co = nil - return co_wakeup(co, nil, "read timeout") + tcp_push(self.READ_IO) + tcp_stop(self.READ_IO) + self.timer = nil + self.read_co = nil + self.READ_IO = nil + self.read_current_co = nil + return co_wakeup(co, nil, "read timeout") end) tcp_start(self.READ_IO, self.fd, EVENT_READ, self.read_co) return co_wait() end function TCP:ssl_recv(bytes) - if not self.ssl then - Log:ERROR("Please use recv method :)") - return - end - local buf, len = tcp_sslread(self.ssl, bytes) - if not buf then - local co = co_self() - self.read_current_co = co_self() - self.READ_IO = tcp_pop() - self.read_co = co_new(function ( ... ) - while 1 do - local buf, len = tcp_sslread(self.ssl, bytes) - if not buf and not len then - if self.timer then - self.timer:stop() - self.timer = nil - end - tcp_push(self.READ_IO) - tcp_stop(self.READ_IO) - self.READ_IO = nil - self.read_co = nil - self.read_current_co = nil - return co_wakeup(co) - end - if buf and len then - if self.timer then - self.timer:stop() - self.timer = nil - end - tcp_push(self.READ_IO) - tcp_stop(self.READ_IO) - self.READ_IO = nil - self.read_co = nil - self.read_current_co = nil - return co_wakeup(co, buf, len) - end - co_wait() - end - end) - self.timer = ti_timeout(self._timeout, function ( ... ) - tcp_push(self.READ_IO) - tcp_stop(self.READ_IO) + if not self.ssl then + Log:ERROR("Please use recv method :)") + return nil, "Please use recv method :)" + end + if not self.fd then + return + end + local buf, len = tcp_sslread(self.ssl, bytes) + if not buf then + local co = co_self() + self.read_current_co = co_self() + self.READ_IO = tcp_pop() + self.read_co = co_new(function ( ... ) + while 1 do + local buf, len = tcp_sslread(self.ssl, bytes) + if not buf and not len then + if self.timer then + self.timer:stop() self.timer = nil - self.READ_IO = nil - self.read_co = nil - self.read_current_co = nil - return co_wakeup(co, nil, "read timeout") - end) - tcp_start(self.READ_IO, self.fd, EVENT_READ, self.read_co) - return co_wait() - end - return buf, len + end + tcp_push(self.READ_IO) + tcp_stop(self.READ_IO) + self.READ_IO = nil + self.read_co = nil + self.read_current_co = nil + return co_wakeup(co) + end + if buf and len then + if self.timer then + self.timer:stop() + self.timer = nil + end + tcp_push(self.READ_IO) + tcp_stop(self.READ_IO) + self.READ_IO = nil + self.read_co = nil + self.read_current_co = nil + return co_wakeup(co, buf, len) + end + co_wait() + end + end) + self.timer = ti_timeout(self._timeout, function ( ... ) + tcp_push(self.READ_IO) + tcp_stop(self.READ_IO) + self.timer = nil + self.READ_IO = nil + self.read_co = nil + self.read_current_co = nil + return co_wakeup(co, nil, "read timeout") + end) + tcp_start(self.READ_IO, self.fd, EVENT_READ, self.read_co) + return co_wait() + end + return buf, len end function TCP:listen(ip, port, cb) @@ -447,8 +453,8 @@ function TCP:close() end if self.sendfile_current_co then - co_wakeup(self.sendfile_current_co) - self.sendfile_current_co = nil + co_wakeup(self.sendfile_current_co) + self.sendfile_current_co = nil end if self._timeout then diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index 22b5faf7..26b6f614 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -245,7 +245,7 @@ local function Switch_Protocol(http, cls, sock, header, method, version, path, i if not ok then return end - return wsserver:new({cls = cls, sock = sock}):start() + return wsserver.start {cls = cls, sock = sock} end local function send_header (sock, header) diff --git a/lualib/protocol/websocket/server.lua b/lualib/protocol/websocket/server.lua index 0da15d50..66ffa288 100644 --- a/lualib/protocol/websocket/server.lua +++ b/lualib/protocol/websocket/server.lua @@ -6,8 +6,7 @@ local wbproto = require "protocol.websocket.protocol" local _recv_frame = wbproto.recv_frame local _send_frame = wbproto.send_frame -local log = require "logging" -local Log = log:new({ dump = true, path = 'protocol-websocket-server'}) +local Log = require "logging":new({ dump = true, path = 'protocol-websocket-server'}) local type = type local pcall = pcall @@ -17,18 +16,26 @@ local char = string.char local class = require "class" -local websocket = class("websocket-server") +local ws = class("ws") -function websocket:ctor(opt) - self._VERSION = '0.07' - self.cls = opt.cls +function ws:ctor(opt) self.sock = opt.sock - self.closed = nil - self.sock._timeout = nil + self.send_masked = nil + self.max_payload_len = 65535 +end + +-- 设置发送掩码 +function ws:set_send_masked(send_masked) + self.send_masked = send_masked +end + +-- 设置最大数据载荷长度 +function ws:set_max_payload_len(max_payload_len) + self.max_payload_len = max_payload_len end -- 异步消息发送 -function websocket:add_to_queue (f) +function ws:add_to_queue (f) if not self.queue then self.queue = {f} return cf_fork(function (...) @@ -37,7 +44,7 @@ function websocket:add_to_queue (f) if not ok then Log:ERROR(writeable) end - if not writeable then + if not ok or not writeable then break end end @@ -47,8 +54,8 @@ function websocket:add_to_queue (f) return self.queue and insert(self.queue, f) end --- send_text、send_binary -function websocket:send (data, binary) +-- 发送text/binary消息 +function ws:send (data, binary) if self.closed then return end @@ -59,7 +66,7 @@ function websocket:send (data, binary) end -- 发送close帧 -function websocket:close (data) +function ws:close(data) if self.closed then return end @@ -72,27 +79,33 @@ function websocket:close (data) end) end +local Websocket = { __Version__ = 1.0 } + -- Websocket Server 事件循环 -function websocket:start() - local sock = self.sock - local cls = self.cls:new { ws = self } +function Websocket.start(opt) + local sock = opt.sock + local w = ws:new { sock = sock } + + local cls = opt.cls:new { ws = w } local on_open = cls.on_open local on_message = cls.on_message local on_error = cls.on_error local on_close = cls.on_close - self.cls = nil - self.sock._timeout = cls.timeout - self.send_masked = cls.send_masked - self.max_payload_len = cls.max_payload_len or 65535 + + sock._timeout = cls.timeout or nil + local send_masked = cls.send_masked or nil + local max_payload_len = cls.max_payload_len or 65535 + w:set_send_masked(send_masked) + w:set_max_payload_len(max_payload_len) local ok, err = pcall(on_open, cls) if not ok then - self.sock = nil - return Log:ERROR(err) + Log:ERROR(err) + return end while 1 do - local data, typ, err = _recv_frame(sock, self.max_payload_len, self.send_masked) + local data, typ, err = _recv_frame(sock, max_payload_len, send_masked) if (not data and not typ) or typ == 'close' then - self.closed = true + w.closed = true if err and err ~= 'read timeout' then local ok, err = pcall(on_error, cls, err) if not ok then @@ -103,15 +116,16 @@ function websocket:start() if not ok then Log:ERROR(err) end - return + break end if typ == 'ping' then - self:add_to_queue(function () return _send_frame(sock, true, 0xA, data or '', self.max_payload_len, self.send_masked) end) + w:add_to_queue(function () return _send_frame(sock, true, 0xA, data or '', max_payload_len, send_masked) end) end if typ == 'text' or typ == 'binary' then cf_fork(on_message, cls, data, typ == 'binary') end end + return end -return websocket +return Websocket \ No newline at end of file From 540ced779f9e9ea42236c28add6782390e029a27 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 4 Feb 2020 14:07:16 +0800 Subject: [PATCH 477/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E7=A6=81=E6=AD=A2=E5=A4=9A=E7=BA=BF=E7=A8=8B=E4=B8=8B=E8=BD=BD?= =?UTF-8?q?=E9=9D=99=E6=80=81=E6=96=87=E4=BB=B6=E7=9A=84http=E5=A4=B4?= =?UTF-8?q?=E9=83=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index 26b6f614..f9ecb60b 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -372,7 +372,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) 'Server: ' .. (server or SERVER), 'Connection: close', 'Content-Type: ' .. REQUEST_MIME_RESPONSE('html'), - 'Content-Length: '..tostring(#data), + 'Content-Length: ' .. tostring(#data), }, CRLF), CRLF2, data})) return sock:close() end @@ -448,9 +448,9 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) local conten_type = REQUEST_MIME_RESPONSE(lower(file_type or '')) if not conten_type then header[#header+1] = 'Content-Disposition: attachment' -- 确保浏览器提示需要下载 - static = fmt('Content-Type: %s', 'application/octet-stream') + static = 'Content-Type: application/octet-stream' else - static = fmt('Content-Type: %s', conten_type..'; charset=utf-8') + static = 'Content-Type: ' .. conten_type .. '; charset=utf-8' end -- 如果是静态文件, 增加默认跨域访问支持 header[#header+1] = "Access-Control-Allow-Origin: *" @@ -495,6 +495,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) if body_len then header[#header+1] = 'Content-Length: '.. body_len end + header[#header+1] = 'Accept-Ranges: none' header[#header+1] = static end -- 不计算数据传输时间, 仅计算实际回调处理所用时间. From 2359e700c77e5d0c57a121c19756ad868edc9b76 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 27 Feb 2020 22:43:15 +0800 Subject: [PATCH 478/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0crypt=E5=BA=93,=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E4=B8=80=E7=B3=BB=E5=88=97=E5=8A=A0?= =?UTF-8?q?=E5=AF=86/=E8=A7=A3=E5=AF=86=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/Makefile | 6 +- luaclib/src/lcrypt/aes.c | 607 +++++++++++++++++ luaclib/src/lcrypt/attributes.h | 160 ----- luaclib/src/lcrypt/b64.c | 119 ++++ luaclib/src/lcrypt/crc.c | 10 +- luaclib/src/lcrypt/des.c | 407 ++++++++++++ luaclib/src/lcrypt/dh.c | 111 ++++ luaclib/src/lcrypt/hex.c | 46 ++ luaclib/src/lcrypt/hmac.c | 130 ++++ luaclib/src/lcrypt/hmac_ex.c | 216 ++++++ luaclib/src/lcrypt/lcrypt.c | 1090 +++---------------------------- luaclib/src/lcrypt/lcrypt.h | 66 ++ luaclib/src/lcrypt/md5.c | 308 --------- luaclib/src/lcrypt/md5.h | 37 -- luaclib/src/lcrypt/rsa.c | 178 +++++ luaclib/src/lcrypt/sha.c | 76 +++ luaclib/src/lcrypt/sha1.c | 223 ------- luaclib/src/lcrypt/sha2.c | 1007 ---------------------------- luaclib/src/lcrypt/sha2.h | 81 --- luaclib/src/lcrypt/url.c | 22 +- lualib/crypt/init.lua | 99 +++ private1024.pem | 15 + private2048.pem | 27 + private4096.pem | 51 ++ public1024.pem | 6 + public2048.pem | 9 + public4096.pem | 14 + script/test_crypt.lua | 346 ++++++++-- 28 files changed, 2590 insertions(+), 2877 deletions(-) create mode 100644 luaclib/src/lcrypt/aes.c delete mode 100644 luaclib/src/lcrypt/attributes.h create mode 100644 luaclib/src/lcrypt/b64.c create mode 100644 luaclib/src/lcrypt/des.c create mode 100644 luaclib/src/lcrypt/dh.c create mode 100644 luaclib/src/lcrypt/hex.c create mode 100644 luaclib/src/lcrypt/hmac.c create mode 100644 luaclib/src/lcrypt/hmac_ex.c create mode 100644 luaclib/src/lcrypt/lcrypt.h delete mode 100644 luaclib/src/lcrypt/md5.c delete mode 100644 luaclib/src/lcrypt/md5.h create mode 100644 luaclib/src/lcrypt/rsa.c create mode 100644 luaclib/src/lcrypt/sha.c delete mode 100644 luaclib/src/lcrypt/sha1.c delete mode 100644 luaclib/src/lcrypt/sha2.c delete mode 100644 luaclib/src/lcrypt/sha2.h create mode 100644 private1024.pem create mode 100644 private2048.pem create mode 100644 private4096.pem create mode 100644 public1024.pem create mode 100644 public2048.pem create mode 100644 public4096.pem diff --git a/luaclib/src/lcrypt/Makefile b/luaclib/src/lcrypt/Makefile index 7bd5637c..3c566ccb 100644 --- a/luaclib/src/lcrypt/Makefile +++ b/luaclib/src/lcrypt/Makefile @@ -9,12 +9,12 @@ default : CC = cc -CFLAGS = -O3 -Wall -shared -fPIC -fno-strict-aliasing -DLL = -lcore -llua +CFLAGS = -O3 -Wall -shared -fPIC -fno-strict-aliasing -ggdb +DLL = -lcore -llua -lcrypto INCLUDES = -I../../../ -I/usr/local/include LIBS = -L../ -L../../../ -L/usr/local/lib build: - @$(CC) -o lcrypt.so lcrypt.c crc.c md5.c url.c sha1.c sha2.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) + @$(CC) -o lcrypt.so lcrypt.c aes.c des.c dh.c rsa.c sha.c hmac.c hmac_ex.c b64.c crc.c url.c hex.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) @mv *.so ../../ diff --git a/luaclib/src/lcrypt/aes.c b/luaclib/src/lcrypt/aes.c new file mode 100644 index 00000000..a7e4f614 --- /dev/null +++ b/luaclib/src/lcrypt/aes.c @@ -0,0 +1,607 @@ +#include "lcrypt.h" + +#define aes_bit_to_ecb_evp(bit) (bit == 16 ? EVP_aes_128_ecb() : bit == 24 ? EVP_aes_192_ecb() : EVP_aes_256_ecb()) + +#define aes_bit_to_cbc_evp(bit) (bit == 16 ? EVP_aes_128_cbc() : bit == 24 ? EVP_aes_192_cbc() : EVP_aes_256_cbc()) + +#define aes_bit_to_cfb_evp(bit) (bit == 16 ? EVP_aes_128_cfb() : bit == 24 ? EVP_aes_192_cfb() : EVP_aes_256_cfb()) + +#define aes_bit_to_ofb_evp(bit) (bit == 16 ? EVP_aes_128_ofb() : bit == 24 ? EVP_aes_192_ofb() : EVP_aes_256_ofb()) + +#define aes_bit_to_ctr_evp(bit) (bit == 16 ? EVP_aes_128_ctr() : bit == 24 ? EVP_aes_192_ctr() : EVP_aes_256_ctr()) + +/* ------ 以下为aes加密函数 ------ */ + +int laes_ecb_encrypt(lua_State *L) { + lua_Integer bit = luaL_checkinteger(L, 1); + if (bit != 16 && bit != 24 && bit != 32) + return luaL_error(L, "Invalid bit"); + + const uint8_t* key = (const uint8_t*)luaL_checkstring(L, 2); + if (!key) + return luaL_error(L, "Invalid key"); + + size_t text_sz = 0; + const uint8_t* text = (const uint8_t*)luaL_checklstring(L, 3, &text_sz); + if (!text) + return luaL_error(L, "Invalid text"); + + const uint8_t* iv = (const uint8_t*)luaL_checkstring(L, 4); + if (!iv) + return luaL_error(L, "Invalid iv"); + + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + if (!ctx) + return luaL_error(L, "allocate EVP failed."); + + if (1 != EVP_EncryptInit_ex(ctx, aes_bit_to_ecb_evp(bit), NULL, key, iv)){ + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return luaL_error(L, "aes_init failed."); + } + + luaL_Buffer b; + size_t out_len = text_sz * 2; + size_t resuilt_len = 0; + unsigned char *out = (unsigned char *)luaL_buffinitsize(L, &b, text_sz * 2); + + int buffer_len = out_len; + if (0 == EVP_EncryptUpdate(ctx, out, &buffer_len, text, text_sz)) { + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + luaL_pushresultsize(&b, 0); + return luaL_error(L, "aes_update failed."); + } + resuilt_len += buffer_len; + + int final_len = resuilt_len; + if (0 == EVP_EncryptFinal_ex(ctx, out + final_len, &final_len)) { + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + luaL_pushresultsize(&b, 0); + return luaL_error(L, "aes_final failed."); + } + resuilt_len += final_len; + + luaL_pushresultsize(&b, resuilt_len); + + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return 1; +} + +int laes_cbc_encrypt(lua_State *L) { + lua_Integer bit = luaL_checkinteger(L, 1); + if (bit != 16 && bit != 24 && bit != 32) + return luaL_error(L, "Invalid bit"); + + const uint8_t* key = (const uint8_t*)luaL_checkstring(L, 2); + if (!key) + return luaL_error(L, "Invalid key"); + + size_t text_sz = 0; + const uint8_t* text = (const uint8_t*)luaL_checklstring(L, 3, &text_sz); + if (!text) + return luaL_error(L, "Invalid text"); + + const uint8_t* iv = (const uint8_t*)luaL_checkstring(L, 4); + if (!iv) + return luaL_error(L, "Invalid iv"); + + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + if (!ctx) + return luaL_error(L, "allocate EVP failed."); + + if (1 != EVP_EncryptInit_ex(ctx, aes_bit_to_cbc_evp(bit), NULL, key, iv)){ + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return luaL_error(L, "aes_init failed."); + } + + luaL_Buffer b; + size_t out_len = text_sz * 2; + size_t resuilt_len = 0; + unsigned char *out = (unsigned char *)luaL_buffinitsize(L, &b, text_sz * 2); + + int buffer_len = out_len; + if (0 == EVP_EncryptUpdate(ctx, out, &buffer_len, text, text_sz)) { + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + luaL_pushresultsize(&b, 0); + return luaL_error(L, "aes_update failed."); + } + resuilt_len += buffer_len; + + int final_len = resuilt_len; + if (0 == EVP_EncryptFinal_ex(ctx, out + final_len, &final_len)) { + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + luaL_pushresultsize(&b, 0); + return luaL_error(L, "aes_final failed."); + } + resuilt_len += final_len; + + luaL_pushresultsize(&b, resuilt_len); + + // printf("ENC: 需要%d长度的iv[%d], 实际长度为:%lu\n", EVP_CIPHER_iv_length(aes_bit_to_cbc_evp(bit)), bit, strlen(iv)); + + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return 1; +} + +/* + +int laes_cfb_encrypt(lua_State *L) { + lua_Integer bit = luaL_checkinteger(L, 1); + if (bit != 16 && bit != 24 && bit != 32) + return luaL_error(L, "Invalid bit"); + + const uint8_t* key = (const uint8_t*)luaL_checkstring(L, 2); + if (!key) + return luaL_error(L, "Invalid key"); + + size_t text_sz = 0; + const uint8_t* text = (const uint8_t*)luaL_checklstring(L, 3, &text_sz); + if (!text) + return luaL_error(L, "Invalid text"); + + const uint8_t* iv = (const uint8_t*)luaL_checkstring(L, 4); + if (!iv) + return luaL_error(L, "Invalid iv"); + + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + if (!ctx) + return luaL_error(L, "allocate EVP failed."); + + if (1 != EVP_EncryptInit_ex(ctx, aes_bit_to_cfb_evp(bit), NULL, key, iv)){ + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return luaL_error(L, "aes_init failed."); + } + + luaL_Buffer b; + size_t out_len = text_sz * 2; + size_t resuilt_len = 0; + unsigned char *out = (unsigned char *)luaL_buffinitsize(L, &b, text_sz * 2); + + int buffer_len = out_len; + if (0 == EVP_EncryptUpdate(ctx, out, &buffer_len, text, text_sz)) { + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + luaL_pushresultsize(&b, 0); + return luaL_error(L, "aes_update failed."); + } + resuilt_len += buffer_len; + + int final_len = resuilt_len; + if (0 == EVP_EncryptFinal_ex(ctx, out + final_len, &final_len)) { + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + luaL_pushresultsize(&b, 0); + return luaL_error(L, "aes_final failed."); + } + resuilt_len += final_len; + + luaL_pushresultsize(&b, resuilt_len); + + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return 1; +} + +int laes_ofb_encrypt(lua_State *L) { + lua_Integer bit = luaL_checkinteger(L, 1); + if (bit != 16 && bit != 24 && bit != 32) + return luaL_error(L, "Invalid bit"); + + const uint8_t* key = (const uint8_t*)luaL_checkstring(L, 2); + if (!key) + return luaL_error(L, "Invalid key"); + + size_t text_sz = 0; + const uint8_t* text = (const uint8_t*)luaL_checklstring(L, 3, &text_sz); + if (!text) + return luaL_error(L, "Invalid text"); + + const uint8_t* iv = (const uint8_t*)luaL_checkstring(L, 4); + if (!iv) + return luaL_error(L, "Invalid iv"); + + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + if (!ctx) + return luaL_error(L, "allocate EVP failed."); + + if (1 != EVP_EncryptInit_ex(ctx, aes_bit_to_ofb_evp(bit), NULL, key, iv)){ + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return luaL_error(L, "aes_init failed."); + } + + luaL_Buffer b; + size_t out_len = text_sz * 2; + size_t resuilt_len = 0; + unsigned char *out = (unsigned char *)luaL_buffinitsize(L, &b, text_sz * 2); + + int buffer_len = out_len; + if (0 == EVP_EncryptUpdate(ctx, out, &buffer_len, text, text_sz)) { + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + luaL_pushresultsize(&b, 0); + return luaL_error(L, "aes_update failed."); + } + resuilt_len += buffer_len; + + int final_len = resuilt_len; + if (0 == EVP_EncryptFinal_ex(ctx, out + final_len, &final_len)) { + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + luaL_pushresultsize(&b, 0); + return luaL_error(L, "aes_final failed."); + } + resuilt_len += final_len; + + luaL_pushresultsize(&b, resuilt_len); + + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return 1; +} + +int laes_ctr_encrypt(lua_State *L) { + lua_Integer bit = luaL_checkinteger(L, 1); + if (bit != 16 && bit != 24 && bit != 32) + return luaL_error(L, "Invalid bit"); + + const uint8_t* key = (const uint8_t*)luaL_checkstring(L, 2); + if (!key) + return luaL_error(L, "Invalid key"); + + size_t text_sz = 0; + const uint8_t* text = (const uint8_t*)luaL_checklstring(L, 3, &text_sz); + if (!text) + return luaL_error(L, "Invalid text"); + + const uint8_t* iv = (const uint8_t*)luaL_checkstring(L, 4); + if (!iv) + return luaL_error(L, "Invalid iv"); + + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + if (!ctx) + return luaL_error(L, "allocate EVP failed."); + + if (1 != EVP_EncryptInit_ex(ctx, aes_bit_to_ctr_evp(bit), NULL, key, iv)){ + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return luaL_error(L, "aes_init failed."); + } + + luaL_Buffer b; + size_t out_len = text_sz * 2; + size_t resuilt_len = 0; + unsigned char *out = (unsigned char *)luaL_buffinitsize(L, &b, text_sz * 2); + + int buffer_len = out_len; + if (0 == EVP_EncryptUpdate(ctx, out, &buffer_len, text, text_sz)) { + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + luaL_pushresultsize(&b, 0); + return luaL_error(L, "aes_update failed."); + } + resuilt_len += buffer_len; + + int final_len = resuilt_len; + if (0 == EVP_EncryptFinal_ex(ctx, out + final_len, &final_len)) { + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + luaL_pushresultsize(&b, 0); + return luaL_error(L, "aes_final failed."); + } + resuilt_len += final_len; + + luaL_pushresultsize(&b, resuilt_len); + + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return 1; +} +*/ + +/* ------ 以下为aes解密函数 ------ */ + + +int laes_ecb_decrypt(lua_State *L) { + lua_Integer bit = luaL_checkinteger(L, 1); + if (bit != 16 && bit != 24 && bit != 32) + return luaL_error(L, "Invalid bit"); + + const uint8_t* key = (const uint8_t*)luaL_checkstring(L, 2); + if (!key) + return luaL_error(L, "Invalid key"); + + size_t text_sz = 0; + const uint8_t* text = (const uint8_t*)luaL_checklstring(L, 3, &text_sz); + if (!text) + return luaL_error(L, "Invalid text"); + + const uint8_t* iv = (const uint8_t*)luaL_checkstring(L, 4); + if (!iv) + return luaL_error(L, "Invalid iv"); + + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + if (!ctx) + return luaL_error(L, "allocate EVP failed."); + + if (1 != EVP_DecryptInit_ex(ctx, aes_bit_to_ecb_evp(bit), NULL, key, iv)){ + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return luaL_error(L, "aes_init failed."); + } + + luaL_Buffer b; + size_t out_len = text_sz * 2; + size_t resuilt_len = 0; + unsigned char *out = (unsigned char *)luaL_buffinitsize(L, &b, text_sz * 2); + + int buffer_len = out_len; + if (0 == EVP_DecryptUpdate(ctx, out, &buffer_len, text, text_sz)) { + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + luaL_pushresultsize(&b, 0); + return luaL_error(L, "aes_update failed."); + } + resuilt_len += buffer_len; + + int final_len = resuilt_len; + if (0 == EVP_DecryptFinal_ex(ctx, out + final_len, &final_len)) { + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + luaL_pushresultsize(&b, 0); + return luaL_error(L, "aes_final failed."); + } + resuilt_len += final_len; + + luaL_pushresultsize(&b, resuilt_len); + + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return 1; +} + +int laes_cbc_decrypt(lua_State *L) { + lua_Integer bit = luaL_checkinteger(L, 1); + if (bit != 16 && bit != 24 && bit != 32) + return luaL_error(L, "Invalid bit"); + + const uint8_t* key = (const uint8_t*)luaL_checkstring(L, 2); + if (!key) + return luaL_error(L, "Invalid key"); + + size_t text_sz = 0; + const uint8_t* text = (const uint8_t*)luaL_checklstring(L, 3, &text_sz); + if (!text) + return luaL_error(L, "Invalid text"); + + const uint8_t* iv = (const uint8_t*)luaL_checkstring(L, 4); + if (!iv) + return luaL_error(L, "Invalid iv"); + + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + if (!ctx) + return luaL_error(L, "allocate EVP failed."); + + if (1 != EVP_DecryptInit_ex(ctx, aes_bit_to_cbc_evp(bit), NULL, key, iv)){ + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return luaL_error(L, "aes_init failed."); + } + + EVP_CIPHER_CTX_set_key_length(ctx, bit); + + luaL_Buffer b; + size_t out_len = text_sz * 2; + size_t resuilt_len = 0; + unsigned char *out = (unsigned char *)luaL_buffinitsize(L, &b, text_sz * 2); + + int buffer_len = out_len; + if (0 == EVP_DecryptUpdate(ctx, out, &buffer_len, text, text_sz)) { + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + luaL_pushresultsize(&b, 0); + return luaL_error(L, "aes_update failed."); + } + resuilt_len += buffer_len; + + int final_len = resuilt_len; + if (0 == EVP_DecryptFinal_ex(ctx, out + final_len, &final_len)) { + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + luaL_pushresultsize(&b, 0); + return luaL_error(L, "aes_final failed."); + } + resuilt_len += final_len; + + luaL_pushresultsize(&b, resuilt_len); + + // printf("DEC: 需要%d长度的iv[%d], 实际长度为:%lu\n", EVP_CIPHER_iv_length(aes_bit_to_cbc_evp(bit)), bit, strlen(iv)); + + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return 1; +} + +/* +int laes_cfb_decrypt(lua_State *L) { + lua_Integer bit = luaL_checkinteger(L, 1); + if (bit != 16 && bit != 24 && bit != 32) + return luaL_error(L, "Invalid bit"); + + const uint8_t* key = (const uint8_t*)luaL_checkstring(L, 2); + if (!key) + return luaL_error(L, "Invalid key"); + + size_t text_sz = 0; + const uint8_t* text = (const uint8_t*)luaL_checklstring(L, 3, &text_sz); + if (!text) + return luaL_error(L, "Invalid text"); + + const uint8_t* iv = (const uint8_t*)luaL_checkstring(L, 4); + if (!iv) + return luaL_error(L, "Invalid iv"); + + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + if (!ctx) + return luaL_error(L, "allocate EVP failed."); + + if (1 != EVP_DecryptInit_ex(ctx, aes_bit_to_cfb_evp(bit), NULL, key, iv)){ + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return luaL_error(L, "aes_init failed."); + } + + luaL_Buffer b; + size_t out_len = text_sz * 2; + size_t resuilt_len = 0; + unsigned char *out = (unsigned char *)luaL_buffinitsize(L, &b, text_sz * 2); + + int buffer_len = out_len; + if (0 == EVP_DecryptUpdate(ctx, out, &buffer_len, text, text_sz)) { + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + luaL_pushresultsize(&b, 0); + return luaL_error(L, "aes_update failed."); + } + resuilt_len += buffer_len; + + int final_len = resuilt_len; + if (0 == EVP_DecryptFinal_ex(ctx, out + final_len, &final_len)) { + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + luaL_pushresultsize(&b, 0); + return luaL_error(L, "aes_final failed."); + } + resuilt_len += final_len; + + luaL_pushresultsize(&b, resuilt_len); + + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return 1; +} + +int laes_ofb_decrypt(lua_State *L) { + lua_Integer bit = luaL_checkinteger(L, 1); + if (bit != 16 && bit != 24 && bit != 32) + return luaL_error(L, "Invalid bit"); + + const uint8_t* key = (const uint8_t*)luaL_checkstring(L, 2); + if (!key) + return luaL_error(L, "Invalid key"); + + size_t text_sz = 0; + const uint8_t* text = (const uint8_t*)luaL_checklstring(L, 3, &text_sz); + if (!text) + return luaL_error(L, "Invalid text"); + + const uint8_t* iv = (const uint8_t*)luaL_checkstring(L, 4); + if (!iv) + return luaL_error(L, "Invalid iv"); + + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + if (!ctx) + return luaL_error(L, "allocate EVP failed."); + + if (1 != EVP_DecryptInit_ex(ctx, aes_bit_to_ofb_evp(bit), NULL, key, iv)){ + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return luaL_error(L, "aes_init failed."); + } + + luaL_Buffer b; + size_t out_len = text_sz * 2; + size_t resuilt_len = 0; + unsigned char *out = (unsigned char *)luaL_buffinitsize(L, &b, text_sz * 2); + + int buffer_len = out_len; + if (0 == EVP_DecryptUpdate(ctx, out, &buffer_len, text, text_sz)) { + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + luaL_pushresultsize(&b, 0); + return luaL_error(L, "aes_update failed."); + } + resuilt_len += buffer_len; + + int final_len = resuilt_len; + if (0 == EVP_DecryptFinal_ex(ctx, out + final_len, &final_len)) { + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + luaL_pushresultsize(&b, 0); + return luaL_error(L, "aes_final failed."); + } + resuilt_len += final_len; + + luaL_pushresultsize(&b, resuilt_len); + + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return 1; +} + +int laes_ctr_decrypt(lua_State *L) { + lua_Integer bit = luaL_checkinteger(L, 1); + if (bit != 16 && bit != 24 && bit != 32) + return luaL_error(L, "Invalid bit"); + + const uint8_t* key = (const uint8_t*)luaL_checkstring(L, 2); + if (!key) + return luaL_error(L, "Invalid key"); + + size_t text_sz = 0; + const uint8_t* text = (const uint8_t*)luaL_checklstring(L, 3, &text_sz); + if (!text) + return luaL_error(L, "Invalid text"); + + const uint8_t* iv = (const uint8_t*)luaL_checkstring(L, 4); + if (!iv) + return luaL_error(L, "Invalid iv"); + + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + if (!ctx) + return luaL_error(L, "allocate EVP failed."); + + if (1 != EVP_DecryptInit_ex(ctx, aes_bit_to_ctr_evp(bit), NULL, key, iv)){ + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return luaL_error(L, "aes_init failed."); + } + + luaL_Buffer b; + size_t out_len = text_sz * 2; + size_t resuilt_len = 0; + unsigned char *out = (unsigned char *)luaL_buffinitsize(L, &b, text_sz * 2); + + int buffer_len = out_len; + if (0 == EVP_DecryptUpdate(ctx, out, &buffer_len, text, text_sz)) { + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + luaL_pushresultsize(&b, 0); + return luaL_error(L, "aes_update failed."); + } + resuilt_len += buffer_len; + + int final_len = resuilt_len; + if (0 == EVP_DecryptFinal_ex(ctx, out + final_len, &final_len)) { + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + luaL_pushresultsize(&b, 0); + return luaL_error(L, "aes_final failed."); + } + resuilt_len += final_len; + + luaL_pushresultsize(&b, resuilt_len); + + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return 1; +} +*/ \ No newline at end of file diff --git a/luaclib/src/lcrypt/attributes.h b/luaclib/src/lcrypt/attributes.h deleted file mode 100644 index 866e143a..00000000 --- a/luaclib/src/lcrypt/attributes.h +++ /dev/null @@ -1,160 +0,0 @@ -/* attributes.h; some GCC extensions wrapped up so they do nothing elsewhere - * Copyright 2009-2010 Rob Kendrick , distributed under MIT - * - * Of course, some compilers define __GNUC__ and a GCC version number, and - * aren't GCC. If they choke when you use this file, report a bug to your - * compiler vendor. - */ - -#ifndef _GCC_ATTRIBUTES_ -#define _GCC_ATTRIBUTES_ - -#ifdef __GNUC__ -# define GCC_VERSION (__GNUC__ * 10000 \ - + __GNUC_MINOR__ * 100 \ - + __GNUC_PATCHLEVEL__) -#else -# define GCC_VERSION 0 -#endif - -/* Symbol behaviors *********************************************************/ - -#if GCC_VERSION >= 20500 -# define _NORETURN __attribute__ ((noreturn)) -#else -# define _NORETURN -#endif - -#if GCC_VERSION >= 20700 -# define _CONSTRUCTOR __attribute__ ((constructor)) -# define _DESTRUCTOR __attribute__ ((destructor)) -# define _WEAK __attribute__ ((weak)) -#else -# define _CONSTRUCTOR -# define _DESTRUCTOR -# define _WEAK -#endif - -#if GCC_VERSION >= 30000 -# define _MALLOC __attribute__ ((malloc)) -#else -# define _MALLOC -#endif - -#if GCC_VERSION >= 30300 -# define _CLEANUP(x) __attribute__ ((cleanup(x))) -#else -# define _CLEANUP(x) -#endif - -/* Parameter and call checking **********************************************/ - -#if GCC_VERSION >= 20300 -# define _FORMAT(...) __attribute__ ((format(__VA_ARGS__))) -#else -# define _FORMAT(...) -#endif - -#if GCC_VERSION >= 20700 -# define _UNUSED __attribute__ ((unused)) -#else -# define _UNUSED -#endif - -#if GCC_VERSION >= 20800 -# define _FORMAT_ARG(x) __attribute__ ((format_arg(x))) -#else -# define _FORMAT_ARG(x) -#endif - -#if GCC_VERSION >= 30100 -# define _DEPRECATED __attribute__ ((deprecated)) -# define _USED __attribute__ ((used)) -#else -# define _DEPRECATED -# define _USED -#endif - -#if GCC_VERSION >= 30300 -# define _NONNULL(...) __attribute__ ((nonnull(__VA_ARGS__))) -# define _WARN_UNUSED_RESULT __attribute__ ((warn_unused_result)) -#else -# define _NONNULL(...) -# define _WARN_UNUSED_RESULT -#endif - -#if GCC_VERSION >= 40400 -# define _BOUNDED(...) __attribute ((__bounded__(__VAR_ARGS__))) -#else -# define _BOUNDED(...) -#endif - -/* Performance and optimisation-related *************************************/ - -#if GCC_VERSION >= 20500 -# define _CONST __attribute__ ((const)) -#else -# define _CONST -#endif - -#if GCC_VERSION >= 20700 -# define _ALIGNED(x) __attribute__ ((aligned(x))) -# define _PACKED __attribute__ ((packed)) -#else -# define _ALIGNED(x) -# define _PACKED -#endif - -#if GCC_VERSION >= 30000 -# define _PURE __attribute__ ((pure)) -#else -# define _PURE -#endif - -#if GCC_VERSION >= 30100 -# define _NO_INLINE __attribute__ ((noinline)) -#else -# define _NO_INLINE -#endif - -#if GCC_VERSION >= 30200 -# define _NOTHROW __attribute__ ((nothrow)) -#else -# define _NOTHROW -#endif - -#if GCC_VERSION >= 40300 -# define _HOT __attribute__ ((hot)) -# define _COLD __attribute__ ((cold)) -#else -# define _HOT -# define _COLD -#endif - -#if GCC_VERSION >= 40400 -# define _OPTIMISE(x) __attribute__ ((optimize(x))) -# define _TARGET(x) __attribute__ ((target(x))) -# define _SSEREGPARM __attribute__ ((sseregparm)) -#else -# define _OPTIMISE(x) -# define _TARGET(x) -# define _SSEREGPARM -#endif - -/* We don't have any version support information for these */ - -#if __GNUC__ -# define _ALWAYS_INLINE __attribute__ ((always_inline)) -# define _FLATTEN __attribute__ ((flatten)) -#else -# define _ALWAYS_INLINE -# define _FLATTEN -#endif - -/****************************************************************************/ - -#ifdef __GNUC__ -# undef GCC_VERSION -#endif - -#endif /* _GCC_ATTRIBUTES_ */ diff --git a/luaclib/src/lcrypt/b64.c b/luaclib/src/lcrypt/b64.c new file mode 100644 index 00000000..c46512f9 --- /dev/null +++ b/luaclib/src/lcrypt/b64.c @@ -0,0 +1,119 @@ +#include "lcrypt.h" + +static inline int b64index(uint8_t c) { + static const int decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51}; + int decoding_size = sizeof(decoding)/sizeof(decoding[0]); + if (c<43) { + return -1; + } + c -= 43; + if (c>=decoding_size) + return -1; + return decoding[c]; +} + + +int lb64encode(lua_State *L) { + static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + size_t sz = 0; + const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 1, &sz); + int encode_sz = (sz + 2)/3*4; + char tmp[SMALL_CHUNK]; + char *buffer = tmp; + if (encode_sz > SMALL_CHUNK) { + buffer = lua_newuserdata(L, encode_sz); + } + int i,j; + j=0; + for (i=0;i<(int)sz-2;i+=3) { + uint32_t v = text[i] << 16 | text[i+1] << 8 | text[i+2]; + buffer[j] = encoding[v >> 18]; + buffer[j+1] = encoding[(v >> 12) & 0x3f]; + buffer[j+2] = encoding[(v >> 6) & 0x3f]; + buffer[j+3] = encoding[(v) & 0x3f]; + j+=4; + } + int padding = sz-i; + uint32_t v; + switch(padding) { + case 1 : + v = text[i]; + buffer[j] = encoding[v >> 2]; + buffer[j+1] = encoding[(v & 3) << 4]; + buffer[j+2] = '='; + buffer[j+3] = '='; + break; + case 2 : + v = text[i] << 8 | text[i+1]; + buffer[j] = encoding[v >> 10]; + buffer[j+1] = encoding[(v >> 4) & 0x3f]; + buffer[j+2] = encoding[(v & 0xf) << 2]; + buffer[j+3] = '='; + break; + } + lua_pushlstring(L, buffer, encode_sz); + return 1; +} + +int lb64decode(lua_State *L) { + size_t sz = 0; + const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 1, &sz); + int decode_sz = (sz+3)/4*3; + char tmp[SMALL_CHUNK]; + char *buffer = tmp; + if (decode_sz > SMALL_CHUNK) { + buffer = lua_newuserdata(L, decode_sz); + } + int i,j; + int output = 0; + for (i=0;i=sz) { + return luaL_error(L, "Invalid base64 text"); + } + c[j] = b64index(text[i]); + if (c[j] == -1) { + ++i; + continue; + } + if (c[j] == -2) { + ++padding; + } + ++i; + ++j; + } + uint32_t v; + switch (padding) { + case 0: + v = (unsigned)c[0] << 18 | c[1] << 12 | c[2] << 6 | c[3]; + buffer[output] = v >> 16; + buffer[output+1] = (v >> 8) & 0xff; + buffer[output+2] = v & 0xff; + output += 3; + break; + case 1: + if (c[3] != -2 || (c[2] & 3)!=0) { + return luaL_error(L, "Invalid base64 text"); + } + v = (unsigned)c[0] << 10 | c[1] << 4 | c[2] >> 2 ; + buffer[output] = v >> 8; + buffer[output+1] = v & 0xff; + output += 2; + break; + case 2: + if (c[3] != -2 || c[2] != -2 || (c[1] & 0xf) !=0) { + return luaL_error(L, "Invalid base64 text"); + } + v = (unsigned)c[0] << 2 | c[1] >> 4; + buffer[output] = v; + ++ output; + break; + default: + return luaL_error(L, "Invalid base64 text"); + } + } + lua_pushlstring(L, buffer, output); + return 1; +} \ No newline at end of file diff --git a/luaclib/src/lcrypt/crc.c b/luaclib/src/lcrypt/crc.c index 107c4981..0c9eedbc 100644 --- a/luaclib/src/lcrypt/crc.c +++ b/luaclib/src/lcrypt/crc.c @@ -1,6 +1,4 @@ -#define LUA_LIB - -#include "../../../src/core.h" +#include "lcrypt.h" /* CRC32 TAB */ static uint32_t CRC32[] = { @@ -202,8 +200,7 @@ static uint64_t CRC64[] = { 0x536fa08fdfd90e51, 0x29b7d047efec8728, }; -int -lcrc32(lua_State *L){ +int lcrc32(lua_State *L){ size_t len; const char *str = luaL_checklstring(L, 1, &len); @@ -215,8 +212,7 @@ lcrc32(lua_State *L){ return 1; }; -int -lcrc64(lua_State *L){ +int lcrc64(lua_State *L){ size_t len; const char *str = luaL_checklstring(L, 1, &len); diff --git a/luaclib/src/lcrypt/des.c b/luaclib/src/lcrypt/des.c new file mode 100644 index 00000000..8d82c519 --- /dev/null +++ b/luaclib/src/lcrypt/des.c @@ -0,0 +1,407 @@ +#include "lcrypt.h" + +/* the eight DES S-boxes */ + +static uint32_t SB1[64] = { + 0x01010400, 0x00000000, 0x00010000, 0x01010404, + 0x01010004, 0x00010404, 0x00000004, 0x00010000, + 0x00000400, 0x01010400, 0x01010404, 0x00000400, + 0x01000404, 0x01010004, 0x01000000, 0x00000004, + 0x00000404, 0x01000400, 0x01000400, 0x00010400, + 0x00010400, 0x01010000, 0x01010000, 0x01000404, + 0x00010004, 0x01000004, 0x01000004, 0x00010004, + 0x00000000, 0x00000404, 0x00010404, 0x01000000, + 0x00010000, 0x01010404, 0x00000004, 0x01010000, + 0x01010400, 0x01000000, 0x01000000, 0x00000400, + 0x01010004, 0x00010000, 0x00010400, 0x01000004, + 0x00000400, 0x00000004, 0x01000404, 0x00010404, + 0x01010404, 0x00010004, 0x01010000, 0x01000404, + 0x01000004, 0x00000404, 0x00010404, 0x01010400, + 0x00000404, 0x01000400, 0x01000400, 0x00000000, + 0x00010004, 0x00010400, 0x00000000, 0x01010004 +}; + +static uint32_t SB2[64] = { + 0x80108020, 0x80008000, 0x00008000, 0x00108020, + 0x00100000, 0x00000020, 0x80100020, 0x80008020, + 0x80000020, 0x80108020, 0x80108000, 0x80000000, + 0x80008000, 0x00100000, 0x00000020, 0x80100020, + 0x00108000, 0x00100020, 0x80008020, 0x00000000, + 0x80000000, 0x00008000, 0x00108020, 0x80100000, + 0x00100020, 0x80000020, 0x00000000, 0x00108000, + 0x00008020, 0x80108000, 0x80100000, 0x00008020, + 0x00000000, 0x00108020, 0x80100020, 0x00100000, + 0x80008020, 0x80100000, 0x80108000, 0x00008000, + 0x80100000, 0x80008000, 0x00000020, 0x80108020, + 0x00108020, 0x00000020, 0x00008000, 0x80000000, + 0x00008020, 0x80108000, 0x00100000, 0x80000020, + 0x00100020, 0x80008020, 0x80000020, 0x00100020, + 0x00108000, 0x00000000, 0x80008000, 0x00008020, + 0x80000000, 0x80100020, 0x80108020, 0x00108000 +}; + +static uint32_t SB3[64] = { + 0x00000208, 0x08020200, 0x00000000, 0x08020008, + 0x08000200, 0x00000000, 0x00020208, 0x08000200, + 0x00020008, 0x08000008, 0x08000008, 0x00020000, + 0x08020208, 0x00020008, 0x08020000, 0x00000208, + 0x08000000, 0x00000008, 0x08020200, 0x00000200, + 0x00020200, 0x08020000, 0x08020008, 0x00020208, + 0x08000208, 0x00020200, 0x00020000, 0x08000208, + 0x00000008, 0x08020208, 0x00000200, 0x08000000, + 0x08020200, 0x08000000, 0x00020008, 0x00000208, + 0x00020000, 0x08020200, 0x08000200, 0x00000000, + 0x00000200, 0x00020008, 0x08020208, 0x08000200, + 0x08000008, 0x00000200, 0x00000000, 0x08020008, + 0x08000208, 0x00020000, 0x08000000, 0x08020208, + 0x00000008, 0x00020208, 0x00020200, 0x08000008, + 0x08020000, 0x08000208, 0x00000208, 0x08020000, + 0x00020208, 0x00000008, 0x08020008, 0x00020200 +}; + +static uint32_t SB4[64] = { + 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802080, 0x00800081, 0x00800001, 0x00002001, + 0x00000000, 0x00802000, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00800080, 0x00800001, + 0x00000001, 0x00002000, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002001, 0x00002080, + 0x00800081, 0x00000001, 0x00002080, 0x00800080, + 0x00002000, 0x00802080, 0x00802081, 0x00000081, + 0x00800080, 0x00800001, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00000000, 0x00802000, + 0x00002080, 0x00800080, 0x00800081, 0x00000001, + 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802081, 0x00000081, 0x00000001, 0x00002000, + 0x00800001, 0x00002001, 0x00802080, 0x00800081, + 0x00002001, 0x00002080, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002000, 0x00802080 +}; + +static uint32_t SB5[64] = { + 0x00000100, 0x02080100, 0x02080000, 0x42000100, + 0x00080000, 0x00000100, 0x40000000, 0x02080000, + 0x40080100, 0x00080000, 0x02000100, 0x40080100, + 0x42000100, 0x42080000, 0x00080100, 0x40000000, + 0x02000000, 0x40080000, 0x40080000, 0x00000000, + 0x40000100, 0x42080100, 0x42080100, 0x02000100, + 0x42080000, 0x40000100, 0x00000000, 0x42000000, + 0x02080100, 0x02000000, 0x42000000, 0x00080100, + 0x00080000, 0x42000100, 0x00000100, 0x02000000, + 0x40000000, 0x02080000, 0x42000100, 0x40080100, + 0x02000100, 0x40000000, 0x42080000, 0x02080100, + 0x40080100, 0x00000100, 0x02000000, 0x42080000, + 0x42080100, 0x00080100, 0x42000000, 0x42080100, + 0x02080000, 0x00000000, 0x40080000, 0x42000000, + 0x00080100, 0x02000100, 0x40000100, 0x00080000, + 0x00000000, 0x40080000, 0x02080100, 0x40000100 +}; + +static uint32_t SB6[64] = { + 0x20000010, 0x20400000, 0x00004000, 0x20404010, + 0x20400000, 0x00000010, 0x20404010, 0x00400000, + 0x20004000, 0x00404010, 0x00400000, 0x20000010, + 0x00400010, 0x20004000, 0x20000000, 0x00004010, + 0x00000000, 0x00400010, 0x20004010, 0x00004000, + 0x00404000, 0x20004010, 0x00000010, 0x20400010, + 0x20400010, 0x00000000, 0x00404010, 0x20404000, + 0x00004010, 0x00404000, 0x20404000, 0x20000000, + 0x20004000, 0x00000010, 0x20400010, 0x00404000, + 0x20404010, 0x00400000, 0x00004010, 0x20000010, + 0x00400000, 0x20004000, 0x20000000, 0x00004010, + 0x20000010, 0x20404010, 0x00404000, 0x20400000, + 0x00404010, 0x20404000, 0x00000000, 0x20400010, + 0x00000010, 0x00004000, 0x20400000, 0x00404010, + 0x00004000, 0x00400010, 0x20004010, 0x00000000, + 0x20404000, 0x20000000, 0x00400010, 0x20004010 +}; + +static uint32_t SB7[64] = { + 0x00200000, 0x04200002, 0x04000802, 0x00000000, + 0x00000800, 0x04000802, 0x00200802, 0x04200800, + 0x04200802, 0x00200000, 0x00000000, 0x04000002, + 0x00000002, 0x04000000, 0x04200002, 0x00000802, + 0x04000800, 0x00200802, 0x00200002, 0x04000800, + 0x04000002, 0x04200000, 0x04200800, 0x00200002, + 0x04200000, 0x00000800, 0x00000802, 0x04200802, + 0x00200800, 0x00000002, 0x04000000, 0x00200800, + 0x04000000, 0x00200800, 0x00200000, 0x04000802, + 0x04000802, 0x04200002, 0x04200002, 0x00000002, + 0x00200002, 0x04000000, 0x04000800, 0x00200000, + 0x04200800, 0x00000802, 0x00200802, 0x04200800, + 0x00000802, 0x04000002, 0x04200802, 0x04200000, + 0x00200800, 0x00000000, 0x00000002, 0x04200802, + 0x00000000, 0x00200802, 0x04200000, 0x00000800, + 0x04000002, 0x04000800, 0x00000800, 0x00200002 +}; + +static uint32_t SB8[64] = { + 0x10001040, 0x00001000, 0x00040000, 0x10041040, + 0x10000000, 0x10001040, 0x00000040, 0x10000000, + 0x00040040, 0x10040000, 0x10041040, 0x00041000, + 0x10041000, 0x00041040, 0x00001000, 0x00000040, + 0x10040000, 0x10000040, 0x10001000, 0x00001040, + 0x00041000, 0x00040040, 0x10040040, 0x10041000, + 0x00001040, 0x00000000, 0x00000000, 0x10040040, + 0x10000040, 0x10001000, 0x00041040, 0x00040000, + 0x00041040, 0x00040000, 0x10041000, 0x00001000, + 0x00000040, 0x10040040, 0x00001000, 0x00041040, + 0x10001000, 0x00000040, 0x10000040, 0x10040000, + 0x10040040, 0x10000000, 0x00040000, 0x10001040, + 0x00000000, 0x10041040, 0x00040040, 0x10000040, + 0x10040000, 0x10001000, 0x10001040, 0x00000000, + 0x10041040, 0x00041000, 0x00041000, 0x00001040, + 0x00001040, 0x00040040, 0x10000000, 0x10041000 +}; + +/* PC1: left and right halves bit-swap */ + +static uint32_t LHs[16] = { + 0x00000000, 0x00000001, 0x00000100, 0x00000101, + 0x00010000, 0x00010001, 0x00010100, 0x00010101, + 0x01000000, 0x01000001, 0x01000100, 0x01000101, + 0x01010000, 0x01010001, 0x01010100, 0x01010101 +}; + +static uint32_t RHs[16] = { + 0x00000000, 0x01000000, 0x00010000, 0x01010000, + 0x00000100, 0x01000100, 0x00010100, 0x01010100, + 0x00000001, 0x01000001, 0x00010001, 0x01010001, + 0x00000101, 0x01000101, 0x00010101, 0x01010101, +}; + +/* platform-independant 32-bit integer manipulation macros */ + +#define GET_UINT32(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ + | ( (uint32_t) (b)[(i) + 1] << 16 ) \ + | ( (uint32_t) (b)[(i) + 2] << 8 ) \ + | ( (uint32_t) (b)[(i) + 3] ); \ +} + +#define PUT_UINT32(n,b,i) \ +{ \ + (b)[(i) ] = (uint8_t) ( (n) >> 24 ); \ + (b)[(i) + 1] = (uint8_t) ( (n) >> 16 ); \ + (b)[(i) + 2] = (uint8_t) ( (n) >> 8 ); \ + (b)[(i) + 3] = (uint8_t) ( (n) ); \ +} + +/* Initial Permutation macro */ + +#define DES_IP(X,Y) \ +{ \ + T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ + T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ + T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ + T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ + Y = ((Y << 1) | (Y >> 31)) & 0xFFFFFFFF; \ + T = (X ^ Y) & 0xAAAAAAAA; Y ^= T; X ^= T; \ + X = ((X << 1) | (X >> 31)) & 0xFFFFFFFF; \ +} + +/* Final Permutation macro */ + +#define DES_FP(X,Y) \ +{ \ + X = ((X << 31) | (X >> 1)) & 0xFFFFFFFF; \ + T = (X ^ Y) & 0xAAAAAAAA; X ^= T; Y ^= T; \ + Y = ((Y << 31) | (Y >> 1)) & 0xFFFFFFFF; \ + T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ + T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ + T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ + T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ +} + +/* DES round macro */ + +#define DES_ROUND(X,Y) \ +{ \ + T = *SK++ ^ X; \ + Y ^= SB8[ (T ) & 0x3F ] ^ \ + SB6[ (T >> 8) & 0x3F ] ^ \ + SB4[ (T >> 16) & 0x3F ] ^ \ + SB2[ (T >> 24) & 0x3F ]; \ + \ + T = *SK++ ^ ((X << 28) | (X >> 4)); \ + Y ^= SB7[ (T ) & 0x3F ] ^ \ + SB5[ (T >> 8) & 0x3F ] ^ \ + SB3[ (T >> 16) & 0x3F ] ^ \ + SB1[ (T >> 24) & 0x3F ]; \ +} + +/* DES key schedule */ + +static inline void des_main_ks( uint32_t SK[32], const uint8_t key[8] ) { + int i; + uint32_t X, Y, T; + + GET_UINT32( X, key, 0 ); + GET_UINT32( Y, key, 4 ); + + /* Permuted Choice 1 */ + + T = ((Y >> 4) ^ X) & 0x0F0F0F0F; X ^= T; Y ^= (T << 4); + T = ((Y ) ^ X) & 0x10101010; X ^= T; Y ^= (T ); + + X = (LHs[ (X ) & 0xF] << 3) | (LHs[ (X >> 8) & 0xF ] << 2) + | (LHs[ (X >> 16) & 0xF] << 1) | (LHs[ (X >> 24) & 0xF ] ) + | (LHs[ (X >> 5) & 0xF] << 7) | (LHs[ (X >> 13) & 0xF ] << 6) + | (LHs[ (X >> 21) & 0xF] << 5) | (LHs[ (X >> 29) & 0xF ] << 4); + + Y = (RHs[ (Y >> 1) & 0xF] << 3) | (RHs[ (Y >> 9) & 0xF ] << 2) + | (RHs[ (Y >> 17) & 0xF] << 1) | (RHs[ (Y >> 25) & 0xF ] ) + | (RHs[ (Y >> 4) & 0xF] << 7) | (RHs[ (Y >> 12) & 0xF ] << 6) + | (RHs[ (Y >> 20) & 0xF] << 5) | (RHs[ (Y >> 28) & 0xF ] << 4); + + X &= 0x0FFFFFFF; + Y &= 0x0FFFFFFF; + + /* calculate subkeys */ + + for( i = 0; i < 16; i++ ) + { + if( i < 2 || i == 8 || i == 15 ) + { + X = ((X << 1) | (X >> 27)) & 0x0FFFFFFF; + Y = ((Y << 1) | (Y >> 27)) & 0x0FFFFFFF; + } + else + { + X = ((X << 2) | (X >> 26)) & 0x0FFFFFFF; + Y = ((Y << 2) | (Y >> 26)) & 0x0FFFFFFF; + } + + *SK++ = ((X << 4) & 0x24000000) | ((X << 28) & 0x10000000) + | ((X << 14) & 0x08000000) | ((X << 18) & 0x02080000) + | ((X << 6) & 0x01000000) | ((X << 9) & 0x00200000) + | ((X >> 1) & 0x00100000) | ((X << 10) & 0x00040000) + | ((X << 2) & 0x00020000) | ((X >> 10) & 0x00010000) + | ((Y >> 13) & 0x00002000) | ((Y >> 4) & 0x00001000) + | ((Y << 6) & 0x00000800) | ((Y >> 1) & 0x00000400) + | ((Y >> 14) & 0x00000200) | ((Y ) & 0x00000100) + | ((Y >> 5) & 0x00000020) | ((Y >> 10) & 0x00000010) + | ((Y >> 3) & 0x00000008) | ((Y >> 18) & 0x00000004) + | ((Y >> 26) & 0x00000002) | ((Y >> 24) & 0x00000001); + + *SK++ = ((X << 15) & 0x20000000) | ((X << 17) & 0x10000000) + | ((X << 10) & 0x08000000) | ((X << 22) & 0x04000000) + | ((X >> 2) & 0x02000000) | ((X << 1) & 0x01000000) + | ((X << 16) & 0x00200000) | ((X << 11) & 0x00100000) + | ((X << 3) & 0x00080000) | ((X >> 6) & 0x00040000) + | ((X << 15) & 0x00020000) | ((X >> 4) & 0x00010000) + | ((Y >> 2) & 0x00002000) | ((Y << 8) & 0x00001000) + | ((Y >> 14) & 0x00000808) | ((Y >> 9) & 0x00000400) + | ((Y ) & 0x00000200) | ((Y << 7) & 0x00000100) + | ((Y >> 7) & 0x00000020) | ((Y >> 3) & 0x00000011) + | ((Y << 2) & 0x00000004) | ((Y >> 21) & 0x00000002); + } +} + +/* DES 64-bit block encryption/decryption */ + +static inline void des_crypt( const uint32_t SK[32], const uint8_t input[8], uint8_t output[8] ) { + uint32_t X, Y, T; + + GET_UINT32( X, input, 0 ); + GET_UINT32( Y, input, 4 ); + + DES_IP( X, Y ); + + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + + DES_FP( Y, X ); + + PUT_UINT32( Y, output, 0 ); + PUT_UINT32( X, output, 4 ); +} + +static inline void des_key(lua_State *L, uint32_t SK[32]) { + size_t keysz = 0; + const void * key = luaL_checklstring(L, 1, &keysz); + if (keysz != 8) { + luaL_error(L, "Invalid key size %d, need 8 bytes", (int)keysz); + } + des_main_ks(SK, key); +} + +int ldesencode(lua_State *L) { + uint32_t SK[32]; + des_key(L, SK); + + size_t textsz = 0; + const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 2, &textsz); + size_t chunksz = (textsz + 8) & ~7; + uint8_t tmp[SMALL_CHUNK]; + uint8_t *buffer = tmp; + if (chunksz > SMALL_CHUNK) { + buffer = lua_newuserdata(L, chunksz); + } + int i; + for (i=0;i<(int)textsz-7;i+=8) { + des_crypt(SK, text+i, buffer+i); + } + int bytes = textsz - i; + uint8_t tail[8]; + int j; + for (j=0;j<8;j++) { + if (j < bytes) { + tail[j] = text[i+j]; + } else if (j==bytes) { + tail[j] = 0x80; + } else { + tail[j] = 0; + } + } + des_crypt(SK, tail, buffer+i); + lua_pushlstring(L, (const char *)buffer, chunksz); + + return 1; +} + +int ldesdecode(lua_State *L) { + uint32_t ESK[32]; + des_key(L, ESK); + uint32_t SK[32]; + int i; + for( i = 0; i < 32; i += 2 ) { + SK[i] = ESK[30 - i]; + SK[i + 1] = ESK[31 - i]; + } + size_t textsz = 0; + const uint8_t *text = (const uint8_t *)luaL_checklstring(L, 2, &textsz); + if ((textsz & 7) || textsz == 0) { + return luaL_error(L, "Invalid des crypt text length %d", (int)textsz); + } + uint8_t tmp[SMALL_CHUNK]; + uint8_t *buffer = tmp; + if (textsz > SMALL_CHUNK) { + buffer = lua_newuserdata(L, textsz); + } + for (i=0;i=textsz-8;i--) { + if (buffer[i] == 0) { + padding++; + } else if (buffer[i] == 0x80) { + break; + } else { + return luaL_error(L, "Invalid des crypt text"); + } + } + if (padding > 8) { + return luaL_error(L, "Invalid des crypt text"); + } + lua_pushlstring(L, (const char *)buffer, textsz - padding); + return 1; +} diff --git a/luaclib/src/lcrypt/dh.c b/luaclib/src/lcrypt/dh.c new file mode 100644 index 00000000..e950afa4 --- /dev/null +++ b/luaclib/src/lcrypt/dh.c @@ -0,0 +1,111 @@ +#include "lcrypt.h" + +// powmodp64 for DH-key exchange + +// The biggest 64bit prime +#define P 0xffffffffffffffc5ull + +static inline void read64(lua_State *L, uint32_t xx[2], uint32_t yy[2]) { + size_t sz = 0; + const uint8_t *x = (const uint8_t *)luaL_checklstring(L, 1, &sz); + if (sz != 8) { + luaL_error(L, "Invalid uint64 x"); + } + const uint8_t *y = (const uint8_t *)luaL_checklstring(L, 2, &sz); + if (sz != 8) { + luaL_error(L, "Invalid uint64 y"); + } + xx[0] = x[0] | x[1]<<8 | x[2]<<16 | x[3]<<24; + xx[1] = x[4] | x[5]<<8 | x[6]<<16 | x[7]<<24; + yy[0] = y[0] | y[1]<<8 | y[2]<<16 | y[3]<<24; + yy[1] = y[4] | y[5]<<8 | y[6]<<16 | y[7]<<24; +} + +static inline uint64_t mul_mod_p(uint64_t a, uint64_t b) { + uint64_t m = 0; + while(b) { + if(b&1) { + uint64_t t = P-a; + if ( m >= t) { + m -= t; + } else { + m += a; + } + } + if (a >= P - a) { + a = a * 2 - P; + } else { + a = a * 2; + } + b>>=1; + } + return m; +} + +static inline uint64_t pow_mod_p(uint64_t a, uint64_t b) { + if (b==1) { + return a; + } + uint64_t t = pow_mod_p(a, b>>1); + t = mul_mod_p(t,t); + if (b % 2) { + t = mul_mod_p(t, a); + } + return t; +} + +// calc a^b % p +static inline uint64_t powmodp(uint64_t a, uint64_t b) { + if (a > P) + a%=P; + return pow_mod_p(a,b); +} + +static inline void push64(lua_State *L, uint64_t r) { + uint8_t tmp[8]; + tmp[0] = r & 0xff; + tmp[1] = (r >> 8 )& 0xff; + tmp[2] = (r >> 16 )& 0xff; + tmp[3] = (r >> 24 )& 0xff; + tmp[4] = (r >> 32 )& 0xff; + tmp[5] = (r >> 40 )& 0xff; + tmp[6] = (r >> 48 )& 0xff; + tmp[7] = (r >> 56 )& 0xff; + + lua_pushlstring(L, (const char *)tmp, 8); +} + +int ldhsecret(lua_State *L) { + uint32_t x[2], y[2]; + read64(L, x, y); + uint64_t xx = (uint64_t)x[0] | (uint64_t)x[1]<<32; + uint64_t yy = (uint64_t)y[0] | (uint64_t)y[1]<<32; + if (xx == 0 || yy == 0) + return luaL_error(L, "Can't be 0"); + uint64_t r = powmodp(xx, yy); + + push64(L, r); + + return 1; +} + +#define G 5 + +int ldhexchange(lua_State *L) { + size_t sz = 0; + const uint8_t *x = (const uint8_t *)luaL_checklstring(L, 1, &sz); + if (sz != 8) { + luaL_error(L, "Invalid dh uint64 key"); + } + uint32_t xx[2]; + xx[0] = x[0] | x[1]<<8 | x[2]<<16 | x[3]<<24; + xx[1] = x[4] | x[5]<<8 | x[6]<<16 | x[7]<<24; + + uint64_t x64 = (uint64_t)xx[0] | (uint64_t)xx[1]<<32; + if (x64 == 0) + return luaL_error(L, "Can't be 0"); + + uint64_t r = powmodp(G, x64); + push64(L, r); + return 1; +} \ No newline at end of file diff --git a/luaclib/src/lcrypt/hex.c b/luaclib/src/lcrypt/hex.c new file mode 100644 index 00000000..0cdbe5f9 --- /dev/null +++ b/luaclib/src/lcrypt/hex.c @@ -0,0 +1,46 @@ +#include "lcrypt.h" + +#define HEX(v, c) { char tmp = (char) c; if (tmp >= '0' && tmp <= '9') { v = tmp-'0'; } else { v = tmp - 'a' + 10; } } + +int ltohex(lua_State *L) { + static char hex[] = "0123456789abcdef"; + size_t sz = 0; + const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 1, &sz); + char tmp[SMALL_CHUNK]; + char *buffer = tmp; + if (sz > SMALL_CHUNK/2) { + buffer = lua_newuserdata(L, sz * 2); + } + int i; + for (i=0;i> 4]; + buffer[i*2+1] = hex[text[i] & 0xf]; + } + lua_pushlstring(L, buffer, sz * 2); + return 1; +} + +int lfromhex(lua_State *L) { + size_t sz = 0; + const char * text = luaL_checklstring(L, 1, &sz); + if (sz & 1) { + return luaL_error(L, "Invalid hex text size %d", (int)sz); + } + char tmp[SMALL_CHUNK]; + char *buffer = tmp; + if (sz > SMALL_CHUNK*2) { + buffer = lua_newuserdata(L, sz / 2); + } + int i; + for (i=0;i 16 || low > 16) { + return luaL_error(L, "Invalid hex text", text); + } + buffer[i/2] = hi<<4 | low; + } + lua_pushlstring(L, buffer, i/2); + return 1; +} \ No newline at end of file diff --git a/luaclib/src/lcrypt/hmac.c b/luaclib/src/lcrypt/hmac.c new file mode 100644 index 00000000..1d71f30b --- /dev/null +++ b/luaclib/src/lcrypt/hmac.c @@ -0,0 +1,130 @@ +#include "lcrypt.h" + +/* +# define MD5_DIGEST_LENGTH 16 +# define SHA_DIGEST_LENGTH 20 +# define SHA224_DIGEST_LENGTH 28 +# define SHA256_DIGEST_LENGTH 32 +# define SHA384_DIGEST_LENGTH 48 +# define SHA512_DIGEST_LENGTH 64 +*/ + +int lhmac_md5(lua_State *L) { + size_t key_sz = 0; + size_t text_sz = 0; + + const char * key = luaL_checklstring(L, 1, &key_sz); + if (!key || key_sz <= 0) + return luaL_error(L, "Invalid key value."); + + const char * text = luaL_checklstring(L, 2, &text_sz); + if (!text || text_sz <= 0) + return luaL_error(L, "Invalid text value."); + + uint32_t result_len = MD5_DIGEST_LENGTH; + unsigned char result[result_len]; + + HMAC(EVP_md5(), key, key_sz, (const unsigned char*)text, text_sz, result, &result_len); + + lua_pushlstring(L, (const char *)result, result_len); + return 1; +}; + +int lhmac_sha128(lua_State *L) { + size_t key_sz = 0; + size_t text_sz = 0; + + const char * key = luaL_checklstring(L, 1, &key_sz); + if (!key || key_sz <= 0) + return luaL_error(L, "Invalid key value."); + + const char * text = luaL_checklstring(L, 2, &text_sz); + if (!text || text_sz <= 0) + return luaL_error(L, "Invalid text value."); + + uint32_t result_len = SHA_DIGEST_LENGTH; + unsigned char result[result_len]; + + HMAC(EVP_sha1(), key, key_sz, (const unsigned char*)text, text_sz, result, &result_len); + + lua_pushlstring(L, (const char *)result, result_len); + return 1; +}; + +// int lhmac_sha224(lua_State *L) { +// size_t key_sz = 0; +// size_t text_sz = 0; + +// const char * key = luaL_checklstring(L, 1, &key_sz); +// if (!key || key_sz <= 0) +// return luaL_error(L, "Invalid key value."); + +// const char * text = luaL_checklstring(L, 2, &text_sz); +// if (!text || text_sz <= 0) +// return luaL_error(L, "Invalid text value."); + +// uint32_t result_len = SHA224_DIGEST_LENGTH; +// unsigned char result[result_len]; + +// HMAC(EVP_sha224(), key, key_sz, (const unsigned char*)text, text_sz, result, &result_len); + +// lua_pushlstring(L, (const char *)result, result_len); +// return 1; +// }; + +int lhmac_sha256(lua_State *L) { + size_t key_sz = 0; + size_t text_sz = 0; + + const char * key = luaL_checklstring(L, 1, &key_sz); + if (!key || key_sz <= 0) + return luaL_error(L, "Invalid key value."); + + const char * text = luaL_checklstring(L, 2, &text_sz); + if (!text || text_sz <= 0) + return luaL_error(L, "Invalid text value."); + + uint32_t result_len = SHA256_DIGEST_LENGTH; + unsigned char result[result_len]; + HMAC(EVP_sha256(), key, key_sz, (const unsigned char*)text, text_sz, result, &result_len); + lua_pushlstring(L, (const char *)result, result_len); + return 1; +}; + +// int lhmac_sha384(lua_State *L) { +// size_t key_sz = 0; +// size_t text_sz = 0; + +// const char * key = luaL_checklstring(L, 1, &key_sz); +// if (!key || key_sz <= 0) +// return luaL_error(L, "Invalid key value."); + +// const char * text = luaL_checklstring(L, 2, &text_sz); +// if (!text || text_sz <= 0) +// return luaL_error(L, "Invalid text value."); + +// uint32_t result_len = SHA384_DIGEST_LENGTH; +// unsigned char result[result_len]; +// HMAC(EVP_sha384(), key, key_sz, (const unsigned char*)text, text_sz, result, &result_len); +// lua_pushlstring(L, (const char *)result, result_len); +// return 1; +// }; + +int lhmac_sha512(lua_State *L) { + size_t key_sz = 0; + size_t text_sz = 0; + + const char * key = luaL_checklstring(L, 1, &key_sz); + if (!key || key_sz <= 0) + return luaL_error(L, "Invalid key value."); + + const char * text = luaL_checklstring(L, 2, &text_sz); + if (!text || text_sz <= 0) + return luaL_error(L, "Invalid text value."); + + uint32_t result_len = SHA384_DIGEST_LENGTH; + unsigned char result[result_len]; + HMAC(EVP_sha512(), key, key_sz, (const unsigned char*)text, text_sz, result, &result_len); + lua_pushlstring(L, (const char *)result, result_len); + return 1; +}; \ No newline at end of file diff --git a/luaclib/src/lcrypt/hmac_ex.c b/luaclib/src/lcrypt/hmac_ex.c new file mode 100644 index 00000000..ff163fc8 --- /dev/null +++ b/luaclib/src/lcrypt/hmac_ex.c @@ -0,0 +1,216 @@ +#include "lcrypt.h" + +/* -- Hashkey/Hmac_hash -- */ + +// Constants are the integer part of the sines of integers (in radians) * 2^32. +static const uint32_t k[64] = { +0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee , +0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501 , +0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be , +0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821 , +0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa , +0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8 , +0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed , +0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a , +0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c , +0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70 , +0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05 , +0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665 , +0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039 , +0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1 , +0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1 , +0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 }; + +// r specifies the per-round shift amounts +static const uint32_t r[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21}; + +// leftrotate function definition +#define LEFTROTATE(x, c) (((x) << (c)) | ((x) >> (32 - (c)))) + +static inline void Hash(const char * str, int sz, uint8_t key[8]) { + uint32_t djb_hash = 5381L; + uint32_t js_hash = 1315423911L; + + int i; + for (i=0;i> 2)); + } + + key[0] = djb_hash & 0xff; + key[1] = (djb_hash >> 8) & 0xff; + key[2] = (djb_hash >> 16) & 0xff; + key[3] = (djb_hash >> 24) & 0xff; + + key[4] = js_hash & 0xff; + key[5] = (js_hash >> 8) & 0xff; + key[6] = (js_hash >> 16) & 0xff; + key[7] = (js_hash >> 24) & 0xff; +} + +static inline void digest_md5(uint32_t w[16], uint32_t result[4]) { + uint32_t a, b, c, d, f, g, temp; + int i; + + a = 0x67452301u; + b = 0xefcdab89u; + c = 0x98badcfeu; + d = 0x10325476u; + + for(i = 0; i<64; i++) { + if (i < 16) { + f = (b & c) | ((~b) & d); + g = i; + } else if (i < 32) { + f = (d & b) | ((~d) & c); + g = (5*i + 1) % 16; + } else if (i < 48) { + f = b ^ c ^ d; + g = (3*i + 5) % 16; + } else { + f = c ^ (b | (~d)); + g = (7*i) % 16; + } + + temp = d; + d = c; + c = b; + b = b + LEFTROTATE((a + f + k[i] + w[g]), r[i]); + a = temp; + } + + result[0] = a; + result[1] = b; + result[2] = c; + result[3] = d; +} + +// hmac64 use md5 algorithm without padding, and the result is (c^d .. a^b) +static inline void hmac(uint32_t x[2], uint32_t y[2], uint32_t result[2]) { + uint32_t w[16]; + uint32_t r[4]; + int i; + for (i=0;i<16;i+=4) { + w[i] = x[1]; + w[i+1] = x[0]; + w[i+2] = y[1]; + w[i+3] = y[0]; + } + + digest_md5(w,r); + + result[0] = r[2]^r[3]; + result[1] = r[0]^r[1]; +} + +static inline void hmac_md5(uint32_t x[2], uint32_t y[2], uint32_t result[2]) { + uint32_t w[16]; + uint32_t r[4]; + int i; + for (i=0;i<12;i+=4) { + w[i] = x[0]; + w[i+1] = x[1]; + w[i+2] = y[0]; + w[i+3] = y[1]; + } + + w[12] = 0x80; + w[13] = 0; + w[14] = 384; + w[15] = 0; + + digest_md5(w,r); + + result[0] = (r[0] + 0x67452301u) ^ (r[2] + 0x98badcfeu); + result[1] = (r[1] + 0xefcdab89u) ^ (r[3] + 0x10325476u); +} + +static inline void read64(lua_State *L, uint32_t xx[2], uint32_t yy[2]) { + size_t sz = 0; + const uint8_t *x = (const uint8_t *)luaL_checklstring(L, 1, &sz); + if (sz != 8) { + luaL_error(L, "Invalid uint64 x"); + } + const uint8_t *y = (const uint8_t *)luaL_checklstring(L, 2, &sz); + if (sz != 8) { + luaL_error(L, "Invalid uint64 y"); + } + xx[0] = x[0] | x[1]<<8 | x[2]<<16 | x[3]<<24; + xx[1] = x[4] | x[5]<<8 | x[6]<<16 | x[7]<<24; + yy[0] = y[0] | y[1]<<8 | y[2]<<16 | y[3]<<24; + yy[1] = y[4] | y[5]<<8 | y[6]<<16 | y[7]<<24; +} + +static inline int pushqword(lua_State *L, uint32_t result[2]) { + uint8_t tmp[8]; + tmp[0] = result[0] & 0xff; + tmp[1] = (result[0] >> 8 )& 0xff; + tmp[2] = (result[0] >> 16 )& 0xff; + tmp[3] = (result[0] >> 24 )& 0xff; + tmp[4] = result[1] & 0xff; + tmp[5] = (result[1] >> 8 )& 0xff; + tmp[6] = (result[1] >> 16 )& 0xff; + tmp[7] = (result[1] >> 24 )& 0xff; + + lua_pushlstring(L, (const char *)tmp, 8); + return 1; +} + +int lhmac64(lua_State *L) { + uint32_t x[2], y[2]; + read64(L, x, y); + uint32_t result[2]; + hmac(x,y,result); + return pushqword(L, result); +} + +/* + h1 = crypt.hmac64_md5(a,b) + m = md5.sum((a..b):rep(3)) + h2 = crypt.xor_str(m:sub(1,8), m:sub(9,16)) + assert(h1 == h2) + */ +int lhmac64_md5(lua_State *L) { + uint32_t x[2], y[2]; + read64(L, x, y); + uint32_t result[2]; + hmac_md5(x,y,result); + return pushqword(L, result); +} + +/* + 8bytes key + string text + */ +int lhmac_hash(lua_State *L) { + uint32_t key[2]; + size_t sz = 0; + const uint8_t *x = (const uint8_t *)luaL_checklstring(L, 1, &sz); + if (sz != 8) { + luaL_error(L, "Invalid uint64 key"); + } + key[0] = x[0] | x[1]<<8 | x[2]<<16 | x[3]<<24; + key[1] = x[4] | x[5]<<8 | x[6]<<16 | x[7]<<24; + const char * text = luaL_checklstring(L, 2, &sz); + uint8_t h[8]; + Hash(text,(int)sz,h); + uint32_t htext[2]; + htext[0] = h[0] | h[1]<<8 | h[2]<<16 | h[3]<<24; + htext[1] = h[4] | h[5]<<8 | h[6]<<16 | h[7]<<24; + uint32_t result[2]; + hmac(htext,key,result); + return pushqword(L, result); +} + +int lhashkey(lua_State *L) { + size_t sz = 0; + const char * key = luaL_checklstring(L, 1, &sz); + uint8_t realkey[8]; + Hash(key,(int)sz,realkey); + lua_pushlstring(L, (const char *)realkey, 8); + return 1; +} \ No newline at end of file diff --git a/luaclib/src/lcrypt/lcrypt.c b/luaclib/src/lcrypt/lcrypt.c index 3271f22e..74b8bae8 100644 --- a/luaclib/src/lcrypt/lcrypt.c +++ b/luaclib/src/lcrypt/lcrypt.c @@ -1,1003 +1,99 @@ -#define LUA_LIB +#include "lcrypt.h" + +/* -- xor_str -- */ +static int lxor_str(lua_State *L) { + size_t len1,len2; + const char *s1 = luaL_checklstring(L, 1, &len1); + const char *s2 = luaL_checklstring(L, 2, &len2); + if (len2 == 0) { + return luaL_error(L, "Can't xor empty string"); + } + luaL_Buffer b; + char * buffer = luaL_buffinitsize(L, &b, len1); + int i; + for (i=0;i> 24 ); \ - (b)[(i) + 1] = (uint8_t) ( (n) >> 16 ); \ - (b)[(i) + 2] = (uint8_t) ( (n) >> 8 ); \ - (b)[(i) + 3] = (uint8_t) ( (n) ); \ -} - -/* Initial Permutation macro */ - -#define DES_IP(X,Y) \ -{ \ - T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ - T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ - T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ - T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ - Y = ((Y << 1) | (Y >> 31)) & 0xFFFFFFFF; \ - T = (X ^ Y) & 0xAAAAAAAA; Y ^= T; X ^= T; \ - X = ((X << 1) | (X >> 31)) & 0xFFFFFFFF; \ -} - -/* Final Permutation macro */ - -#define DES_FP(X,Y) \ -{ \ - X = ((X << 31) | (X >> 1)) & 0xFFFFFFFF; \ - T = (X ^ Y) & 0xAAAAAAAA; X ^= T; Y ^= T; \ - Y = ((Y << 31) | (Y >> 1)) & 0xFFFFFFFF; \ - T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ - T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ - T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ - T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ -} - -/* DES round macro */ - -#define DES_ROUND(X,Y) \ -{ \ - T = *SK++ ^ X; \ - Y ^= SB8[ (T ) & 0x3F ] ^ \ - SB6[ (T >> 8) & 0x3F ] ^ \ - SB4[ (T >> 16) & 0x3F ] ^ \ - SB2[ (T >> 24) & 0x3F ]; \ - \ - T = *SK++ ^ ((X << 28) | (X >> 4)); \ - Y ^= SB7[ (T ) & 0x3F ] ^ \ - SB5[ (T >> 8) & 0x3F ] ^ \ - SB3[ (T >> 16) & 0x3F ] ^ \ - SB1[ (T >> 24) & 0x3F ]; \ -} - -/* DES key schedule */ - -static void -des_main_ks( uint32_t SK[32], const uint8_t key[8] ) { - int i; - uint32_t X, Y, T; - - GET_UINT32( X, key, 0 ); - GET_UINT32( Y, key, 4 ); - - /* Permuted Choice 1 */ - - T = ((Y >> 4) ^ X) & 0x0F0F0F0F; X ^= T; Y ^= (T << 4); - T = ((Y ) ^ X) & 0x10101010; X ^= T; Y ^= (T ); - - X = (LHs[ (X ) & 0xF] << 3) | (LHs[ (X >> 8) & 0xF ] << 2) - | (LHs[ (X >> 16) & 0xF] << 1) | (LHs[ (X >> 24) & 0xF ] ) - | (LHs[ (X >> 5) & 0xF] << 7) | (LHs[ (X >> 13) & 0xF ] << 6) - | (LHs[ (X >> 21) & 0xF] << 5) | (LHs[ (X >> 29) & 0xF ] << 4); - - Y = (RHs[ (Y >> 1) & 0xF] << 3) | (RHs[ (Y >> 9) & 0xF ] << 2) - | (RHs[ (Y >> 17) & 0xF] << 1) | (RHs[ (Y >> 25) & 0xF ] ) - | (RHs[ (Y >> 4) & 0xF] << 7) | (RHs[ (Y >> 12) & 0xF ] << 6) - | (RHs[ (Y >> 20) & 0xF] << 5) | (RHs[ (Y >> 28) & 0xF ] << 4); - - X &= 0x0FFFFFFF; - Y &= 0x0FFFFFFF; - - /* calculate subkeys */ - - for( i = 0; i < 16; i++ ) - { - if( i < 2 || i == 8 || i == 15 ) - { - X = ((X << 1) | (X >> 27)) & 0x0FFFFFFF; - Y = ((Y << 1) | (Y >> 27)) & 0x0FFFFFFF; - } - else - { - X = ((X << 2) | (X >> 26)) & 0x0FFFFFFF; - Y = ((Y << 2) | (Y >> 26)) & 0x0FFFFFFF; - } - - *SK++ = ((X << 4) & 0x24000000) | ((X << 28) & 0x10000000) - | ((X << 14) & 0x08000000) | ((X << 18) & 0x02080000) - | ((X << 6) & 0x01000000) | ((X << 9) & 0x00200000) - | ((X >> 1) & 0x00100000) | ((X << 10) & 0x00040000) - | ((X << 2) & 0x00020000) | ((X >> 10) & 0x00010000) - | ((Y >> 13) & 0x00002000) | ((Y >> 4) & 0x00001000) - | ((Y << 6) & 0x00000800) | ((Y >> 1) & 0x00000400) - | ((Y >> 14) & 0x00000200) | ((Y ) & 0x00000100) - | ((Y >> 5) & 0x00000020) | ((Y >> 10) & 0x00000010) - | ((Y >> 3) & 0x00000008) | ((Y >> 18) & 0x00000004) - | ((Y >> 26) & 0x00000002) | ((Y >> 24) & 0x00000001); - - *SK++ = ((X << 15) & 0x20000000) | ((X << 17) & 0x10000000) - | ((X << 10) & 0x08000000) | ((X << 22) & 0x04000000) - | ((X >> 2) & 0x02000000) | ((X << 1) & 0x01000000) - | ((X << 16) & 0x00200000) | ((X << 11) & 0x00100000) - | ((X << 3) & 0x00080000) | ((X >> 6) & 0x00040000) - | ((X << 15) & 0x00020000) | ((X >> 4) & 0x00010000) - | ((Y >> 2) & 0x00002000) | ((Y << 8) & 0x00001000) - | ((Y >> 14) & 0x00000808) | ((Y >> 9) & 0x00000400) - | ((Y ) & 0x00000200) | ((Y << 7) & 0x00000100) - | ((Y >> 7) & 0x00000020) | ((Y >> 3) & 0x00000011) - | ((Y << 2) & 0x00000004) | ((Y >> 21) & 0x00000002); - } -} - -/* DES 64-bit block encryption/decryption */ - -static void -des_crypt( const uint32_t SK[32], const uint8_t input[8], uint8_t output[8] ) { - uint32_t X, Y, T; - - GET_UINT32( X, input, 0 ); - GET_UINT32( Y, input, 4 ); - - DES_IP( X, Y ); - - DES_ROUND( Y, X ); DES_ROUND( X, Y ); - DES_ROUND( Y, X ); DES_ROUND( X, Y ); - DES_ROUND( Y, X ); DES_ROUND( X, Y ); - DES_ROUND( Y, X ); DES_ROUND( X, Y ); - DES_ROUND( Y, X ); DES_ROUND( X, Y ); - DES_ROUND( Y, X ); DES_ROUND( X, Y ); - DES_ROUND( Y, X ); DES_ROUND( X, Y ); - DES_ROUND( Y, X ); DES_ROUND( X, Y ); - - DES_FP( Y, X ); - - PUT_UINT32( Y, output, 0 ); - PUT_UINT32( X, output, 4 ); -} - -static int -lrandomkey(lua_State *L) { - char tmp[8]; - int i; - char x = 0; - for (i=0;i<8;i++) { - tmp[i] = random() & 0xff; - x ^= tmp[i]; - } - if (x==0) { - tmp[0] |= 1; // avoid 0 - } - lua_pushlstring(L, tmp, 8); - return 1; -} - -static void -des_key(lua_State *L, uint32_t SK[32]) { - size_t keysz = 0; - const void * key = luaL_checklstring(L, 1, &keysz); - if (keysz != 8) { - luaL_error(L, "Invalid key size %d, need 8 bytes", (int)keysz); - } - des_main_ks(SK, key); -} - -static int -ldesencode(lua_State *L) { - uint32_t SK[32]; - des_key(L, SK); - - size_t textsz = 0; - const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 2, &textsz); - size_t chunksz = (textsz + 8) & ~7; - uint8_t tmp[SMALL_CHUNK]; - uint8_t *buffer = tmp; - if (chunksz > SMALL_CHUNK) { - buffer = lua_newuserdata(L, chunksz); - } - int i; - for (i=0;i<(int)textsz-7;i+=8) { - des_crypt(SK, text+i, buffer+i); - } - int bytes = textsz - i; - uint8_t tail[8]; - int j; - for (j=0;j<8;j++) { - if (j < bytes) { - tail[j] = text[i+j]; - } else if (j==bytes) { - tail[j] = 0x80; - } else { - tail[j] = 0; - } - } - des_crypt(SK, tail, buffer+i); - lua_pushlstring(L, (const char *)buffer, chunksz); - - return 1; -} - -static int -ldesdecode(lua_State *L) { - uint32_t ESK[32]; - des_key(L, ESK); - uint32_t SK[32]; - int i; - for( i = 0; i < 32; i += 2 ) { - SK[i] = ESK[30 - i]; - SK[i + 1] = ESK[31 - i]; - } - size_t textsz = 0; - const uint8_t *text = (const uint8_t *)luaL_checklstring(L, 2, &textsz); - if ((textsz & 7) || textsz == 0) { - return luaL_error(L, "Invalid des crypt text length %d", (int)textsz); - } - uint8_t tmp[SMALL_CHUNK]; - uint8_t *buffer = tmp; - if (textsz > SMALL_CHUNK) { - buffer = lua_newuserdata(L, textsz); - } - for (i=0;i=textsz-8;i--) { - if (buffer[i] == 0) { - padding++; - } else if (buffer[i] == 0x80) { - break; - } else { - return luaL_error(L, "Invalid des crypt text"); - } - } - if (padding > 8) { - return luaL_error(L, "Invalid des crypt text"); - } - lua_pushlstring(L, (const char *)buffer, textsz - padding); - return 1; -} - - -static void -Hash(const char * str, int sz, uint8_t key[8]) { - uint32_t djb_hash = 5381L; - uint32_t js_hash = 1315423911L; - - int i; - for (i=0;i> 2)); - } - - key[0] = djb_hash & 0xff; - key[1] = (djb_hash >> 8) & 0xff; - key[2] = (djb_hash >> 16) & 0xff; - key[3] = (djb_hash >> 24) & 0xff; - - key[4] = js_hash & 0xff; - key[5] = (js_hash >> 8) & 0xff; - key[6] = (js_hash >> 16) & 0xff; - key[7] = (js_hash >> 24) & 0xff; -} - -static int -lhashkey(lua_State *L) { - size_t sz = 0; - const char * key = luaL_checklstring(L, 1, &sz); - uint8_t realkey[8]; - Hash(key,(int)sz,realkey); - lua_pushlstring(L, (const char *)realkey, 8); - return 1; -} - -static int -ltohex(lua_State *L) { - static char hex[] = "0123456789abcdef"; - size_t sz = 0; - const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 1, &sz); - char tmp[SMALL_CHUNK]; - char *buffer = tmp; - if (sz > SMALL_CHUNK/2) { - buffer = lua_newuserdata(L, sz * 2); - } - int i; - for (i=0;i> 4]; - buffer[i*2+1] = hex[text[i] & 0xf]; - } - lua_pushlstring(L, buffer, sz * 2); - return 1; -} - -#define HEX(v,c) { char tmp = (char) c; if (tmp >= '0' && tmp <= '9') { v = tmp-'0'; } else { v = tmp - 'a' + 10; } } - -static int -lfromhex(lua_State *L) { - size_t sz = 0; - const char * text = luaL_checklstring(L, 1, &sz); - if (sz & 1) { - return luaL_error(L, "Invalid hex text size %d", (int)sz); - } - char tmp[SMALL_CHUNK]; - char *buffer = tmp; - if (sz > SMALL_CHUNK*2) { - buffer = lua_newuserdata(L, sz / 2); - } - int i; - for (i=0;i 16 || low > 16) { - return luaL_error(L, "Invalid hex text", text); - } - buffer[i/2] = hi<<4 | low; - } - lua_pushlstring(L, buffer, i/2); - return 1; -} - -// Constants are the integer part of the sines of integers (in radians) * 2^32. -static const uint32_t k[64] = { -0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee , -0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501 , -0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be , -0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821 , -0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa , -0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8 , -0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed , -0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a , -0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c , -0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70 , -0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05 , -0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665 , -0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039 , -0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1 , -0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1 , -0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 }; - -// r specifies the per-round shift amounts -static const uint32_t r[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, - 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, - 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, - 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21}; - -// leftrotate function definition -#define LEFTROTATE(x, c) (((x) << (c)) | ((x) >> (32 - (c)))) - -static void -digest_md5(uint32_t w[16], uint32_t result[4]) { - uint32_t a, b, c, d, f, g, temp; - int i; - - a = 0x67452301u; - b = 0xefcdab89u; - c = 0x98badcfeu; - d = 0x10325476u; - - for(i = 0; i<64; i++) { - if (i < 16) { - f = (b & c) | ((~b) & d); - g = i; - } else if (i < 32) { - f = (d & b) | ((~d) & c); - g = (5*i + 1) % 16; - } else if (i < 48) { - f = b ^ c ^ d; - g = (3*i + 5) % 16; - } else { - f = c ^ (b | (~d)); - g = (7*i) % 16; - } - - temp = d; - d = c; - c = b; - b = b + LEFTROTATE((a + f + k[i] + w[g]), r[i]); - a = temp; - } - - result[0] = a; - result[1] = b; - result[2] = c; - result[3] = d; -} - -// hmac64 use md5 algorithm without padding, and the result is (c^d .. a^b) -static void -hmac(uint32_t x[2], uint32_t y[2], uint32_t result[2]) { - uint32_t w[16]; - uint32_t r[4]; - int i; - for (i=0;i<16;i+=4) { - w[i] = x[1]; - w[i+1] = x[0]; - w[i+2] = y[1]; - w[i+3] = y[0]; - } - - digest_md5(w,r); - - result[0] = r[2]^r[3]; - result[1] = r[0]^r[1]; -} - -static void -hmac_md5(uint32_t x[2], uint32_t y[2], uint32_t result[2]) { - uint32_t w[16]; - uint32_t r[4]; - int i; - for (i=0;i<12;i+=4) { - w[i] = x[0]; - w[i+1] = x[1]; - w[i+2] = y[0]; - w[i+3] = y[1]; - } - - w[12] = 0x80; - w[13] = 0; - w[14] = 384; - w[15] = 0; - - digest_md5(w,r); - - result[0] = (r[0] + 0x67452301u) ^ (r[2] + 0x98badcfeu); - result[1] = (r[1] + 0xefcdab89u) ^ (r[3] + 0x10325476u); -} - -static void -read64(lua_State *L, uint32_t xx[2], uint32_t yy[2]) { - size_t sz = 0; - const uint8_t *x = (const uint8_t *)luaL_checklstring(L, 1, &sz); - if (sz != 8) { - luaL_error(L, "Invalid uint64 x"); - } - const uint8_t *y = (const uint8_t *)luaL_checklstring(L, 2, &sz); - if (sz != 8) { - luaL_error(L, "Invalid uint64 y"); - } - xx[0] = x[0] | x[1]<<8 | x[2]<<16 | x[3]<<24; - xx[1] = x[4] | x[5]<<8 | x[6]<<16 | x[7]<<24; - yy[0] = y[0] | y[1]<<8 | y[2]<<16 | y[3]<<24; - yy[1] = y[4] | y[5]<<8 | y[6]<<16 | y[7]<<24; -} - -static int -pushqword(lua_State *L, uint32_t result[2]) { - uint8_t tmp[8]; - tmp[0] = result[0] & 0xff; - tmp[1] = (result[0] >> 8 )& 0xff; - tmp[2] = (result[0] >> 16 )& 0xff; - tmp[3] = (result[0] >> 24 )& 0xff; - tmp[4] = result[1] & 0xff; - tmp[5] = (result[1] >> 8 )& 0xff; - tmp[6] = (result[1] >> 16 )& 0xff; - tmp[7] = (result[1] >> 24 )& 0xff; - - lua_pushlstring(L, (const char *)tmp, 8); - return 1; -} - -static int -lhmac64(lua_State *L) { - uint32_t x[2], y[2]; - read64(L, x, y); - uint32_t result[2]; - hmac(x,y,result); - return pushqword(L, result); -} - -/* - h1 = crypt.hmac64_md5(a,b) - m = md5.sum((a..b):rep(3)) - h2 = crypt.xor_str(m:sub(1,8), m:sub(9,16)) - assert(h1 == h2) - */ -static int -lhmac64_md5(lua_State *L) { - uint32_t x[2], y[2]; - read64(L, x, y); - uint32_t result[2]; - hmac_md5(x,y,result); - return pushqword(L, result); -} - -/* - 8bytes key - string text - */ -static int -lhmac_hash(lua_State *L) { - uint32_t key[2]; - size_t sz = 0; - const uint8_t *x = (const uint8_t *)luaL_checklstring(L, 1, &sz); - if (sz != 8) { - luaL_error(L, "Invalid uint64 key"); - } - key[0] = x[0] | x[1]<<8 | x[2]<<16 | x[3]<<24; - key[1] = x[4] | x[5]<<8 | x[6]<<16 | x[7]<<24; - const char * text = luaL_checklstring(L, 2, &sz); - uint8_t h[8]; - Hash(text,(int)sz,h); - uint32_t htext[2]; - htext[0] = h[0] | h[1]<<8 | h[2]<<16 | h[3]<<24; - htext[1] = h[4] | h[5]<<8 | h[6]<<16 | h[7]<<24; - uint32_t result[2]; - hmac(htext,key,result); - return pushqword(L, result); -} - -// powmodp64 for DH-key exchange - -// The biggest 64bit prime -#define P 0xffffffffffffffc5ull - -static inline uint64_t -mul_mod_p(uint64_t a, uint64_t b) { - uint64_t m = 0; - while(b) { - if(b&1) { - uint64_t t = P-a; - if ( m >= t) { - m -= t; - } else { - m += a; - } - } - if (a >= P - a) { - a = a * 2 - P; - } else { - a = a * 2; - } - b>>=1; - } - return m; -} - -static inline uint64_t -pow_mod_p(uint64_t a, uint64_t b) { - if (b==1) { - return a; - } - uint64_t t = pow_mod_p(a, b>>1); - t = mul_mod_p(t,t); - if (b % 2) { - t = mul_mod_p(t, a); - } - return t; -} - -// calc a^b % p -static uint64_t -powmodp(uint64_t a, uint64_t b) { - if (a > P) - a%=P; - return pow_mod_p(a,b); -} - -static void -push64(lua_State *L, uint64_t r) { - uint8_t tmp[8]; - tmp[0] = r & 0xff; - tmp[1] = (r >> 8 )& 0xff; - tmp[2] = (r >> 16 )& 0xff; - tmp[3] = (r >> 24 )& 0xff; - tmp[4] = (r >> 32 )& 0xff; - tmp[5] = (r >> 40 )& 0xff; - tmp[6] = (r >> 48 )& 0xff; - tmp[7] = (r >> 56 )& 0xff; - - lua_pushlstring(L, (const char *)tmp, 8); -} - -static int -ldhsecret(lua_State *L) { - uint32_t x[2], y[2]; - read64(L, x, y); - uint64_t xx = (uint64_t)x[0] | (uint64_t)x[1]<<32; - uint64_t yy = (uint64_t)y[0] | (uint64_t)y[1]<<32; - if (xx == 0 || yy == 0) - return luaL_error(L, "Can't be 0"); - uint64_t r = powmodp(xx, yy); - - push64(L, r); - - return 1; -} - -#define G 5 - -static int -ldhexchange(lua_State *L) { - size_t sz = 0; - const uint8_t *x = (const uint8_t *)luaL_checklstring(L, 1, &sz); - if (sz != 8) { - luaL_error(L, "Invalid dh uint64 key"); - } - uint32_t xx[2]; - xx[0] = x[0] | x[1]<<8 | x[2]<<16 | x[3]<<24; - xx[1] = x[4] | x[5]<<8 | x[6]<<16 | x[7]<<24; - - uint64_t x64 = (uint64_t)xx[0] | (uint64_t)xx[1]<<32; - if (x64 == 0) - return luaL_error(L, "Can't be 0"); - - uint64_t r = powmodp(G, x64); - push64(L, r); - return 1; -} - -// base64 - -static int -lb64encode(lua_State *L) { - static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - size_t sz = 0; - const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 1, &sz); - int encode_sz = (sz + 2)/3*4; - char tmp[SMALL_CHUNK]; - char *buffer = tmp; - if (encode_sz > SMALL_CHUNK) { - buffer = lua_newuserdata(L, encode_sz); - } - int i,j; - j=0; - for (i=0;i<(int)sz-2;i+=3) { - uint32_t v = text[i] << 16 | text[i+1] << 8 | text[i+2]; - buffer[j] = encoding[v >> 18]; - buffer[j+1] = encoding[(v >> 12) & 0x3f]; - buffer[j+2] = encoding[(v >> 6) & 0x3f]; - buffer[j+3] = encoding[(v) & 0x3f]; - j+=4; - } - int padding = sz-i; - uint32_t v; - switch(padding) { - case 1 : - v = text[i]; - buffer[j] = encoding[v >> 2]; - buffer[j+1] = encoding[(v & 3) << 4]; - buffer[j+2] = '='; - buffer[j+3] = '='; - break; - case 2 : - v = text[i] << 8 | text[i+1]; - buffer[j] = encoding[v >> 10]; - buffer[j+1] = encoding[(v >> 4) & 0x3f]; - buffer[j+2] = encoding[(v & 0xf) << 2]; - buffer[j+3] = '='; - break; - } - lua_pushlstring(L, buffer, encode_sz); - return 1; -} - -static inline int -b64index(uint8_t c) { - static const int decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51}; - int decoding_size = sizeof(decoding)/sizeof(decoding[0]); - if (c<43) { - return -1; - } - c -= 43; - if (c>=decoding_size) - return -1; - return decoding[c]; -} - -static int -lb64decode(lua_State *L) { - size_t sz = 0; - const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 1, &sz); - int decode_sz = (sz+3)/4*3; - char tmp[SMALL_CHUNK]; - char *buffer = tmp; - if (decode_sz > SMALL_CHUNK) { - buffer = lua_newuserdata(L, decode_sz); - } - int i,j; - int output = 0; - for (i=0;i=sz) { - return luaL_error(L, "Invalid base64 text"); - } - c[j] = b64index(text[i]); - if (c[j] == -1) { - ++i; - continue; - } - if (c[j] == -2) { - ++padding; - } - ++i; - ++j; - } - uint32_t v; - switch (padding) { - case 0: - v = (unsigned)c[0] << 18 | c[1] << 12 | c[2] << 6 | c[3]; - buffer[output] = v >> 16; - buffer[output+1] = (v >> 8) & 0xff; - buffer[output+2] = v & 0xff; - output += 3; - break; - case 1: - if (c[3] != -2 || (c[2] & 3)!=0) { - return luaL_error(L, "Invalid base64 text"); - } - v = (unsigned)c[0] << 10 | c[1] << 4 | c[2] >> 2 ; - buffer[output] = v >> 8; - buffer[output+1] = v & 0xff; - output += 2; - break; - case 2: - if (c[3] != -2 || c[2] != -2 || (c[1] & 0xf) !=0) { - return luaL_error(L, "Invalid base64 text"); - } - v = (unsigned)c[0] << 2 | c[1] >> 4; - buffer[output] = v; - ++ output; - break; - default: - return luaL_error(L, "Invalid base64 text"); - } - } - lua_pushlstring(L, buffer, output); - return 1; -} - -static int -lxor_str(lua_State *L) { - size_t len1,len2; - const char *s1 = luaL_checklstring(L,1,&len1); - const char *s2 = luaL_checklstring(L,2,&len2); - if (len2 == 0) { - return luaL_error(L, "Can't xor empty string"); - } - luaL_Buffer b; - char * buffer = luaL_buffinitsize(L, &b, len1); - int i; - for (i=0;i 私钥解密 + {"rsa_public_key_encode", lrsa_public_key_encode}, + {"rsa_private_key_decode", lrsa_private_key_decode}, + // 私钥加密 -> 公钥解密 + {"rsa_private_key_encode", lrsa_private_key_encode}, + {"rsa_public_key_decode", lrsa_public_key_decode}, + // aes 加密 + {"aes_ecb_encrypt", laes_ecb_encrypt}, + {"aes_cbc_encrypt", laes_cbc_encrypt}, + // {"aes_cfb_encrypt", laes_cfb_encrypt}, + // {"aes_ofb_encrypt", laes_ofb_encrypt}, + // {"aes_ctr_encrypt", laes_ctr_encrypt}, + // aes 解密 + {"aes_ecb_decrypt", laes_ecb_decrypt}, + {"aes_cbc_decrypt", laes_cbc_decrypt}, + // {"aes_cfb_decrypt", laes_cfb_decrypt}, + // {"aes_ofb_decrypt", laes_ofb_decrypt}, + // {"aes_ctr_decrypt", laes_ctr_decrypt}, + { NULL, NULL }, + }; + luaL_newlib(L, lcrypt); + return 1; } diff --git a/luaclib/src/lcrypt/lcrypt.h b/luaclib/src/lcrypt/lcrypt.h new file mode 100644 index 00000000..285caa5c --- /dev/null +++ b/luaclib/src/lcrypt/lcrypt.h @@ -0,0 +1,66 @@ +#define LUA_LIB + +#include "../../../src/core.h" +#include +#include +#include +#include +#include + +#define SMALL_CHUNK 256 + +int ltohex(lua_State *L); +int lfromhex(lua_State *L); + +int lcrc32(lua_State *L); +int lcrc64(lua_State *L); + +int lb64encode(lua_State *L); +int lb64decode(lua_State *L); + +int lurlencode(lua_State *L); +int lurldecode(lua_State *L); + +int ldesencode(lua_State *L); +int ldesdecode(lua_State *L); + +int ldhsecret(lua_State *L); +int ldhexchange(lua_State *L); + +int lhashkey(lua_State *L); +int lhmac_hash(lua_State *L); + +int lhmac64(lua_State *L); +int lhmac64_md5(lua_State *L); + +int lmd5(lua_State *L); +int lsha128(lua_State *L); +int lsha224(lua_State *L); +int lsha256(lua_State *L); +int lsha384(lua_State *L); +int lsha512(lua_State *L); + +int lhmac_md5(lua_State *L); +int lhmac_sha128(lua_State *L); +// int lhmac_sha224(lua_State *L); +int lhmac_sha256(lua_State *L); +// int lhmac_sha384(lua_State *L); +int lhmac_sha512(lua_State *L); + +int laes_ecb_encrypt(lua_State *L); +int laes_cbc_encrypt(lua_State *L); +// int laes_cfb_encrypt(lua_State *L); +// int laes_ofb_encrypt(lua_State *L); +// int laes_ctr_encrypt(lua_State *L); + +int laes_ecb_decrypt(lua_State *L); +int laes_cbc_decrypt(lua_State *L); +// int laes_cfb_decrypt(lua_State *L); +// int laes_ofb_decrypt(lua_State *L); +// int laes_ctr_decrypt(lua_State *L); + +int lrsa_public_key_encode(lua_State *L); +int lrsa_private_key_decode(lua_State *L); + +int lrsa_private_key_encode(lua_State *L); +int lrsa_public_key_decode(lua_State *L); \ No newline at end of file diff --git a/luaclib/src/lcrypt/md5.c b/luaclib/src/lcrypt/md5.c deleted file mode 100644 index a48fb4f4..00000000 --- a/luaclib/src/lcrypt/md5.c +++ /dev/null @@ -1,308 +0,0 @@ -/* - * This code implements the MD5 message-digest algorithm. - * The algorithm is due to Ron Rivest. This code was - * written by Colin Plumb in 1993, no copyright is claimed. - * This code is in the public domain; do with it what you wish. - * - * Equivalent code is available from RSA Data Security, Inc. - * This code has been tested against that, and is equivalent, - * except that you don't need to include two pages of legalese - * with every copy. - * - * To compute the message digest of a chunk of bytes, declare an - * MD5Context structure, pass it to MD5Init, call MD5Update as - * needed on buffers full of bytes, and then call MD5Final, which - * will fill a supplied 16-byte array with the digest. - */ - -/* Trivial Lua Binding, Copyright 2010 Rob Kendrick */ - -#define LUA_LIB - -#include "md5.h" - -#define PUT_64BIT_LE(cp, value) do { \ - (cp)[7] = (value) >> 56; \ - (cp)[6] = (value) >> 48; \ - (cp)[5] = (value) >> 40; \ - (cp)[4] = (value) >> 32; \ - (cp)[3] = (value) >> 24; \ - (cp)[2] = (value) >> 16; \ - (cp)[1] = (value) >> 8; \ - (cp)[0] = (value); } while (0) - -#define PUT_32BIT_LE(cp, value) do { \ - (cp)[3] = (value) >> 24; \ - (cp)[2] = (value) >> 16; \ - (cp)[1] = (value) >> 8; \ - (cp)[0] = (value); } while (0) - -static u_int8_t PADDING[MD5_BLOCK_LENGTH] = { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -/* - * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious - * initialization constants. - */ -static void -MD5Init(MD5_CTX *ctx) -{ - ctx->count = 0; - ctx->state[0] = 0x67452301; - ctx->state[1] = 0xefcdab89; - ctx->state[2] = 0x98badcfe; - ctx->state[3] = 0x10325476; -} - -/* - * Update context to reflect the concatenation of another buffer full - * of bytes. - */ -static void -MD5Update(MD5_CTX *ctx, const unsigned char *input, size_t len) -{ - size_t have, need; - - /* Check how many bytes we already have and how many more we need. */ - have = (size_t)((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1)); - need = MD5_BLOCK_LENGTH - have; - - /* Update bitcount */ - ctx->count += (u_int64_t)len << 3; - - if (len >= need) { - if (have != 0) { - bcopy(input, ctx->buffer + have, need); - MD5Transform(ctx->state, ctx->buffer); - input += need; - len -= need; - have = 0; - } - - /* Process data in MD5_BLOCK_LENGTH-byte chunks. */ - while (len >= MD5_BLOCK_LENGTH) { - MD5Transform(ctx->state, input); - input += MD5_BLOCK_LENGTH; - len -= MD5_BLOCK_LENGTH; - } - } - - /* Handle any remaining bytes of data. */ - if (len != 0) - bcopy(input, ctx->buffer + have, len); -} - -/* - * Final wrapup - pad to 64-byte boundary with the bit pattern - * 1 0* (64-bit count of bits processed, MSB-first) - */ -static void -MD5Final(unsigned char digest[MD5_DIGEST_LENGTH], MD5_CTX *ctx) -{ - u_int8_t count[8]; - size_t padlen; - int i; - - /* Convert count to 8 bytes in little endian order. */ - PUT_64BIT_LE(count, ctx->count); - - /* Pad out to 56 mod 64. */ - padlen = MD5_BLOCK_LENGTH - - ((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1)); - if (padlen < 1 + 8) - padlen += MD5_BLOCK_LENGTH; - MD5Update(ctx, PADDING, padlen - 8); /* padlen - 8 <= 64 */ - MD5Update(ctx, count, 8); - - if (digest != NULL) { - for (i = 0; i < 4; i++) - PUT_32BIT_LE(digest + i * 4, ctx->state[i]); - } - bzero(ctx, sizeof(*ctx)); /* in case it's sensitive */ -} - - -/* The four core functions - F1 is optimized somewhat */ - -/* #define F1(x, y, z) (x & y | ~x & z) */ -#define F1(x, y, z) (z ^ (x & (y ^ z))) -#define F2(x, y, z) F1(z, x, y) -#define F3(x, y, z) (x ^ y ^ z) -#define F4(x, y, z) (y ^ (x | ~z)) - -/* This is the central step in the MD5 algorithm. */ -#define MD5STEP(f, w, x, y, z, data, s) \ - ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) - -/* - * The core of the MD5 algorithm, this alters an existing MD5 hash to - * reflect the addition of 16 longwords of new data. MD5Update blocks - * the data and converts bytes into longwords for this routine. - */ -static void -MD5Transform(u_int32_t state[4], const u_int8_t block[MD5_BLOCK_LENGTH]) -{ - u_int32_t a, b, c, d, in[MD5_BLOCK_LENGTH / 4]; - -#if BYTE_ORDER == LITTLE_ENDIAN - bcopy(block, in, sizeof(in)); -#else - for (a = 0; a < MD5_BLOCK_LENGTH / 4; a++) { - in[a] = (u_int32_t)( - (u_int32_t)(block[a * 4 + 0]) | - (u_int32_t)(block[a * 4 + 1]) << 8 | - (u_int32_t)(block[a * 4 + 2]) << 16 | - (u_int32_t)(block[a * 4 + 3]) << 24); - } -#endif - - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - - MD5STEP(F1, a, b, c, d, in[ 0] + 0xd76aa478, 7); - MD5STEP(F1, d, a, b, c, in[ 1] + 0xe8c7b756, 12); - MD5STEP(F1, c, d, a, b, in[ 2] + 0x242070db, 17); - MD5STEP(F1, b, c, d, a, in[ 3] + 0xc1bdceee, 22); - MD5STEP(F1, a, b, c, d, in[ 4] + 0xf57c0faf, 7); - MD5STEP(F1, d, a, b, c, in[ 5] + 0x4787c62a, 12); - MD5STEP(F1, c, d, a, b, in[ 6] + 0xa8304613, 17); - MD5STEP(F1, b, c, d, a, in[ 7] + 0xfd469501, 22); - MD5STEP(F1, a, b, c, d, in[ 8] + 0x698098d8, 7); - MD5STEP(F1, d, a, b, c, in[ 9] + 0x8b44f7af, 12); - MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); - MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); - MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); - MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); - MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); - MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); - - MD5STEP(F2, a, b, c, d, in[ 1] + 0xf61e2562, 5); - MD5STEP(F2, d, a, b, c, in[ 6] + 0xc040b340, 9); - MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); - MD5STEP(F2, b, c, d, a, in[ 0] + 0xe9b6c7aa, 20); - MD5STEP(F2, a, b, c, d, in[ 5] + 0xd62f105d, 5); - MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); - MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); - MD5STEP(F2, b, c, d, a, in[ 4] + 0xe7d3fbc8, 20); - MD5STEP(F2, a, b, c, d, in[ 9] + 0x21e1cde6, 5); - MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); - MD5STEP(F2, c, d, a, b, in[ 3] + 0xf4d50d87, 14); - MD5STEP(F2, b, c, d, a, in[ 8] + 0x455a14ed, 20); - MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); - MD5STEP(F2, d, a, b, c, in[ 2] + 0xfcefa3f8, 9); - MD5STEP(F2, c, d, a, b, in[ 7] + 0x676f02d9, 14); - MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); - - MD5STEP(F3, a, b, c, d, in[ 5] + 0xfffa3942, 4); - MD5STEP(F3, d, a, b, c, in[ 8] + 0x8771f681, 11); - MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); - MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); - MD5STEP(F3, a, b, c, d, in[ 1] + 0xa4beea44, 4); - MD5STEP(F3, d, a, b, c, in[ 4] + 0x4bdecfa9, 11); - MD5STEP(F3, c, d, a, b, in[ 7] + 0xf6bb4b60, 16); - MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); - MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); - MD5STEP(F3, d, a, b, c, in[ 0] + 0xeaa127fa, 11); - MD5STEP(F3, c, d, a, b, in[ 3] + 0xd4ef3085, 16); - MD5STEP(F3, b, c, d, a, in[ 6] + 0x04881d05, 23); - MD5STEP(F3, a, b, c, d, in[ 9] + 0xd9d4d039, 4); - MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); - MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); - MD5STEP(F3, b, c, d, a, in[2 ] + 0xc4ac5665, 23); - - MD5STEP(F4, a, b, c, d, in[ 0] + 0xf4292244, 6); - MD5STEP(F4, d, a, b, c, in[7 ] + 0x432aff97, 10); - MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); - MD5STEP(F4, b, c, d, a, in[5 ] + 0xfc93a039, 21); - MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); - MD5STEP(F4, d, a, b, c, in[3 ] + 0x8f0ccc92, 10); - MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); - MD5STEP(F4, b, c, d, a, in[1 ] + 0x85845dd1, 21); - MD5STEP(F4, a, b, c, d, in[8 ] + 0x6fa87e4f, 6); - MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); - MD5STEP(F4, c, d, a, b, in[6 ] + 0xa3014314, 15); - MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); - MD5STEP(F4, a, b, c, d, in[4 ] + 0xf7537e82, 6); - MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); - MD5STEP(F4, c, d, a, b, in[2 ] + 0x2ad7d2bb, 15); - MD5STEP(F4, b, c, d, a, in[9 ] + 0xeb86d391, 21); - - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; -} - -int -lmd5(lua_State *L) -{ - MD5_CTX ctx; - u_int8_t sum[MD5_DIGEST_LENGTH]; - size_t l; - const char *d = luaL_checklstring(L, 1, &l); - - MD5Init(&ctx); - MD5Update(&ctx, (const u_int8_t *)d, l); - MD5Final(sum, &ctx); - - lua_pushlstring(L, (char *)sum, MD5_DIGEST_LENGTH); - - return 1; -} - - -#define BLOCKSIZE 64 - -static inline -void xor_key(uint8_t key[BLOCKSIZE], uint32_t xor) { - int i; - for (i=0;i BLOCKSIZE) { - MD5_CTX ctx; - MD5Init(&ctx); - MD5Update(&ctx, key, key_sz); - MD5Final(rkey, &ctx1); - key_sz = MD5_DIGEST_LENGTH; - } else { - memcpy(rkey, key, key_sz); - } - - xor_key(rkey, 0x5c5c5c5c); - MD5Init(&ctx1); - MD5Update(&ctx1, rkey, BLOCKSIZE); - - xor_key(rkey, 0x5c5c5c5c ^ 0x36363636); - MD5Init(&ctx2); - MD5Update(&ctx2, rkey, BLOCKSIZE); - MD5Update(&ctx2, text, text_sz); - MD5Final(digest2, &ctx2); - - MD5Update(&ctx1, digest2, MD5_DIGEST_LENGTH); - MD5Final(digest1, &ctx1); - - lua_pushlstring(L, (const char *)digest1, MD5_DIGEST_LENGTH); - - return 1; -} diff --git a/luaclib/src/lcrypt/md5.h b/luaclib/src/lcrypt/md5.h deleted file mode 100644 index ede61288..00000000 --- a/luaclib/src/lcrypt/md5.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * This code implements the MD5 message-digest algorithm. - * The algorithm is due to Ron Rivest. This code was - * written by Colin Plumb in 1993, no copyright is claimed. - * This code is in the public domain; do with it what you wish. - * - * Equivalent code is available from RSA Data Security, Inc. - * This code has been tested against that, and is equivalent, - * except that you don't need to include two pages of legalese - * with every copy. - */ - -#ifndef _MD5_H_ -#define _MD5_H_ - -#include "attributes.h" -#include "../../../src/core.h" - -#define MD5_BLOCK_LENGTH 64 -#define MD5_DIGEST_LENGTH 16 - -typedef struct MD5Context { - u_int32_t state[4]; /* state */ - u_int64_t count; /* number of bits, mod 2^64 */ - u_int8_t buffer[MD5_BLOCK_LENGTH]; /* input buffer */ -} MD5_CTX; - -static void MD5Init(MD5_CTX *); -static void MD5Update(MD5_CTX *, const u_int8_t *, size_t); - // _BOUNDED(__string__,2,3); -static void MD5Final(u_int8_t [MD5_DIGEST_LENGTH], MD5_CTX *); - // _BOUNDED(__minbytes__,1,MD5_DIGEST_LENGTH); -static void MD5Transform(u_int32_t [4], const u_int8_t [MD5_BLOCK_LENGTH]); - // _BOUNDED(__minbytes__,1,4) - // _BOUNDED(__minbytes__,2,MD5_BLOCK_LENGTH); - -#endif /* _MD5_H_ */ diff --git a/luaclib/src/lcrypt/rsa.c b/luaclib/src/lcrypt/rsa.c new file mode 100644 index 00000000..e4c1e89b --- /dev/null +++ b/luaclib/src/lcrypt/rsa.c @@ -0,0 +1,178 @@ +#include "lcrypt.h" +#include + +static inline RSA* READ_PEM_PUB_KEY(FILE *f) { + RSA *key = NULL ; + key = PEM_read_RSA_PUBKEY(f, NULL, NULL, NULL); + if (key) + return key; + return PEM_read_RSAPublicKey(f, NULL, NULL, NULL); +} + +static inline RSA* READ_PEM_PRI_KEY(FILE *f) { + return PEM_read_RSAPrivateKey(f, NULL, NULL, NULL); +} + +static inline RSA* new_public_key(lua_State *L) { + size_t p_size = 0; + const uint8_t* p_path = (const uint8_t*)luaL_checklstring(L, 2, &p_size); + if (!p_path || p_size <= 0) + return NULL; + + FILE* f = fopen((const char *)p_path, "rb"); + if (!f) + return NULL; + + RSA* p_key = READ_PEM_PUB_KEY(f); + if (!p_key){ + fclose(f); + return NULL; + } + + // RSA_print_fp(f, p_key, 0); + // fflush(stdout); + fclose(f); + return p_key; +} + +static inline RSA* new_private_key(lua_State *L) { + size_t p_size = 0; + const uint8_t* p_path = (const uint8_t*)luaL_checklstring(L, 2, &p_size); + if (!p_path || p_size <= 0) + return NULL; + + FILE* f = fopen((const char *)p_path, "rb"); + if (!f) + return NULL; + + RSA* p_key = READ_PEM_PRI_KEY(f); + if (!p_key){ + fclose(f); + return NULL; + } + + // RSA_print_fp(f, p_key, 0); + // fflush(stdout); + fclose(f); + return p_key; +} + +static inline const uint8_t* get_text(lua_State *L, size_t *size) { + size_t text_size = -1; + const uint8_t* text = (const uint8_t*)luaL_checklstring(L, 1, &text_size); + if (!text || text_size < 1) + return NULL; + + *size = text_size; + return text; +} + +int lrsa_public_key_encode(lua_State *L){ + + size_t text_size = 0; + const uint8_t* text = get_text(L, &text_size); + if (!text || text_size < 1) + return luaL_error(L, "Invalid text"); + + RSA* key = new_public_key(L); + if (!key) + return luaL_error(L, "Can't find public key or Invalid public key"); + + luaL_Buffer b; + unsigned char* result = (unsigned char*)luaL_buffinitsize(L, &b, RSA_size(key)); + + if (0 > RSA_public_encrypt(text_size, text, result, key, RSA_PKCS1_PADDING)) { + RSA_free(key); + luaL_pushresultsize(&b, 0); + return luaL_error(L, "encrypt text falied."); + } + + luaL_pushresultsize(&b, RSA_size(key)); + + RSA_free(key); + return 1; + +} + +int lrsa_private_key_decode(lua_State *L) { + + size_t text_size = 0; + const uint8_t* text = get_text(L, &text_size); + if (!text || text_size < 1) + return luaL_error(L, "Invalid text"); + + RSA* key = new_private_key(L); + if (!key) + return luaL_error(L, "Can't find public key or Invalid public key"); + + luaL_Buffer b; + unsigned char* result = (unsigned char*)luaL_buffinitsize(L, &b, RSA_size(key)); + + size_t len = RSA_private_decrypt(text_size, text, result, key, RSA_PKCS1_PADDING); + if (0 > len) { + RSA_free(key); + luaL_pushresultsize(&b, 0); + return luaL_error(L, "encrypt text falied."); + } + + luaL_pushresultsize(&b, len); + + RSA_free(key); + + return 1; + +} + +int lrsa_private_key_encode(lua_State *L){ + + size_t text_size = 0; + const uint8_t* text = get_text(L, &text_size); + if (!text || text_size < 1) + return luaL_error(L, "Invalid text"); + + RSA* key = new_private_key(L); + if (!key) + return luaL_error(L, "Can't find public key or Invalid public key"); + + luaL_Buffer b; + unsigned char* result = (unsigned char*)luaL_buffinitsize(L, &b, RSA_size(key)); + + if (0 > RSA_private_encrypt(text_size, text, result, key, RSA_PKCS1_PADDING)) { + RSA_free(key); + luaL_pushresultsize(&b, 0); + return luaL_error(L, "encrypt text falied."); + } + + luaL_pushresultsize(&b, RSA_size(key)); + + RSA_free(key); + + return 1; +} + +int lrsa_public_key_decode(lua_State *L){ + size_t text_size = 0; + const uint8_t* text = get_text(L, &text_size); + if (!text || text_size < 1) + return luaL_error(L, "Invalid text"); + + RSA* key = new_public_key(L); + if (!key) + return luaL_error(L, "Can't find public key or Invalid public key"); + + luaL_Buffer b; + unsigned char* result = (unsigned char*)luaL_buffinitsize(L, &b, RSA_size(key)); + + size_t len = RSA_public_decrypt(text_size, text, result, key, RSA_PKCS1_PADDING); + if (0 > len) { + RSA_free(key); + luaL_pushresultsize(&b, 0); + return luaL_error(L, "encrypt text falied."); + } + + luaL_pushresultsize(&b, len); + + RSA_free(key); + + return 1; +} \ No newline at end of file diff --git a/luaclib/src/lcrypt/sha.c b/luaclib/src/lcrypt/sha.c new file mode 100644 index 00000000..365e9176 --- /dev/null +++ b/luaclib/src/lcrypt/sha.c @@ -0,0 +1,76 @@ +#include "lcrypt.h" + +/* +# define MD5_DIGEST_LENGTH 16 +# define SHA_DIGEST_LENGTH 20 +# define SHA224_DIGEST_LENGTH 28 +# define SHA256_DIGEST_LENGTH 32 +# define SHA384_DIGEST_LENGTH 48 +# define SHA512_DIGEST_LENGTH 64 +*/ + +int lmd5(lua_State *L) { + size_t sz = 0; + const char * text = luaL_checklstring(L, 1, &sz); + if (!text || sz <= 0) + return luaL_error(L, "Invalid text value."); + unsigned char result[MD5_DIGEST_LENGTH]; + MD5((const unsigned char*) text, sz, result); + lua_pushlstring(L, (const char *)result, MD5_DIGEST_LENGTH); + return 1; +}; + +int lsha128(lua_State *L) { + size_t sz = 0; + const char * text = luaL_checklstring(L, 1, &sz); + if (!text || sz <= 0) + return luaL_error(L, "Invalid text value."); + unsigned char result[SHA_DIGEST_LENGTH]; + SHA1((const unsigned char*) text, sz, result); + lua_pushlstring(L, (const char *)result, SHA_DIGEST_LENGTH); + return 1; +}; + +int lsha224(lua_State *L) { + size_t sz = 0; + const char * text = luaL_checklstring(L, 1, &sz); + if (!text || sz <= 0) + return luaL_error(L, "Invalid text value."); + unsigned char result[SHA224_DIGEST_LENGTH]; + SHA224((const unsigned char*) text, sz, result); + lua_pushlstring(L, (const char *)result, SHA224_DIGEST_LENGTH); + return 1; +}; + +int lsha256(lua_State *L) { + size_t sz = 0; + const char * text = luaL_checklstring(L, 1, &sz); + if (!text || sz <= 0) + return luaL_error(L, "Invalid text value."); + unsigned char result[SHA256_DIGEST_LENGTH]; + SHA256((const unsigned char*) text, sz, result); + lua_pushlstring(L, (const char *)result, SHA256_DIGEST_LENGTH); + return 1; +}; + +int lsha384(lua_State *L) { + size_t sz = 0; + const char * text = luaL_checklstring(L, 1, &sz); + if (!text || sz <= 0) + return luaL_error(L, "Invalid text value."); + unsigned char result[SHA384_DIGEST_LENGTH]; + SHA384((const unsigned char*) text, sz, result); + lua_pushlstring(L, (const char *)result, SHA384_DIGEST_LENGTH); + return 1; +}; + +int lsha512(lua_State *L) { + size_t sz = 0; + const char * text = luaL_checklstring(L, 1, &sz); + if (!text || sz <= 0) + return luaL_error(L, "Invalid text value."); + unsigned char result[SHA512_DIGEST_LENGTH]; + SHA512((const unsigned char*) text, sz, result); + lua_pushlstring(L, (const char *)result, SHA512_DIGEST_LENGTH); + return 1; +}; diff --git a/luaclib/src/lcrypt/sha1.c b/luaclib/src/lcrypt/sha1.c deleted file mode 100644 index 099189ea..00000000 --- a/luaclib/src/lcrypt/sha1.c +++ /dev/null @@ -1,223 +0,0 @@ -#define LUA_LIB - -#include "../../../src/core.h" - -typedef struct { - uint32_t state[5]; - uint32_t count[2]; - uint8_t buffer[64]; -} SHA1_CTX; - -#define SHA1_DIGEST_SIZE 20 - -static void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]); - -#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) - -/* blk0() and blk() perform the initial expand. */ -/* I got the idea of expanding during the round function from SSLeay */ -/* FIXME: can we do this in an endian-proof way? */ -#ifdef WORDS_BIGENDIAN -#define blk0(i) block.l[i] -#else -#define blk0(i) (block.l[i] = (rol(block.l[i],24)&0xFF00FF00) \ - |(rol(block.l[i],8)&0x00FF00FF)) -#endif -#define blk(i) (block.l[i&15] = rol(block.l[(i+13)&15]^block.l[(i+8)&15] \ - ^block.l[(i+2)&15]^block.l[i&15],1)) - -/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ -#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); -#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); -#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); -#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); -#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); - - -/* Hash a single 512-bit block. This is the core of the algorithm. */ -static void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]) -{ - uint32_t a, b, c, d, e; - typedef union { - uint8_t c[64]; - uint32_t l[16]; - } CHAR64LONG16; - CHAR64LONG16 block; - - memcpy(&block, buffer, 64); - - /* Copy context->state[] to working vars */ - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - - /* 4 rounds of 20 operations each. Loop unrolled. */ - R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); - R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); - R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); - R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); - R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); - R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); - R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); - R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); - R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); - R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); - R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); - R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); - R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); - R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); - R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); - R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); - R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); - R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); - R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); - R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); - - /* Add the working vars back into context.state[] */ - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - - /* Wipe variables */ - a = b = c = d = e = 0; -} - - -/* SHA1Init - Initialize new context */ -static void sat_SHA1_Init(SHA1_CTX* context) -{ - /* SHA1 initialization constants */ - context->state[0] = 0x67452301; - context->state[1] = 0xEFCDAB89; - context->state[2] = 0x98BADCFE; - context->state[3] = 0x10325476; - context->state[4] = 0xC3D2E1F0; - context->count[0] = context->count[1] = 0; -} - - -/* Run your data through this. */ -static void sat_SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len) -{ - size_t i, j; - -#ifdef VERBOSE - SHAPrintContext(context, "before"); -#endif - - j = (context->count[0] >> 3) & 63; - if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; - context->count[1] += (len >> 29); - if ((j + len) > 63) { - memcpy(&context->buffer[j], data, (i = 64-j)); - SHA1_Transform(context->state, context->buffer); - for ( ; i + 63 < len; i += 64) { - SHA1_Transform(context->state, data + i); - } - j = 0; - } - else i = 0; - memcpy(&context->buffer[j], &data[i], len - i); - -#ifdef VERBOSE - SHAPrintContext(context, "after "); -#endif -} - - -/* Add padding and return the message digest. */ -static void sat_SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE]) -{ - uint32_t i; - uint8_t finalcount[8]; - - for (i = 0; i < 8; i++) { - finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] - >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ - } - sat_SHA1_Update(context, (uint8_t *)"\200", 1); - while ((context->count[0] & 504) != 448) { - sat_SHA1_Update(context, (uint8_t *)"\0", 1); - } - sat_SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */ - for (i = 0; i < SHA1_DIGEST_SIZE; i++) { - digest[i] = (uint8_t) - ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); - } - - /* Wipe variables */ - i = 0; - memset(context->buffer, 0, 64); - memset(context->state, 0, 20); - memset(context->count, 0, 8); - memset(finalcount, 0, 8); /* SWR */ -} - -int -lsha1(lua_State *L) { - size_t sz = 0; - const uint8_t * buffer = (const uint8_t *)luaL_checklstring(L, 1, &sz); - uint8_t digest[SHA1_DIGEST_SIZE]; - SHA1_CTX ctx; - sat_SHA1_Init(&ctx); - sat_SHA1_Update(&ctx, buffer, sz); - sat_SHA1_Final(&ctx, digest); - lua_pushlstring(L, (const char *)digest, SHA1_DIGEST_SIZE); - - return 1; -} - -#define BLOCKSIZE 64 - -static inline void -xor_key(uint8_t key[BLOCKSIZE], uint32_t xor) { - int i; - for (i=0;i BLOCKSIZE) { - SHA1_CTX ctx; - sat_SHA1_Init(&ctx); - sat_SHA1_Update(&ctx, key, key_sz); - sat_SHA1_Final(&ctx, rkey); - key_sz = SHA1_DIGEST_SIZE; - } else { - memcpy(rkey, key, key_sz); - } - - xor_key(rkey, 0x5c5c5c5c); - sat_SHA1_Init(&ctx1); - sat_SHA1_Update(&ctx1, rkey, BLOCKSIZE); - - xor_key(rkey, 0x5c5c5c5c ^ 0x36363636); - sat_SHA1_Init(&ctx2); - sat_SHA1_Update(&ctx2, rkey, BLOCKSIZE); - sat_SHA1_Update(&ctx2, text, text_sz); - sat_SHA1_Final(&ctx2, digest2); - - sat_SHA1_Update(&ctx1, digest2, SHA1_DIGEST_SIZE); - sat_SHA1_Final(&ctx1, digest1); - - lua_pushlstring(L, (const char *)digest1, SHA1_DIGEST_SIZE); - - return 1; -} diff --git a/luaclib/src/lcrypt/sha2.c b/luaclib/src/lcrypt/sha2.c deleted file mode 100644 index 3d3cc382..00000000 --- a/luaclib/src/lcrypt/sha2.c +++ /dev/null @@ -1,1007 +0,0 @@ -/* - * FILE: sha2.c - * AUTHOR: Aaron D. Gifford - * - * Copyright (c) 2000-2001, Aaron D. Gifford - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the names of contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $From: sha2.c,v 1.1 2001/11/08 00:01:51 adg Exp adg $ - */ - -/* Trivial Lua Binding, Copyright 2010 Rob Kendrick */ - -#define LUA_LIB - -#include "sha2.h" - - -/*** SHA-256/384/512 Machine Architecture Definitions *****************/ -/* - * BYTE_ORDER NOTE: - * - * Please make sure that your system defines BYTE_ORDER. If your - * architecture is little-endian, make sure it also defines - * LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are - * equivilent. - * - * If your system does not define the above, then you can do so by - * hand like this: - * - * #define LITTLE_ENDIAN 1234 - * #define BIG_ENDIAN 4321 - * - * And for little-endian machines, add: - * - * #define BYTE_ORDER LITTLE_ENDIAN - * - * Or for big-endian machines: - * - * #define BYTE_ORDER BIG_ENDIAN - * - * The FreeBSD machine this was written on defines BYTE_ORDER - * appropriately by including (which in turn includes - * where the appropriate definitions are actually - * made). - */ -#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN) -#error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN -#endif - - -/*** SHA-256/384/512 Various Length Definitions ***********************/ -/* NOTE: Most of these are in sha2.h */ -#define SHA256_SHORT_BLOCK_LENGTH (SHA256_BLOCK_LENGTH - 8) -#define SHA384_SHORT_BLOCK_LENGTH (SHA384_BLOCK_LENGTH - 16) -#define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16) - - -/*** ENDIAN REVERSAL MACROS *******************************************/ -#if BYTE_ORDER == LITTLE_ENDIAN -#define REVERSE32(w,x) { \ - u_int32_t tmp = (w); \ - tmp = (tmp >> 16) | (tmp << 16); \ - (x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \ -} -#define REVERSE64(w,x) { \ - u_int64_t tmp = (w); \ - tmp = (tmp >> 32) | (tmp << 32); \ - tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \ - ((tmp & 0x00ff00ff00ff00ffULL) << 8); \ - (x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \ - ((tmp & 0x0000ffff0000ffffULL) << 16); \ -} -#endif /* BYTE_ORDER == LITTLE_ENDIAN */ - -/* - * Macro for incrementally adding the unsigned 64-bit integer n to the - * unsigned 128-bit integer (represented using a two-element array of - * 64-bit words): - */ -#define ADDINC128(w,n) { \ - (w)[0] += (u_int64_t)(n); \ - if ((w)[0] < (n)) { \ - (w)[1]++; \ - } \ -} - -/*** THE SIX LOGICAL FUNCTIONS ****************************************/ -/* - * Bit shifting and rotation (used by the six SHA-XYZ logical functions: - * - * NOTE: The naming of R and S appears backwards here (R is a SHIFT and - * S is a ROTATION) because the SHA-256/384/512 description document - * (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this - * same "backwards" definition. - */ -/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */ -#define R(b,x) ((x) >> (b)) -/* 32-bit Rotate-right (used in SHA-256): */ -#define S32(b,x) (((x) >> (b)) | ((x) << (32 - (b)))) -/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */ -#define S64(b,x) (((x) >> (b)) | ((x) << (64 - (b)))) - -/* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */ -#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) -#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) - -/* Four of six logical functions used in SHA-256: */ -#define Sigma0_256(x) (S32(2, (x)) ^ S32(13, (x)) ^ S32(22, (x))) -#define Sigma1_256(x) (S32(6, (x)) ^ S32(11, (x)) ^ S32(25, (x))) -#define sigma0_256(x) (S32(7, (x)) ^ S32(18, (x)) ^ R(3 , (x))) -#define sigma1_256(x) (S32(17, (x)) ^ S32(19, (x)) ^ R(10, (x))) - -/* Four of six logical functions used in SHA-384 and SHA-512: */ -#define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x))) -#define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x))) -#define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7, (x))) -#define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ R( 6, (x))) - -/*** INTERNAL FUNCTION PROTOTYPES *************************************/ -/* NOTE: These should not be accessed directly from outside this - * library -- they are intended for private internal visibility/use - * only. - */ -static void SHA512Last(SHA2_CTX *); -static void SHA256Transform(SHA2_CTX *, const u_int8_t *); -static void SHA512Transform(SHA2_CTX *, const u_int8_t *); - - -/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/ -/* Hash constant words K for SHA-256: */ -const static u_int32_t K256[64] = { - 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, - 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, - 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, - 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, - 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, - 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, - 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, - 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL, - 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, - 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, - 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, - 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, - 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, - 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL, - 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, - 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL -}; - -/* Initial hash value H for SHA-256: */ -const static u_int32_t sha256_initial_hash_value[8] = { - 0x6a09e667UL, - 0xbb67ae85UL, - 0x3c6ef372UL, - 0xa54ff53aUL, - 0x510e527fUL, - 0x9b05688cUL, - 0x1f83d9abUL, - 0x5be0cd19UL -}; - -/* Hash constant words K for SHA-384 and SHA-512: */ -const static u_int64_t K512[80] = { - 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, - 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, - 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, - 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, - 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, - 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, - 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, - 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, - 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, - 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, - 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, - 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, - 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, - 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, - 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, - 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, - 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, - 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, - 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, - 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, - 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, - 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, - 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, - 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, - 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, - 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, - 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, - 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, - 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, - 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, - 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, - 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, - 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, - 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, - 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, - 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, - 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, - 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, - 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, - 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL -}; - -/* Initial hash value H for SHA-384 */ -// const static u_int64_t sha384_initial_hash_value[8] = { -// 0xcbbb9d5dc1059ed8ULL, -// 0x629a292a367cd507ULL, -// 0x9159015a3070dd17ULL, -// 0x152fecd8f70e5939ULL, -// 0x67332667ffc00b31ULL, -// 0x8eb44a8768581511ULL, -// 0xdb0c2e0d64f98fa7ULL, -// 0x47b5481dbefa4fa4ULL -// }; - -/* Initial hash value H for SHA-512 */ -const static u_int64_t sha512_initial_hash_value[8] = { - 0x6a09e667f3bcc908ULL, - 0xbb67ae8584caa73bULL, - 0x3c6ef372fe94f82bULL, - 0xa54ff53a5f1d36f1ULL, - 0x510e527fade682d1ULL, - 0x9b05688c2b3e6c1fULL, - 0x1f83d9abfb41bd6bULL, - 0x5be0cd19137e2179ULL -}; - - -/*** SHA-256: *********************************************************/ -static void -SHA256Init(SHA2_CTX *context) -{ - if (context == NULL) - return; - bcopy(sha256_initial_hash_value, context->state.st32, - SHA256_DIGEST_LENGTH); - bzero(context->buffer, SHA256_BLOCK_LENGTH); - context->bitcount[0] = 0; -} - -#ifdef SHA2_UNROLL_TRANSFORM - -/* Unrolled SHA-256 round macros: */ - -#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) do { \ - W256[j] = (u_int32_t)data[3] | ((u_int32_t)data[2] << 8) | \ - ((u_int32_t)data[1] << 16) | ((u_int32_t)data[0] << 24); \ - data += 4; \ - T1 = (h) + Sigma1_256((e)) + Ch((e), (f), (g)) + K256[j] + W256[j]; \ - (d) += T1; \ - (h) = T1 + Sigma0_256((a)) + Maj((a), (b), (c)); \ - j++; \ -} while(0) - -#define ROUND256(a,b,c,d,e,f,g,h) do { \ - s0 = W256[(j+1)&0x0f]; \ - s0 = sigma0_256(s0); \ - s1 = W256[(j+14)&0x0f]; \ - s1 = sigma1_256(s1); \ - T1 = (h) + Sigma1_256((e)) + Ch((e), (f), (g)) + K256[j] + \ - (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \ - (d) += T1; \ - (h) = T1 + Sigma0_256((a)) + Maj((a), (b), (c)); \ - j++; \ -} while(0) - -static void -SHA256Transform(SHA2_CTX *context, const u_int8_t *data) -{ - u_int32_t a, b, c, d, e, f, g, h, s0, s1; - u_int32_t T1, *W256; - int j; - - W256 = (u_int32_t *)context->buffer; - - /* Initialize registers with the prev. intermediate value */ - a = context->state.st32[0]; - b = context->state.st32[1]; - c = context->state.st32[2]; - d = context->state.st32[3]; - e = context->state.st32[4]; - f = context->state.st32[5]; - g = context->state.st32[6]; - h = context->state.st32[7]; - - j = 0; - do { - /* Rounds 0 to 15 (unrolled): */ - ROUND256_0_TO_15(a,b,c,d,e,f,g,h); - ROUND256_0_TO_15(h,a,b,c,d,e,f,g); - ROUND256_0_TO_15(g,h,a,b,c,d,e,f); - ROUND256_0_TO_15(f,g,h,a,b,c,d,e); - ROUND256_0_TO_15(e,f,g,h,a,b,c,d); - ROUND256_0_TO_15(d,e,f,g,h,a,b,c); - ROUND256_0_TO_15(c,d,e,f,g,h,a,b); - ROUND256_0_TO_15(b,c,d,e,f,g,h,a); - } while (j < 16); - - /* Now for the remaining rounds to 64: */ - do { - ROUND256(a,b,c,d,e,f,g,h); - ROUND256(h,a,b,c,d,e,f,g); - ROUND256(g,h,a,b,c,d,e,f); - ROUND256(f,g,h,a,b,c,d,e); - ROUND256(e,f,g,h,a,b,c,d); - ROUND256(d,e,f,g,h,a,b,c); - ROUND256(c,d,e,f,g,h,a,b); - ROUND256(b,c,d,e,f,g,h,a); - } while (j < 64); - - /* Compute the current intermediate hash value */ - context->state.st32[0] += a; - context->state.st32[1] += b; - context->state.st32[2] += c; - context->state.st32[3] += d; - context->state.st32[4] += e; - context->state.st32[5] += f; - context->state.st32[6] += g; - context->state.st32[7] += h; - - /* Clean up */ - a = b = c = d = e = f = g = h = T1 = 0; -} - -#else /* SHA2_UNROLL_TRANSFORM */ - -static void -SHA256Transform(SHA2_CTX *context, const u_int8_t *data) -{ - u_int32_t a, b, c, d, e, f, g, h, s0, s1; - u_int32_t T1, T2, *W256; - int j; - - W256 = (u_int32_t *)context->buffer; - - /* Initialize registers with the prev. intermediate value */ - a = context->state.st32[0]; - b = context->state.st32[1]; - c = context->state.st32[2]; - d = context->state.st32[3]; - e = context->state.st32[4]; - f = context->state.st32[5]; - g = context->state.st32[6]; - h = context->state.st32[7]; - - j = 0; - do { - W256[j] = (u_int32_t)data[3] | ((u_int32_t)data[2] << 8) | - ((u_int32_t)data[1] << 16) | ((u_int32_t)data[0] << 24); - data += 4; - /* Apply the SHA-256 compression function to update a..h */ - T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j]; - T2 = Sigma0_256(a) + Maj(a, b, c); - h = g; - g = f; - f = e; - e = d + T1; - d = c; - c = b; - b = a; - a = T1 + T2; - - j++; - } while (j < 16); - - do { - /* Part of the message block expansion: */ - s0 = W256[(j+1)&0x0f]; - s0 = sigma0_256(s0); - s1 = W256[(j+14)&0x0f]; - s1 = sigma1_256(s1); - - /* Apply the SHA-256 compression function to update a..h */ - T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + - (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); - T2 = Sigma0_256(a) + Maj(a, b, c); - h = g; - g = f; - f = e; - e = d + T1; - d = c; - c = b; - b = a; - a = T1 + T2; - - j++; - } while (j < 64); - - /* Compute the current intermediate hash value */ - context->state.st32[0] += a; - context->state.st32[1] += b; - context->state.st32[2] += c; - context->state.st32[3] += d; - context->state.st32[4] += e; - context->state.st32[5] += f; - context->state.st32[6] += g; - context->state.st32[7] += h; - - /* Clean up */ - a = b = c = d = e = f = g = h = T1 = T2 = 0; -} - -#endif /* SHA2_UNROLL_TRANSFORM */ - -static void -SHA256Update(SHA2_CTX *context, const u_int8_t *data, size_t len) -{ - size_t freespace, usedspace; - - /* Calling with no data is valid (we do nothing) */ - if (len == 0) - return; - - usedspace = (context->bitcount[0] >> 3) % SHA256_BLOCK_LENGTH; - if (usedspace > 0) { - /* Calculate how much free space is available in the buffer */ - freespace = SHA256_BLOCK_LENGTH - usedspace; - - if (len >= freespace) { - /* Fill the buffer completely and process it */ - bcopy(data, &context->buffer[usedspace], freespace); - context->bitcount[0] += freespace << 3; - len -= freespace; - data += freespace; - SHA256Transform(context, context->buffer); - } else { - /* The buffer is not yet full */ - bcopy(data, &context->buffer[usedspace], len); - context->bitcount[0] += len << 3; - /* Clean up: */ - usedspace = freespace = 0; - return; - } - } - while (len >= SHA256_BLOCK_LENGTH) { - /* Process as many complete blocks as we can */ - SHA256Transform(context, data); - context->bitcount[0] += SHA256_BLOCK_LENGTH << 3; - len -= SHA256_BLOCK_LENGTH; - data += SHA256_BLOCK_LENGTH; - } - if (len > 0) { - /* There's left-overs, so save 'em */ - bcopy(data, context->buffer, len); - context->bitcount[0] += len << 3; - } - /* Clean up: */ - usedspace = freespace = 0; -} - -static void -SHA256Final(u_int8_t digest[], SHA2_CTX *context) -{ - u_int32_t *d = (u_int32_t *)digest; - unsigned int usedspace; - - /* If no digest buffer is passed, we don't bother doing this: */ - if (digest != NULL) { - usedspace = (context->bitcount[0] >> 3) % SHA256_BLOCK_LENGTH; -#if BYTE_ORDER == LITTLE_ENDIAN - /* Convert FROM host byte order */ - REVERSE64(context->bitcount[0], context->bitcount[0]); -#endif - if (usedspace > 0) { - /* Begin padding with a 1 bit: */ - context->buffer[usedspace++] = 0x80; - - if (usedspace <= SHA256_SHORT_BLOCK_LENGTH) { - /* Set-up for the last transform: */ - bzero(&context->buffer[usedspace], SHA256_SHORT_BLOCK_LENGTH - usedspace); - } else { - if (usedspace < SHA256_BLOCK_LENGTH) { - bzero(&context->buffer[usedspace], SHA256_BLOCK_LENGTH - usedspace); - } - /* Do second-to-last transform: */ - SHA256Transform(context, context->buffer); - - /* And set-up for the last transform: */ - bzero(context->buffer, SHA256_SHORT_BLOCK_LENGTH); - } - } else { - /* Set-up for the last transform: */ - bzero(context->buffer, SHA256_SHORT_BLOCK_LENGTH); - - /* Begin padding with a 1 bit: */ - *context->buffer = 0x80; - } - /* Set the bit count: */ - *(u_int64_t *)&context->buffer[SHA256_SHORT_BLOCK_LENGTH] = context->bitcount[0]; - - /* Final transform: */ - SHA256Transform(context, context->buffer); - -#if BYTE_ORDER == LITTLE_ENDIAN - { - /* Convert TO host byte order */ - int j; - for (j = 0; j < 8; j++) { - REVERSE32(context->state.st32[j], - context->state.st32[j]); - *d++ = context->state.st32[j]; - } - } -#else - bcopy(context->state.st32, d, SHA256_DIGEST_LENGTH); -#endif - } - - /* Clean up state data: */ - bzero(context, sizeof(*context)); - usedspace = 0; -} - - -/*** SHA-512: *********************************************************/ -static void -SHA512Init(SHA2_CTX *context) -{ - if (context == NULL) - return; - bcopy(sha512_initial_hash_value, context->state.st64, - SHA512_DIGEST_LENGTH); - bzero(context->buffer, SHA512_BLOCK_LENGTH); - context->bitcount[0] = context->bitcount[1] = 0; -} - -#ifdef SHA2_UNROLL_TRANSFORM - -/* Unrolled SHA-512 round macros: */ - -#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) do { \ - W512[j] = (u_int64_t)data[7] | ((u_int64_t)data[6] << 8) | \ - ((u_int64_t)data[5] << 16) | ((u_int64_t)data[4] << 24) | \ - ((u_int64_t)data[3] << 32) | ((u_int64_t)data[2] << 40) | \ - ((u_int64_t)data[1] << 48) | ((u_int64_t)data[0] << 56); \ - data += 8; \ - T1 = (h) + Sigma1_512((e)) + Ch((e), (f), (g)) + K512[j] + W512[j]; \ - (d) += T1; \ - (h) = T1 + Sigma0_512((a)) + Maj((a), (b), (c)); \ - j++; \ -} while(0) - - -#define ROUND512(a,b,c,d,e,f,g,h) do { \ - s0 = W512[(j+1)&0x0f]; \ - s0 = sigma0_512(s0); \ - s1 = W512[(j+14)&0x0f]; \ - s1 = sigma1_512(s1); \ - T1 = (h) + Sigma1_512((e)) + Ch((e), (f), (g)) + K512[j] + \ - (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); \ - (d) += T1; \ - (h) = T1 + Sigma0_512((a)) + Maj((a), (b), (c)); \ - j++; \ -} while(0) - -static void -SHA512Transform(SHA2_CTX *context, const u_int8_t *data) -{ - u_int64_t a, b, c, d, e, f, g, h, s0, s1; - u_int64_t T1, *W512 = (u_int64_t *)context->buffer; - int j; - - /* Initialize registers with the prev. intermediate value */ - a = context->state.st64[0]; - b = context->state.st64[1]; - c = context->state.st64[2]; - d = context->state.st64[3]; - e = context->state.st64[4]; - f = context->state.st64[5]; - g = context->state.st64[6]; - h = context->state.st64[7]; - - j = 0; - do { - ROUND512_0_TO_15(a,b,c,d,e,f,g,h); - ROUND512_0_TO_15(h,a,b,c,d,e,f,g); - ROUND512_0_TO_15(g,h,a,b,c,d,e,f); - ROUND512_0_TO_15(f,g,h,a,b,c,d,e); - ROUND512_0_TO_15(e,f,g,h,a,b,c,d); - ROUND512_0_TO_15(d,e,f,g,h,a,b,c); - ROUND512_0_TO_15(c,d,e,f,g,h,a,b); - ROUND512_0_TO_15(b,c,d,e,f,g,h,a); - } while (j < 16); - - /* Now for the remaining rounds up to 79: */ - do { - ROUND512(a,b,c,d,e,f,g,h); - ROUND512(h,a,b,c,d,e,f,g); - ROUND512(g,h,a,b,c,d,e,f); - ROUND512(f,g,h,a,b,c,d,e); - ROUND512(e,f,g,h,a,b,c,d); - ROUND512(d,e,f,g,h,a,b,c); - ROUND512(c,d,e,f,g,h,a,b); - ROUND512(b,c,d,e,f,g,h,a); - } while (j < 80); - - /* Compute the current intermediate hash value */ - context->state.st64[0] += a; - context->state.st64[1] += b; - context->state.st64[2] += c; - context->state.st64[3] += d; - context->state.st64[4] += e; - context->state.st64[5] += f; - context->state.st64[6] += g; - context->state.st64[7] += h; - - /* Clean up */ - a = b = c = d = e = f = g = h = T1 = 0; -} - -#else /* SHA2_UNROLL_TRANSFORM */ - -static void -SHA512Transform(SHA2_CTX *context, const u_int8_t *data) -{ - u_int64_t a, b, c, d, e, f, g, h, s0, s1; - u_int64_t T1, T2, *W512 = (u_int64_t *)context->buffer; - int j; - - /* Initialize registers with the prev. intermediate value */ - a = context->state.st64[0]; - b = context->state.st64[1]; - c = context->state.st64[2]; - d = context->state.st64[3]; - e = context->state.st64[4]; - f = context->state.st64[5]; - g = context->state.st64[6]; - h = context->state.st64[7]; - - j = 0; - do { - W512[j] = (u_int64_t)data[7] | ((u_int64_t)data[6] << 8) | - ((u_int64_t)data[5] << 16) | ((u_int64_t)data[4] << 24) | - ((u_int64_t)data[3] << 32) | ((u_int64_t)data[2] << 40) | - ((u_int64_t)data[1] << 48) | ((u_int64_t)data[0] << 56); - data += 8; - /* Apply the SHA-512 compression function to update a..h */ - T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j]; - T2 = Sigma0_512(a) + Maj(a, b, c); - h = g; - g = f; - f = e; - e = d + T1; - d = c; - c = b; - b = a; - a = T1 + T2; - - j++; - } while (j < 16); - - do { - /* Part of the message block expansion: */ - s0 = W512[(j+1)&0x0f]; - s0 = sigma0_512(s0); - s1 = W512[(j+14)&0x0f]; - s1 = sigma1_512(s1); - - /* Apply the SHA-512 compression function to update a..h */ - T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + - (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); - T2 = Sigma0_512(a) + Maj(a, b, c); - h = g; - g = f; - f = e; - e = d + T1; - d = c; - c = b; - b = a; - a = T1 + T2; - - j++; - } while (j < 80); - - /* Compute the current intermediate hash value */ - context->state.st64[0] += a; - context->state.st64[1] += b; - context->state.st64[2] += c; - context->state.st64[3] += d; - context->state.st64[4] += e; - context->state.st64[5] += f; - context->state.st64[6] += g; - context->state.st64[7] += h; - - /* Clean up */ - a = b = c = d = e = f = g = h = T1 = T2 = 0; -} - -#endif /* SHA2_UNROLL_TRANSFORM */ - -static void -SHA512Update(SHA2_CTX *context, const u_int8_t *data, size_t len) -{ - size_t freespace, usedspace; - - /* Calling with no data is valid (we do nothing) */ - if (len == 0) - return; - - usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; - if (usedspace > 0) { - /* Calculate how much free space is available in the buffer */ - freespace = SHA512_BLOCK_LENGTH - usedspace; - - if (len >= freespace) { - /* Fill the buffer completely and process it */ - bcopy(data, &context->buffer[usedspace], freespace); - ADDINC128(context->bitcount, freespace << 3); - len -= freespace; - data += freespace; - SHA512Transform(context, context->buffer); - } else { - /* The buffer is not yet full */ - bcopy(data, &context->buffer[usedspace], len); - ADDINC128(context->bitcount, len << 3); - /* Clean up: */ - usedspace = freespace = 0; - return; - } - } - while (len >= SHA512_BLOCK_LENGTH) { - /* Process as many complete blocks as we can */ - SHA512Transform(context, data); - ADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3); - len -= SHA512_BLOCK_LENGTH; - data += SHA512_BLOCK_LENGTH; - } - if (len > 0) { - /* There's left-overs, so save 'em */ - bcopy(data, context->buffer, len); - ADDINC128(context->bitcount, len << 3); - } - /* Clean up: */ - usedspace = freespace = 0; -} - -static void -SHA512Last(SHA2_CTX *context) -{ - unsigned int usedspace; - - usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; -#if BYTE_ORDER == LITTLE_ENDIAN - /* Convert FROM host byte order */ - REVERSE64(context->bitcount[0],context->bitcount[0]); - REVERSE64(context->bitcount[1],context->bitcount[1]); -#endif - if (usedspace > 0) { - /* Begin padding with a 1 bit: */ - context->buffer[usedspace++] = 0x80; - - if (usedspace <= SHA512_SHORT_BLOCK_LENGTH) { - /* Set-up for the last transform: */ - bzero(&context->buffer[usedspace], SHA512_SHORT_BLOCK_LENGTH - usedspace); - } else { - if (usedspace < SHA512_BLOCK_LENGTH) { - bzero(&context->buffer[usedspace], SHA512_BLOCK_LENGTH - usedspace); - } - /* Do second-to-last transform: */ - SHA512Transform(context, context->buffer); - - /* And set-up for the last transform: */ - bzero(context->buffer, SHA512_BLOCK_LENGTH - 2); - } - } else { - /* Prepare for final transform: */ - bzero(context->buffer, SHA512_SHORT_BLOCK_LENGTH); - - /* Begin padding with a 1 bit: */ - *context->buffer = 0x80; - } - /* Store the length of input data (in bits): */ - *(u_int64_t *)&context->buffer[SHA512_SHORT_BLOCK_LENGTH] = context->bitcount[1]; - *(u_int64_t *)&context->buffer[SHA512_SHORT_BLOCK_LENGTH+8] = context->bitcount[0]; - - /* Final transform: */ - SHA512Transform(context, context->buffer); -} - -static void -SHA512Final(u_int8_t digest[], SHA2_CTX *context) -{ - u_int64_t *d = (u_int64_t *)digest; - - /* If no digest buffer is passed, we don't bother doing this: */ - if (digest != NULL) { - SHA512Last(context); - - /* Save the hash data for output: */ -#if BYTE_ORDER == LITTLE_ENDIAN - { - /* Convert TO host byte order */ - int j; - for (j = 0; j < 8; j++) { - REVERSE64(context->state.st64[j], - context->state.st64[j]); - *d++ = context->state.st64[j]; - } - } -#else - bcopy(context->state.st64, d, SHA512_DIGEST_LENGTH); -#endif - } - - /* Zero out state data */ - bzero(context, sizeof(*context)); -} - - -/*** SHA-384: *********************************************************/ -// static void -// SHA384Init(SHA2_CTX *context) -// { -// if (context == NULL) -// return; -// bcopy(sha384_initial_hash_value, context->state.st64, -// SHA512_DIGEST_LENGTH); -// bzero(context->buffer, SHA384_BLOCK_LENGTH); -// context->bitcount[0] = context->bitcount[1] = 0; -// } -// -// static void -// SHA384Update(SHA2_CTX *context, const u_int8_t *data, size_t len) -// { -// SHA512Update((SHA2_CTX *)context, data, len); -// } -// -// static void -// SHA384Final(u_int8_t digest[], SHA2_CTX *context) -// { -// u_int64_t *d = (u_int64_t *)digest; -// -// /* If no digest buffer is passed, we don't bother doing this: */ -// if (digest != NULL) { -// SHA512Last((SHA2_CTX *)context); -// -// /* Save the hash data for output: */ -// #if BYTE_ORDER == LITTLE_ENDIAN -// { -// /* Convert TO host byte order */ -// int j; -// for (j = 0; j < 6; j++) { -// REVERSE64(context->state.st64[j], -// context->state.st64[j]); -// *d++ = context->state.st64[j]; -// } -// } -// #else -// bcopy(context->state.st64, d, SHA384_DIGEST_LENGTH); -// #endif -// } -// -// /* Zero out state data */ -// bzero(context, sizeof(*context)); -// } - -int // lua-sha256 -lsha256(lua_State *L){ - SHA2_CTX ctx; - size_t l; - const u_int8_t* str = (const u_int8_t*)luaL_checklstring(L, 1, &l); - u_int8_t text[SHA256_DIGEST_LENGTH]; - SHA256Init(&ctx); - SHA256Update(&ctx, str, l); - SHA256Final(text, &ctx); - lua_pushlstring(L, (const char *)text, SHA256_DIGEST_LENGTH); - return 1; -} - -int // lua-sha512 -lsha512(lua_State *L){ - SHA2_CTX ctx; - size_t l; - const u_int8_t* str = (const u_int8_t*)luaL_checklstring(L, 1, &l); - u_int8_t text[SHA512_DIGEST_LENGTH]; - SHA512Init(&ctx); - SHA512Update(&ctx, str, l); - SHA512Final(text, &ctx); - lua_pushlstring(L, (const char *)text, SHA512_DIGEST_LENGTH); - return 1; -} - -#define BLOCKSIZE_128_256 64 - -static inline void -xor_key_128_256(uint8_t key[BLOCKSIZE_128_256], uint32_t xor) { - int i; - for (i=0;i SHA256_BLOCK_LENGTH) { - SHA2_CTX ctx; - SHA256Init(&ctx); - SHA256Update(&ctx, key, key_sz); - SHA256Final(rkey, &ctx); - key_sz = SHA256_DIGEST_LENGTH; - } else { - memcpy(rkey, key, key_sz); - } - - xor_key_128_256(rkey, 0x5c5c5c5c); - SHA256Init(&ctx1); - SHA256Update(&ctx1, rkey, BLOCKSIZE_128_256); - - xor_key_128_256(rkey, 0x5c5c5c5c ^ 0x36363636); - SHA256Init(&ctx2); - SHA256Update(&ctx2, rkey, BLOCKSIZE_128_256); - SHA256Update(&ctx2, text, text_sz); - SHA256Final(digest2, &ctx2); - - SHA256Update(&ctx1, digest2, SHA256_DIGEST_LENGTH); - SHA256Final(digest1, &ctx1); - - lua_pushlstring(L, (const char *)digest1, SHA256_DIGEST_LENGTH); - return 1; -} - -int // lua-hmac_sha512 -lhmac_sha512(lua_State *L){ - size_t key_sz = 0; - const uint8_t * key = (const uint8_t *)luaL_checklstring(L, 1, &key_sz); - size_t text_sz = 0; - const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 2, &text_sz); - SHA2_CTX ctx1, ctx2; - uint8_t digest1[SHA512_DIGEST_LENGTH]; - uint8_t digest2[SHA512_DIGEST_LENGTH]; - uint8_t rkey[BLOCKSIZE_384_512]; - memset(rkey, 0, BLOCKSIZE_384_512); - - if (key_sz > BLOCKSIZE_384_512) { - SHA2_CTX ctx; - SHA512Init(&ctx); - SHA512Update(&ctx, key, key_sz); - SHA512Final(rkey, &ctx); - key_sz = SHA512_DIGEST_LENGTH; - } else { - memcpy(rkey, key, key_sz); - } - - xor_key_384_512(rkey, 0x5c5c5c5c); - SHA512Init(&ctx1); - SHA512Update(&ctx1, rkey, BLOCKSIZE_384_512); - - xor_key_384_512(rkey, 0x5c5c5c5c ^ 0x36363636); - SHA512Init(&ctx2); - SHA512Update(&ctx2, rkey, BLOCKSIZE_384_512); - SHA512Update(&ctx2, text, text_sz); - SHA512Final(digest2, &ctx2); - - SHA512Update(&ctx1, digest2, SHA512_DIGEST_LENGTH); - SHA512Final(digest1, &ctx1); - - lua_pushlstring(L, (const char *)digest1, SHA512_DIGEST_LENGTH); - return 1; -} diff --git a/luaclib/src/lcrypt/sha2.h b/luaclib/src/lcrypt/sha2.h deleted file mode 100644 index 51368d9c..00000000 --- a/luaclib/src/lcrypt/sha2.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * FILE: sha2.h - * AUTHOR: Aaron D. Gifford - * - * Copyright (c) 2000-2001, Aaron D. Gifford - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the names of contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $From: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $ - */ - -#ifndef _SHA2_H -#define _SHA2_H - -#include "attributes.h" -#include "../../../src/core.h" - -/*** SHA-256/384/512 Various Length Definitions ***********************/ -#define SHA256_BLOCK_LENGTH 64 -#define SHA256_DIGEST_LENGTH 32 -#define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2 + 1) -#define SHA384_BLOCK_LENGTH 128 -#define SHA384_DIGEST_LENGTH 48 -#define SHA384_DIGEST_STRING_LENGTH (SHA384_DIGEST_LENGTH * 2 + 1) -#define SHA512_BLOCK_LENGTH 128 -#define SHA512_DIGEST_LENGTH 64 -#define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1) - - -/*** SHA-256/384/512 Context Structure *******************************/ -typedef struct _SHA2_CTX { - union { - u_int32_t st32[8]; - u_int64_t st64[8]; - } state; - u_int64_t bitcount[2]; - u_int8_t buffer[SHA512_BLOCK_LENGTH]; -} SHA2_CTX; - -static void SHA256Init(SHA2_CTX *); -static void SHA256Update(SHA2_CTX *, const u_int8_t *, size_t); - // _BOUNDED(__string__,2,3); -static void SHA256Final(u_int8_t[SHA256_DIGEST_LENGTH], SHA2_CTX *); - // _BOUNDED(__minbytes__,1,SHA256_DIGEST_LENGTH); - -// static void SHA384Init(SHA2_CTX *); -// static void SHA384Update(SHA2_CTX *, const u_int8_t *, size_t) -// _BOUNDED(__string__,2,3); -// static void SHA384Final(u_int8_t[SHA384_DIGEST_LENGTH], SHA2_CTX *) -// _BOUNDED(__minbytes__,1,SHA384_DIGEST_LENGTH); - -static void SHA512Init(SHA2_CTX *); -static void SHA512Update(SHA2_CTX *, const u_int8_t *, size_t); - // _BOUNDED(__string__,2,3); -static void SHA512Final(u_int8_t[SHA512_DIGEST_LENGTH], SHA2_CTX *); - // _BOUNDED(__minbytes__,1,SHA512_DIGEST_LENGTH); - -#endif /* _SHA2_H */ diff --git a/luaclib/src/lcrypt/url.c b/luaclib/src/lcrypt/url.c index c0512d1b..e5d1e7d4 100644 --- a/luaclib/src/lcrypt/url.c +++ b/luaclib/src/lcrypt/url.c @@ -1,19 +1,19 @@ -#define LUA_LIB - -#include "../../../src/core.h" +#include "lcrypt.h" #define hex_char(ch) ({(uint8_t)((ch) > 9 ? (ch) + 55: (ch) + 48);}) #define is_normal_char(ch) ({((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || ((ch) >= '0' && (ch) <= '9') ? 1 : 0;}) /* url编码 */ -int -lurlencode(lua_State *L){ +int lurlencode(lua_State *L){ size_t url_len; const char* url = luaL_checklstring(L, 1, &url_len); - if (!url) return luaL_error(L, "urlencode error: 需要传递一个有效的url字符串!"); + if (!url) + return luaL_error(L, "Invalid url text"); + luaL_Buffer convert_url; luaL_buffinit(L, &convert_url); + while (*url) { uint8_t ch = (uint8_t)*url++; if (ch == ' ') { @@ -27,18 +27,21 @@ lurlencode(lua_State *L){ char ver[3] = {'%', hex_char(((uint8_t)ch) >> 4), hex_char(((uint8_t)ch) & 15)}; luaL_addlstring(&convert_url, ver, 3); } + luaL_pushresult(&convert_url); return 1; } /* url解码 */ -int -lurldecode(lua_State *L){ +int lurldecode(lua_State *L){ size_t url_len; const char* url = luaL_checklstring(L, 1, &url_len); - if (!url) return luaL_error(L, "urldecode error: 需要传递一个有效的url字符串!"); + if (!url) + return luaL_error(L, "Invalid url text"); + luaL_Buffer convert_url; luaL_buffinit(L, &convert_url); + while (*url) { uint8_t ch = (uint8_t)*url++; if (ch != '%') { @@ -50,6 +53,7 @@ lurldecode(lua_State *L){ vert[1] = (uint8_t)*url++; luaL_addchar(&convert_url, (uint8_t)((vert[0] - 48 - ((vert[0] >= 'A') ? 7 : 0) - ((vert[0] >= 'a') ? 32 : 0)) * 16 + (vert[1] - 48 - ((vert[1] >= 'A') ? 7 : 0) - ((vert[1] >= 'a') ? 32 : 0)))); } + luaL_pushresult(&convert_url); return 1; } diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index fa29100b..3937f169 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -7,11 +7,15 @@ local hmac_md5 = CRYPT.hmac_md5 local hmac64_md5 = CRYPT.hmac64_md5 local sha1 = CRYPT.sha1 +local sha224 = CRYPT.sha224 local sha256 = CRYPT.sha256 +local sha384 = CRYPT.sha384 local sha512 = CRYPT.sha512 local hmac_sha1 = CRYPT.hmac_sha1 +-- local hmac_sha224 = CRYPT.hmac_sha224 local hmac_sha256 = CRYPT.hmac_sha256 +-- local hmac_sha384 = CRYPT.hmac_sha384 local hmac_sha512 = CRYPT.hmac_sha512 local crc32 = CRYPT.crc32 @@ -38,6 +42,17 @@ local dhexchange = CRYPT.dhexchange local urlencode = CRYPT.urlencode local urldecode = CRYPT.urldecode +local aes_ecb_encrypt = CRYPT.aes_ecb_encrypt +local aes_ecb_decrypt = CRYPT.aes_ecb_decrypt + +local aes_cbc_encrypt = CRYPT.aes_cbc_encrypt +local aes_cbc_decrypt = CRYPT.aes_cbc_decrypt + +local rsa_public_key_encode = CRYPT.rsa_public_key_encode +local rsa_private_key_decode = CRYPT.rsa_private_key_decode + +local rsa_private_key_encode = CRYPT.rsa_private_key_encode +local rsa_public_key_decode = CRYPT.rsa_public_key_decode local crypt = {} @@ -65,6 +80,14 @@ function crypt.sha1(str, hex) return hash end +function crypt.sha224 (str, hex) + local hash = sha224(str) + if hash and hex then + return hexencode(hash) + end + return hash +end + function crypt.sha256 (str, hex) local hash = sha256(str) if hash and hex then @@ -73,6 +96,14 @@ function crypt.sha256 (str, hex) return hash end +function crypt.sha384 (str, hex) + local hash = sha384(str) + if hash and hex then + return hexencode(hash) + end + return hash +end + function crypt.sha512 (str, hex) local hash = sha512(str) if hash and hex then @@ -90,6 +121,14 @@ function crypt.hmac_sha1 (key, text, hex) return hash end +-- function crypt.hmac_sha224 (key, text, hex) +-- local hash = hmac_sha224(key, text) +-- if hash and hex then +-- return hexencode(hash) +-- end +-- return hash +-- end + function crypt.hmac_sha256 (key, text, hex) local hash = hmac_sha256(key, text) if hash and hex then @@ -98,6 +137,14 @@ function crypt.hmac_sha256 (key, text, hex) return hash end +-- function crypt.hmac_sha384 (key, text, hex) +-- local hash = hmac_sha384(key, text) +-- if hash and hex then +-- return hexencode(hash) +-- end +-- return hash +-- end + function crypt.hmac_sha512 (key, text, hex) local hash = hmac_sha512(key, text) if hash and hex then @@ -154,6 +201,30 @@ function crypt.hmac64_md5 (key, text, hex) return hash end +function crypt.aes_128_cbc_encrypt(key, text, iv, hex) + local hash = aes_cbc_encrypt(16, key, text, (type(iv) == 'string' and #iv == 16) and iv or "") + if hash and hex then + return hexencode(hash) + end + return hash +end + +function crypt.aes_128_ecb_encrypt(key, text, iv, hex) + local hash = aes_ecb_encrypt(16, key, text, (type(iv) == 'string' and #iv == 16) and iv or "") + if hash and hex then + return hexencode(hash) + end + return hash +end + +function crypt.aes_128_cbc_decrypt(key, text, iv) + return aes_cbc_decrypt(16, key, text, (type(iv) == 'string' and #iv == 16) and iv or "") +end + +function crypt.aes_128_ecb_decrypt(key, text, iv) + return aes_ecb_decrypt(16, key, text, (type(iv) == 'string' and #iv == 16) and iv or "") +end + function crypt.base64urlencode(data) return base64encode(data):gsub('+', '-'):gsub('/', '_') end @@ -217,4 +288,32 @@ function crypt.urlencode (...) return urlencode(...) end +-- text 为原始文本内容, public_key_path 为公钥路径, b64 为是否为结果进行base64编码 +function crypt.rsa_public_key_encode(text, public_key_path, b64) + local hash = rsa_public_key_encode(text, public_key_path) + if hash and b64 then + return base64encode(hash) + end + return hash +end + +-- text 为加密后的内容, private_key_path 为私钥路径, b64 为是否为text先进行base64解码 +function crypt.rsa_private_key_decode(text, private_key_path, b64) + return rsa_private_key_decode(b64 and base64decode(text) or text, private_key_path) +end + +-- text 为原始文本内容, private_key_path 为公钥路径, b64 为是否为结果进行base64编码 +function crypt.rsa_private_key_encode(text, private_key_path, b64) + local hash = rsa_private_key_encode(text, private_key_path) + if hash and b64 then + return base64encode(hash) + end + return hash +end + +-- text 为加密后的内容, public_key_path 为公钥路径, b64 为是否为text先进行base64解码 +function crypt.rsa_public_key_decode(text, public_key_path, b64) + return rsa_public_key_decode(b64 and base64decode(text) or text, public_key_path) +end + return crypt diff --git a/private1024.pem b/private1024.pem new file mode 100644 index 00000000..1939c3c3 --- /dev/null +++ b/private1024.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDc84CClRm5VqlzhvG7Izfff/VBhycuhxYEubSQp6NXT8virjlp +e/g0dW94d1UENsEZYiLxepVwkHKNjxlbKMiLTjk+hs+S2g6U7bIbFLClZAdD9Bf+ +wuKD5YUUX7cRLzFEIFCNLn9E7HZ6/8iRkMtMmZf6Dx0LG/WmZ3WN1CnqwwIDAQAB +AoGBAIOC7BrNZGJMks+QopEghUEiiHhYWZn4DcMCRddT8IUnmdNyn/mJMFMJEzBA +1vmLHaReJS5WKFy3nXdklVMNE0+7khDciu/sDTSgxtZJRWvBwdYmgkzNz8jjkOzH +ZCQuqUu12AS5aFieLRB6i/FeGZiJGEoT+/KBMlVNi11xJXsBAkEA/uu88SwnjnbY +nbMwiDyWhK3/QE3vr82DBg0Z9mrmCnw7iXjB5pKUJLQbAO0NiCbi/HEkwvRkQpgx +qzh8MB4gnQJBAN3i81AnOPPUAOS2Gh1jHpNyT/zr9BZ27aKbXL7eraUky3uEdNps +rWCboyXxR0aF0BSJM4K4rL+Alveqnv/M6t8CQEuTzJKcCqY8KgCnLY5WmDGB/Jku +Ag/XGC9lFvttugIFzwj02lfnwTAYjaD6pvZkwQsi6Ek8d7UetisTNg52ACkCQDAA +gybZ9WY6fR79jlTBNsIrPsa2vQ2HGQ3OkpfwUJyjgynrk+QVEsUNppP0yLinBkcL +D4u+LBEZ3o8h6Ffqmv0CQQCKBHHaAjYAhUd3lGrseN45QSw4VpARQ7/bBKimrq+d +JCNdEpTOt6eQs8t43uOCeDjZ0H33KXG8ofcLprYE5K4G +-----END RSA PRIVATE KEY----- diff --git a/private2048.pem b/private2048.pem new file mode 100644 index 00000000..90ef8afd --- /dev/null +++ b/private2048.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAuhsl0nR17nqITIkMYqzrhbT5me5+Q+G387spzR8KWbVujoRF +Qa6DNzZS8HPNmBW8HjVZJ29uBAAtpWpoERdwvT31Kag+q9eSpX1d02uTd43cESNT +b1bYCYd3LHdPZ9GSH3gIfD/fk2P8zJytzED7cT1gDUqzCcxi3cvH6/yHb6hmYmNs +Qo1MLTn3kxyzU32J3nhNEZTlHq1XE9ydqLVqDCMRPOUuOsXcIb9ASZh3ExSPU2cx ++8WMAWWoCw/kO7EJ4Vinl8NDbsRoyXjweekYZ0bH9YrN4y05KLMhsQhoxBdmKdcJ +ErPwYad17vtmhTpdwEpCriL/iZDLUDOz1G2ngwIDAQABAoIBAQC11cqZm0KS1NQR +e6JHU073oAB0f0qNRqB2GrvX9+kkB5pS0zfb2gfIzWIyH+OUIkBgf51xY3VpoUb4 +JUQy1uVHcZ71qbY6LnHREfG3nZdDK68Ga66czYxdmyc8ogJKnMAZ0SzxQXNQTlR1 +EuzY8fD7Do2nzwGppDJBJVdb4qvt0kAEBu+0O5srUuGNzJJzktAIwzk7aFpVhw+l +cCS6ZY5rRM0AeQ2tomVvf+81bs8JOG8T7JtJ+o2cL+bZjRKhCHliQZQzByEQtOZx +J8s0mr3Tj9FzXfy6pUYSO1+GiWgrjV0nnaKj+sqE/MuuNam/nA64us8kLE7RA3gH +n7tLJwDBAoGBAOLoQf6KuE7D0WTVj5tb/CnAtPsZ18tClvElPgex/32Cjwuwy/Du +yPn/EIWO2EJX1dtRdWe34OJYtOcZk4qbFiyB/rxjsr6UAPNO/tY55nW2OqZ944Dq +3duzUb3jVeAI3HyGksvHZwBJ5t7o80BBYUdk28G2ZNjaalL1HqgXzjxjAoGBANH3 +rZPQU3yvuFQdL3HlV3h7PNyNwkyjVMgD+jCdIrySIw0DqisJFgWoVxNl92lhUkir +ZPY8dzZ6uFh6tx12QyoO2VG6Y9Pf3GvjHC1yUQqWu///+jvHwUqdPPQgHXvwKSOU +eJVaseNHwBNV35AEdRwMDOsqQUN30lhoa5fy8AJhAoGAXQR9WU2gtJlNk5qAnl2d +B7i5+F3luqt3mS99OEZdyCPnZBF76S7aMLHBIh8mxDuhraC9EmGszN00e7BebWma +M3Cu7qeoNLwTj6qIiWV+9i5X6LyesNCXVmMyVTeGkqrPSDUapHL/5HxnKmYwodyr +dksAU27j9InFIHDfumTX5KUCgYEAoXsuCOeIvfVa+33ytlLfAe8t8KYpz80x8B52 +9Zp0U7jEskamQjDbugAs7+NU87wAj5kZrfL08HZTfuDqIgOJRjhjVOLX0eRyXpst +WZp4z378Gbfh2MYZV2w0q8BjTKV4zj9qudslwpm1FGnP5bA37RkrelVmGiB2Kr4s +OZGCmyECgYBhXtk5BfZocI+l2/R2Yncn6Gx8s4s7bD+Fr5LE3FkU1neOuk7Yf1Uu +xf41e3o5YBxbt51T+pNuzF3BMxsXvp9a5HxbPsmGE99BLFwR9DompoLKeUlJcy6f +xJyXi8HBgR0OWNoTjKgaUmTgqKK5LI49C9C4Jr4c3nelo6DBb66vyw== +-----END RSA PRIVATE KEY----- diff --git a/private4096.pem b/private4096.pem new file mode 100644 index 00000000..fdecc78a --- /dev/null +++ b/private4096.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIJKAIBAAKCAgEAqkEuMgW6/rprdpHgAdr4IoGNGSfTQpCvojGHxrt3JTj+Mir4 +lZJw17cvMVfbnpWkCZ0H2jcxLR9t/blCSsNDbxe99Xbib6q6SfIkAtL3BvUU0TTz +D5ZjZR+Ko3kA+wq6yHPa8KW49659IXJzEW9Ci2SJmj6lqrTw0F0ZXDso5ILU1Ujr +YDJdT23hyygh4SI8geDtfmv88iP+V2emhIFgq8BKvExcxlBjX4kzUU/qDddo0OUr +A/wpSSPPAkXYKZ4P/0SZqbkAHNfQFzMcGbf9sGAszXI1QjwomV95Ro0IJ8HCDG2h +BvdR4AqAXI3o+DumF7kHT2pHUUP8yrodVEwWcmCjO9bPh+bm3sketDIgjs6cwuko +hoZFr7aYp9o9l0B6lbQXdg8isut2ebXYru+Rz5KJ2Ca5fOk4PHHMbZ4murBUsQq+ +XT8q8Sldxl7IqPcSEKzLaW+JNMyZ4nX/njARA9cTJDTWp38EqxUdzo1ZifVMZBeI ++xIyqOuyfp/zUpaoeGbCuGQHJ8A7WHagQkh9x1gjOtlIdFuX2scSHrhbbsccmIPJ +J18MquM8SnauF2cW9oLo/8n0dMmaXJFqb5sKgjitPcy+usA6e8ZquwWuDheSuEo5 +p/S7ti02hGhWTzCWvq/nnZnEvUp642LKcIYI8oJxXAFEGPm3HaucWGPNuqECAwEA +AQKCAgBiv8OHiANZ086YyyteaB8cBIBOYucJ6Nm1/Xx/LCSDFnd5qardNj71H27j +882SQcEPQmFXlEOVpHErhNSKgI8QVHj9cqMLmb7LPQLeEHbvNh+I6GlCVTv5Xz4O +axTChwqnUWtfwP9zyWUSjUtohVvXdczKBiQYyzIR6K3Y7Qde4vOHce/zr8KnW1hn +eTONXUozGr8lYIUp/O58y8WtfU0Q0UuHw9Lbw6yfsPhu5ScgOBy3bPRyL0PTBE4B +R3mFSgSFTsjBxGfoUtSDYEWgNTOTpXTXBLMXY8U4kEPLQ+nDBYMDqmKHo9N9Wxnw +lO7Xa+F+SPeQfFg5LnHlM+XnCurJIjctFBC+MnrGOFRMyTonUZJO7/UwQl4GkF65 +66GEsfTe4b4/JK6wr86kH2VJsJ/AW3mwQLQKjgv65Z9WyDsc7KB0FWqjU++h8DPB +xQnbetnFL/q5tRCW2AlH/CLrckc543/jCk4KQcJ1lEyvokfkbl8aPacztIS4BKbq +bWNkI3TmpGIzdBPwqLCjx41G5Ct+TwBRfsK4hWvYiW86jlcrjxwDqcAjvxLMIJ5X +DdAQaRzPZQNg5vVqS+2GQ6AhMstwraMjRT/UMx5uoKS5toWRZQxdwn+ctvKC7sFU +FGbVNwXL+zaRH4bnrZ3oGFFrVdZKNyfRR9e7gQCmICxM5QUdAQKCAQEA1nkYb3eT +BiPSXXFWyhZhoF5+Db7cxdt0HDtYmryYz6H/J2EzRvUcVuMFcAmm0FJBBPinvawR +hYI1B20Q9RjQJmp2ffsXCUbkdq3w70UbNup6vC/4aokIVrt0Oh2bkuCiVV/f8y8B +YAhyYW6q1ril4m5KumrinYlzcF212wkpx9uL44rkNRFliCUV3F8RUomBmuHZ1V1G +PQRvpyHWZJwUeeUAWqKN5qCfaUFz/yaEX7EF4c7PXoVVp40OMBuDnZjkVod24kBr +uoj7JabAr1EPt8dQhyfZvwpwiyYfZa8FbdeFtXNLEe5FZMaBtzrAPW4Z89Sa48zj +lJVDELZ63W2F+QKCAQEAyzhJNDpxebbXKyRD90D+TxIc3VMWPMa8DuuVz99+ROzX +KIuxBeH2K2lEtj31aeLahppnbis4B6u7Yh05bV88n4mfBOsxUMhl0Be5QI/cfPXv +52UKYkZE6qzIOiOi1prJy6mUFqc2z55YESx1pDSa+SvJYI5XMr0c9UpkXbMc739M +g5bskx9tmZHoCGaJ++RBCP1hxpq39vJByruJSwjbKWsVMvU14fpy89yPiS40AfLF +3XQgBmy3MZ+0QVDUNdKSwBkcES8EkgALXo3eaCXSy+NqdqllUct/b3G0oUbc8TI7 +NRwtGTMdz2pecw9vXa/ig8Tq0ZlYMfKHOUg2QKvj6QKCAQEAqGbvM9789447CJoM +3qMSRvzLF3ntGgKFugEzQlSh3C7EDSS6QZYGiYa6Z018yQg8+21PMJQiMeWaQ9l0 +vi6cif2ASs1UOjmK/FD55LYrd0RH2OoFsYklngyUZ2mGFZ8Cd+zPCMC44LHhNfXS +eMUFo7ScQqHYjIA3v1wlhfY88yvFPIZ7R9wAEBWmg6G2FUvZE0cRZwJVO2X3UZE1 +KUyQm2GflIscxqEKang1X3vb5tM13icoFny1U9li8Y05HA7IA9VcGK0iqZYTNW4o +z7/jipca+PTmeaX11py5fHsf1S6sU1xS7qJbpJRll/yuo82G3Tjr4cCoVauZvE68 +TI9J6QKCAQAkYqGIvmYO2tPPn6CjpnliAuY0Imo624JUUY3zOBrNkHI9ijVZzklb +IG/zCUjlen6R1xdpvEc96FuWh5D+qiyai/Ny2AFua1L/XSAIFTnvDcG0dnzTd61j +LyhycGr5baFv257uJ2ZC6iDugj1V9y1AK7zUkue95+pFaNprhGRL5Uj3zo/xD5F6 +C4u15VYTSZzzVRqqio0ho+JvwAAm9SD4W3niM9E/8q2eSAFTGHirWKJgsigBvnlW +YzfM8gHs2RT5XAWQdhCla2idt1z43LzPUJqBQHcpm/vnIj6rGZr5fHrpWXAhsOtH +dc4PX9YauiEeYqWAfaoy1y+q6+j6z0vxAoIBAA5KfaRS2+4THfqRHBsUkSNlQx60 +Ns2gQ2tjLtTCu7o45AjMUca5pkbnKU6+aLYMgYYqPw0pl8EdFJEwNiv3f0L0KWCG +aV5YasjIqRvN1wG5PzGeraGBE4W2qAP0xKKivep1oXOThgwyhlFdKic9/v9Ww9yz +3kmmRoNEooG52Vj6JN8pRPp+Z0pg/DP0fo408wt3webv4fhwNV4bd0DmdAmIOYhd +uGLpi1dvcNAwNd0V+0BAD1Ne3LsWGz5sa6i6vwsbXhqkPbaJg0q2VNpq2r0CM7tf +avK7GVXsdwl/PghGQ3ct3KYqnT8kYQJwbswWfkJfaVFo5AqOhLEsNzAvpK0= +-----END RSA PRIVATE KEY----- diff --git a/public1024.pem b/public1024.pem new file mode 100644 index 00000000..3e385418 --- /dev/null +++ b/public1024.pem @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDc84CClRm5VqlzhvG7Izfff/VB +hycuhxYEubSQp6NXT8virjlpe/g0dW94d1UENsEZYiLxepVwkHKNjxlbKMiLTjk+ +hs+S2g6U7bIbFLClZAdD9Bf+wuKD5YUUX7cRLzFEIFCNLn9E7HZ6/8iRkMtMmZf6 +Dx0LG/WmZ3WN1CnqwwIDAQAB +-----END PUBLIC KEY----- diff --git a/public2048.pem b/public2048.pem new file mode 100644 index 00000000..892ef942 --- /dev/null +++ b/public2048.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuhsl0nR17nqITIkMYqzr +hbT5me5+Q+G387spzR8KWbVujoRFQa6DNzZS8HPNmBW8HjVZJ29uBAAtpWpoERdw +vT31Kag+q9eSpX1d02uTd43cESNTb1bYCYd3LHdPZ9GSH3gIfD/fk2P8zJytzED7 +cT1gDUqzCcxi3cvH6/yHb6hmYmNsQo1MLTn3kxyzU32J3nhNEZTlHq1XE9ydqLVq +DCMRPOUuOsXcIb9ASZh3ExSPU2cx+8WMAWWoCw/kO7EJ4Vinl8NDbsRoyXjweekY +Z0bH9YrN4y05KLMhsQhoxBdmKdcJErPwYad17vtmhTpdwEpCriL/iZDLUDOz1G2n +gwIDAQAB +-----END PUBLIC KEY----- diff --git a/public4096.pem b/public4096.pem new file mode 100644 index 00000000..5da13134 --- /dev/null +++ b/public4096.pem @@ -0,0 +1,14 @@ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAqkEuMgW6/rprdpHgAdr4 +IoGNGSfTQpCvojGHxrt3JTj+Mir4lZJw17cvMVfbnpWkCZ0H2jcxLR9t/blCSsND +bxe99Xbib6q6SfIkAtL3BvUU0TTzD5ZjZR+Ko3kA+wq6yHPa8KW49659IXJzEW9C +i2SJmj6lqrTw0F0ZXDso5ILU1UjrYDJdT23hyygh4SI8geDtfmv88iP+V2emhIFg +q8BKvExcxlBjX4kzUU/qDddo0OUrA/wpSSPPAkXYKZ4P/0SZqbkAHNfQFzMcGbf9 +sGAszXI1QjwomV95Ro0IJ8HCDG2hBvdR4AqAXI3o+DumF7kHT2pHUUP8yrodVEwW +cmCjO9bPh+bm3sketDIgjs6cwukohoZFr7aYp9o9l0B6lbQXdg8isut2ebXYru+R +z5KJ2Ca5fOk4PHHMbZ4murBUsQq+XT8q8Sldxl7IqPcSEKzLaW+JNMyZ4nX/njAR +A9cTJDTWp38EqxUdzo1ZifVMZBeI+xIyqOuyfp/zUpaoeGbCuGQHJ8A7WHagQkh9 +x1gjOtlIdFuX2scSHrhbbsccmIPJJ18MquM8SnauF2cW9oLo/8n0dMmaXJFqb5sK +gjitPcy+usA6e8ZquwWuDheSuEo5p/S7ti02hGhWTzCWvq/nnZnEvUp642LKcIYI +8oJxXAFEGPm3HaucWGPNuqECAwEAAQ== +-----END PUBLIC KEY----- diff --git a/script/test_crypt.lua b/script/test_crypt.lua index d2ec905a..c1244e1a 100644 --- a/script/test_crypt.lua +++ b/script/test_crypt.lua @@ -1,64 +1,320 @@ local crypt = require "crypt" -local Log = require("logging"):new() +local Log = require "logging" --- 测试crc32编码 -Log:DEBUG("测试crc32",crypt.crc32("admin")) --- 测试crc64编码 -Log:DEBUG("测试crc64", crypt.crc64("admin")) +local function test_hex() + print("----------*** 开始测试 HEXENCODE/HEXDECODE ***----------") --- 测试md5编码 -Log:DEBUG("测试md5", crypt.md5("admin", true)) + -- 测试hexencode编码 + Log:DEBUG("测试hexencode", crypt.hexencode("1234567890"), crypt.hexencode("1234567890") == "31323334353637383930") + -- 测试hexdecode编码 + Log:DEBUG("测试hexdecode", crypt.hexdecode("31323334353637383930"), crypt.hexdecode("31323334353637383930") == "1234567890") --- 测试sha1编码, 第二个参数表示进行hex -Log:DEBUG("测试sha1", crypt.sha1("admin", true)) + print("----------*** 开始测试 HEXENCODE/HEXDECODE ***----------\n") +end --- 测试sha256编码, 第二个参数表示进行hex -Log:DEBUG("测试sha256", crypt.sha256("admin", true)) --- 测试sha512编码 -Log:DEBUG("测试sha512", crypt.sha512("admin", true)) +local function test_crc() + print("----------*** 开始测试CRC32/CRC64 ***----------") --- 测试hmac_sha1编码, 第二个参数表示进行hex -Log:DEBUG("测试hmac_sha1", crypt.hmac_sha1("admin", "123", true)) + -- 测试crc32编码 + Log:DEBUG("测试crc32",crypt.crc32("admin")) + -- 测试crc64编码 + Log:DEBUG("测试crc64", crypt.crc64("admin")) --- 测试hmac_sha256编码, 第二个参数表示进行hex -Log:DEBUG("测试hmac_sha256", crypt.hmac_sha256("admin", "123", true)) + print("----------*** CRC32/CRC64 测试完毕 ***----------\n") +end --- 测试hmac_sha512编码, 第二个参数表示进行hex -Log:DEBUG("测试hmac_sha512", crypt.hmac_sha512("admin", "123", true)) --- 测试hmac_md5编码 -Log:DEBUG("测试hmac_md5", crypt.hmac_md5("admin", "123", true)) +local function test_url( ... ) + print("----------*** 开始测试 urlencode/urldecode ***----------") --- 测试hmac64编码 -Log:DEBUG("测试hmac64", crypt.hmac64("12345678", "abcdefgh", true)) + local url = "https://www.baiud.com/我是谁/api?name=水果糖的小铺子&age=30" --- 测试hmac64_md5编码 -Log:DEBUG("测试hmac64_md5", crypt.hmac64_md5("12345678", "abcdefgh", true)) + Log:DEBUG("测试urlencode: " .. crypt.urlencode(url)) --- 测试hmac_hash编码 -Log:DEBUG("测试hmac_hash", crypt.hmac_hash("12345678", "abcdefgh", true)) + Log:DEBUG("测试urlencode: " .. crypt.urldecode(crypt.urlencode(url))) --- 测试randomkey编码 -Log:DEBUG("测试randomkey", crypt.randomkey(true)) + assert(crypt.urldecode(crypt.urlencode(url)) == url, "测试失败") --- 测试hashkey编码 -Log:DEBUG("测试hashkey", crypt.hashkey("admin", true)) + print("----------*** urlencode/urldecode 测试完毕 ***----------\n") +end --- 测试desencode编码 -Log:DEBUG("测试desencode", crypt.desencode("12345678", "87654321")) --- 测试desdecode编码 -Log:DEBUG("测试desdecode", crypt.desdecode("12345678", crypt.desencode("12345678", "87654321")) == "87654321") +local function test_sha() --- 测试hexencode编码 -Log:DEBUG("测试hexencode", crypt.hexencode("1234567890"), crypt.hexencode("1234567890") == "31323334353637383930") --- 测试hexdecode编码 -Log:DEBUG("测试hexdecode", crypt.hexdecode("31323334353637383930"), crypt.hexdecode("31323334353637383930") == "1234567890") + print("----------*** 开始测试MD5/SHA128/SHA256/SHA512 ***----------") --- 测试base64encode编码 -Log:DEBUG("测试base64encode", crypt.base64encode("1234567890"), crypt.base64encode("1234567890") == "MTIzNDU2Nzg5MA==") --- 测试base64decode编码 -Log:DEBUG("测试base64decode", crypt.base64decode("MTIzNDU2Nzg5MA=="), crypt.base64decode("MTIzNDU2Nzg5MA==") == "1234567890") + local text = "123456789admin" --- 测试str_xor编码, 2次xor将还原. -Log:DEBUG("测试str_xor", crypt.xor_str("1234567890", "123", true), crypt.xor_str(crypt.xor_str("1234567890", "123"), "123") == "1234567890") + Log:DEBUG("测试md5 :" .. crypt.md5(text, true)) + assert(crypt.md5(text, true) == "dce70093fd997b5e1a37c86dadaf0a48", "MD5测试失败") + + Log:DEBUG("测试sha128 :" .. crypt.sha1(text, true)) + assert(crypt.sha1(text, true) == "1e2f566cbda0a9c855240bf21b8bae030404cad7", "SHA128测试失败") + + Log:DEBUG("测试sha224 :" .. crypt.sha224(text, true)) + assert(crypt.sha224(text, true) == "47e386607ca91384c4ccca3dfd3da211aaf618b7a043bac2aa138495", "SHA224测试失败") + + Log:DEBUG("测试sha256 :" .. crypt.sha256(text, true)) + assert(crypt.sha256(text, true) == "e39594b63146c3f089bc12e1421cb3fe2fb9e4925908a995989e635d9bd1b096", "SHA256测试失败") + + Log:DEBUG("测试sha384 :" .. crypt.sha384(text, true)) + assert(crypt.sha384(text, true) == "18f26760edbc390cd61834e3100fbf88a14f2fe7b4dfdb11d2d5aea92388274163d5f4ae0cd9662b8a88e148d3b358f4", "SHA384测试失败") + + Log:DEBUG("测试sha512 :" .. crypt.sha512(text, true)) + assert(crypt.sha512(text, true) == "434042f8e8a262ffa53cb2ac1366aa4647c66464e9db8442338f0398cf400d7f966b360f0d12f1670fba01f2a0e900a3295143162ec5a215cf2d6b321294d02e", "SHA512测试失败") + + print("----------*** MD5/SHA128/SHA256/SHA512 测试完毕 ***----------\n") +end + +local function test_hmac() + + print("----------*** 开始测试HMAC(MD5/SHA128/SHA256/SHA512) ***----------") + + local text = "123456789admin" + local key = "admin" + + Log:DEBUG("hmac_md5 :" .. crypt.hmac_md5(key, text, true)) + assert(crypt.hmac_md5(key, text, true) == "fbe0f8e2cfb44139cfdf7162d6b9e709", "HMAC_MD5测试失败") + + Log:DEBUG("hmac_sha128 :" .. crypt.hmac_sha1(key, text, true)) + assert(crypt.hmac_sha1(key, text, true) == "197c3254b4c935717b7d7ca38fbdb642d22a63f9", "HMAC_SHA128测试失败") + + Log:DEBUG("hmac_sha256 :" .. crypt.hmac_sha256(key, text, true)) + assert(crypt.hmac_sha256(key, text, true) == "824902bf7fc037243b6cf0444a4887d21779526b0937b9f76b8ac23dafa0eb45", "HMAC_SHA256测试失败") + + Log:DEBUG("hmac_sha512 :" .. crypt.hmac_sha512(key, text, true)) + assert(crypt.hmac_sha512(key, text, true) == "346b81fb7771816ad1206d996de063859e8225d09a74776ded859d1e3e388b34aae09636c5c60b2ad7d88f5518483cc3d952753573b856aab4b96531a3cb4094", "HMAC_SHA512测试失败") + + -- 测试hmac64编码 + Log:DEBUG("hmac64", crypt.hmac64("12345678", "abcdefgh", true)) + + -- 测试hmac64_md5编码 + Log:DEBUG("hmac64_md5", crypt.hmac64_md5("12345678", "abcdefgh", true)) + + -- 测试hmac_hash编码 + Log:DEBUG("hmac_hash", crypt.hmac_hash("12345678", "abcdefgh", true)) + + print("----------*** HMAC(MD5/SHA128/SHA256/SHA512) 测试完毕 ***----------\n") + +end + +local function test_des( ... ) + + print("----------*** 开始测试 desencode/desdecode ***----------") + + local text = [[{"code":200,"data":[1,2,3,4,5,6,7,8,9,10]}]] + + local key = "12345678" + + -- 测试desencode编码 + Log:DEBUG("测试desencode", crypt.desencode(key, text, true)) + -- 测试desdecode编码 + Log:DEBUG("测试desdecode", crypt.desdecode(key, crypt.desencode(key, text)) == text) + + print("----------*** desencode/desdecode 测试完毕 ***----------\n") + + +end + +local function test_other( ... ) + print("----------*** 开始测试 hashkey/randomkey ***----------") + -- 测试randomkey编码 + Log:DEBUG("测试randomkey", crypt.randomkey(true)) + + -- 测试hashkey编码 + Log:DEBUG("测试hashkey", crypt.hashkey("admin", true)) + + print("----------*** hashkey/randomkey 测试完毕 ***----------\n") +end + + +local function test_strxor( ... ) + + local rawData = "admin00000" + local key = "1234567890" + + print(crypt.xor_str(key, rawData, true)) + + print(crypt.xor_str(key, crypt.xor_str(key, rawData))) + + -- Log:DEBUG("xor_str 原始数据: " .. rawData, "异或key: " .. key) + + -- assert(crypt.xor_str(rawData, crypt.xor_str(rawData, key)) == rawData) + +end + +local function test_b64() + + print("----------*** 开始测试 base64 ***----------") + + local rawData = "123456789" + + Log:DEBUG("测试base64:" .. crypt.base64encode(rawData), "原始数据为:" .. rawData) + + assert(rawData == crypt.base64decode(crypt.base64encode(rawData))) + + print("----------*** base64 测试完毕 ***----------\n") + +end + +local function test_aes() + + print("----------*** 开始测试 aes_cbc/aes_ecb ***----------") + + local text = [[{"code":200,"msg":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]}]] + local key_128 = "abcdefghabcdefgh" + local iv_128 = "98765432100admin" + + local cbc_encryptData = crypt.aes_128_cbc_encrypt(key_128, text, iv_128) + local cbc_rawData = crypt.aes_128_cbc_decrypt(key_128, cbc_encryptData, iv_128) + + Log:DEBUG("测试aes_128_cbc_encrypt: " .. crypt.hexencode(cbc_encryptData)) + assert(cbc_rawData == text, "aes加密/解密失败") + + local ecb_encryptData = crypt.aes_128_ecb_encrypt(key_128, text, iv_128) + local ecb_rawData = crypt.aes_128_ecb_decrypt(key_128, ecb_encryptData, iv_128) + + Log:DEBUG("测试aes_128_ecb_encrypt: " .. crypt.hexencode(ecb_encryptData)) + assert(ecb_rawData == text, "aes加密/解密失败") + + print("----------*** aes_cbc/aes_ecb 测试完毕 ***----------\n") + +end + +local function test_rsa() + + + local function test_rsa_1024 ( ... ) + -- 生成公钥/私钥方法: + + -- 1. 使用openssl命令生成1024位私钥: openssl genrsa -out private1024.pem 1024 + + -- 2. 使用openssl命令根据1024位私钥生成公钥: openssl rsa -in private1024.pem -out public1024.pem -pubout + + local publick_key_path = "public1024.pem" + local private_key_path = "private1024.pem" + + local text = [[{"code":200,"data":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,26,26,27,28,29,30,31,32,33,34,35]}]] + + local encData = crypt.rsa_public_key_encode(text, publick_key_path, true) + -- print(encData) + local decData = crypt.rsa_private_key_decode(encData, private_key_path, true) + + assert(decData == text, "rsa 1024 公钥加密 -> 私钥解密 失败." .. decData) + + + local encData = crypt.rsa_private_key_encode(text, private_key_path, true) + -- print(encData) + local decData = crypt.rsa_public_key_decode(encData, publick_key_path, true) + + assert(decData == text, "rsa 1024 私钥加密 -> 公钥解密 失败.") + + end + + local function test_rsa_2048( ... ) + -- 生成公钥/私钥方法: + + -- 1. 使用openssl命令生成2048位私钥: openssl genrsa -out private2048.pem 2048 + + -- 2. 使用openssl命令根据2048位私钥生成公钥: openssl rsa -in private2048.pem -out public2048.pem -pubout + + local publick_key_path = "public2048.pem" + local private_key_path = "private2048.pem" + + local text = [[{"code":200,"data":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,26,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40]}]] + + local encData = crypt.rsa_public_key_encode(text, publick_key_path, true) + -- print(encData) + local decData = crypt.rsa_private_key_decode(encData, private_key_path, true) + -- print(decData) + assert(decData == text, "rsa 2048 公钥加密 -> 私钥解密 失败." .. decData) + + + local encData = crypt.rsa_private_key_encode(text, private_key_path, true) + -- print(encData) + local decData = crypt.rsa_public_key_decode(encData, publick_key_path, true) + -- print(decData) + assert(decData == text, "rsa 2048 私钥加密 -> 公钥解密 失败.") + + end + + local function test_rsa_4096( ... ) + -- 生成公钥/私钥方法: + + -- 1. 使用openssl命令生成4096位私钥: openssl genrsa -out private4096.pem 4096 + + -- 2. 使用openssl命令根据4096位私钥生成公钥: openssl rsa -in private4096.pem -out public4096.pem -pubout + + local publick_key_path = "public4096.pem" + local private_key_path = "private4096.pem" + + local text = [[{"code":200,"data":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,26,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,]}]] + + local encData = crypt.rsa_public_key_encode(text, publick_key_path, true) + -- print(encData) + local decData = crypt.rsa_private_key_decode(encData, private_key_path, true) + -- print(decData) + assert(decData == text, "rsa 4096 公钥加密 -> 私钥解密 失败." .. decData) + + + local encData = crypt.rsa_private_key_encode(text, private_key_path, true) + -- print(encData) + local decData = crypt.rsa_public_key_decode(encData, publick_key_path, true) + -- print(decData) + assert(decData == text, "rsa 4096 私钥加密 -> 公钥解密 失败.") + + end + + print("----------*** 开始测试 rsa public/private encode/decode ***----------") + + test_rsa_1024() + + test_rsa_2048() + + test_rsa_4096() + + print("----------*** rsa public/private encode/decode 测试完成 ***----------") + +end + +local function main() + + local examples = { + + -- test_hex, + + -- test_crc, + + -- test_url, + + -- test_b64, + + -- test_sha, + + -- test_hmac, + + -- test_des, + + -- test_strxor, + + -- test_aes, + + -- test_other, + + test_rsa(), + + } + + for _, f in ipairs(examples) do + + f() + + end + + if #examples > 0 then + Log:DEBUG("总共完成了" .. #examples .. "个测试用例.") + end + +end + +main() \ No newline at end of file From 78ff1060636505619fa9db7b5926c606de6fb8f4 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Fri, 28 Feb 2020 00:41:34 +0800 Subject: [PATCH 479/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0crypt=E4=B8=80?= =?UTF-8?q?=E4=BA=9B=E6=8F=90=E7=A4=BA=E5=B9=B6=E5=8E=BB=E9=99=A4=E8=B0=83?= =?UTF-8?q?=E8=AF=95=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/Makefile | 2 +- luaclib/src/lcrypt/rsa.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/luaclib/src/lcrypt/Makefile b/luaclib/src/lcrypt/Makefile index 3c566ccb..3adf3b04 100644 --- a/luaclib/src/lcrypt/Makefile +++ b/luaclib/src/lcrypt/Makefile @@ -9,7 +9,7 @@ default : CC = cc -CFLAGS = -O3 -Wall -shared -fPIC -fno-strict-aliasing -ggdb +CFLAGS = -O3 -Wall -shared -fPIC -fno-strict-aliasing DLL = -lcore -llua -lcrypto INCLUDES = -I../../../ -I/usr/local/include diff --git a/luaclib/src/lcrypt/rsa.c b/luaclib/src/lcrypt/rsa.c index e4c1e89b..e534bb76 100644 --- a/luaclib/src/lcrypt/rsa.c +++ b/luaclib/src/lcrypt/rsa.c @@ -103,7 +103,7 @@ int lrsa_private_key_decode(lua_State *L) { RSA* key = new_private_key(L); if (!key) - return luaL_error(L, "Can't find public key or Invalid public key"); + return luaL_error(L, "Can't find private key or Invalid private key"); luaL_Buffer b; unsigned char* result = (unsigned char*)luaL_buffinitsize(L, &b, RSA_size(key)); @@ -132,7 +132,7 @@ int lrsa_private_key_encode(lua_State *L){ RSA* key = new_private_key(L); if (!key) - return luaL_error(L, "Can't find public key or Invalid public key"); + return luaL_error(L, "Can't find private key or Invalid private key"); luaL_Buffer b; unsigned char* result = (unsigned char*)luaL_buffinitsize(L, &b, RSA_size(key)); From 8d11067007b7ec0963097917678e90a56fad6450 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Fri, 28 Feb 2020 01:11:05 +0800 Subject: [PATCH 480/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0ltcp.c?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index cc72e5eb..a277640c 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -812,7 +812,7 @@ luaopen_tcp(lua_State *L){ SSL_load_error_strings(); // ERR_load_crypto_strings(); // OpenSSL_add_ssl_algorithms(); - CRYPTO_set_mem_functions(xmalloc, xrealloc, xfree); + // CRYPTO_set_mem_functions(xmalloc, xrealloc, xfree); /* 添加SSL支持 */ luaL_newmetatable(L, "__TCP__"); lua_pushstring (L, "__index"); From 650467832fcc9801c1d0ab60b893cf34a618b5f6 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Fri, 28 Feb 2020 02:23:23 +0800 Subject: [PATCH 481/956] =?UTF-8?q?crypt=E5=BA=93=E5=A2=9E=E5=8A=A0sha128W?= =?UTF-8?q?ithRSA=E4=B8=8Esha256WithRSA=E7=AD=BE=E5=90=8D/=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/lcrypt.c | 5 ++ luaclib/src/lcrypt/lcrypt.h | 7 ++- luaclib/src/lcrypt/rsa.c | 119 +++++++++++++++++++++++++++++++++++- lualib/crypt/init.lua | 38 ++++++++++++ script/test_crypt.lua | 20 ++++-- 5 files changed, 183 insertions(+), 6 deletions(-) diff --git a/luaclib/src/lcrypt/lcrypt.c b/luaclib/src/lcrypt/lcrypt.c index 74b8bae8..cfb57fe4 100644 --- a/luaclib/src/lcrypt/lcrypt.c +++ b/luaclib/src/lcrypt/lcrypt.c @@ -80,6 +80,11 @@ luaopen_lcrypt(lua_State *L) { // 私钥加密 -> 公钥解密 {"rsa_private_key_encode", lrsa_private_key_encode}, {"rsa_public_key_decode", lrsa_public_key_decode}, + //shawithrsa + {"sha128WithRsa_sign", lSha128WithRsa_sign}, + {"sha128WithRsa_verify", lSha128WithRsa_verify}, + {"sha256WithRsa_sign", lSha256WithRsa_sign}, + {"sha256WithRsa_verify", lSha256WithRsa_verify}, // aes 加密 {"aes_ecb_encrypt", laes_ecb_encrypt}, {"aes_cbc_encrypt", laes_cbc_encrypt}, diff --git a/luaclib/src/lcrypt/lcrypt.h b/luaclib/src/lcrypt/lcrypt.h index 285caa5c..8c7b5704 100644 --- a/luaclib/src/lcrypt/lcrypt.h +++ b/luaclib/src/lcrypt/lcrypt.h @@ -63,4 +63,9 @@ int lrsa_public_key_encode(lua_State *L); int lrsa_private_key_decode(lua_State *L); int lrsa_private_key_encode(lua_State *L); -int lrsa_public_key_decode(lua_State *L); \ No newline at end of file +int lrsa_public_key_decode(lua_State *L); + +int lSha128WithRsa_sign(lua_State *L); +int lSha128WithRsa_verify(lua_State *L); +int lSha256WithRsa_sign(lua_State *L); +int lSha256WithRsa_verify(lua_State *L); \ No newline at end of file diff --git a/luaclib/src/lcrypt/rsa.c b/luaclib/src/lcrypt/rsa.c index e534bb76..bc8b9cc3 100644 --- a/luaclib/src/lcrypt/rsa.c +++ b/luaclib/src/lcrypt/rsa.c @@ -1,5 +1,5 @@ #include "lcrypt.h" -#include +// #include static inline RSA* READ_PEM_PUB_KEY(FILE *f) { RSA *key = NULL ; @@ -174,5 +174,122 @@ int lrsa_public_key_decode(lua_State *L){ RSA_free(key); + return 1; +} + + +int lSha256WithRsa_sign(lua_State *L){ + + size_t text_size = 0; + const uint8_t* text = get_text(L, &text_size); + if (!text || text_size < 1) + return luaL_error(L, "Invalid text"); + + RSA* rsa = new_private_key(L); + if (!rsa) + return luaL_error(L, "Can't find valide private rsa."); + + unsigned char sha_data[SHA256_DIGEST_LENGTH]; + SHA256((const unsigned char*) text, text_size, sha_data); + + luaL_Buffer b; + unsigned char* result = (unsigned char*)luaL_buffinitsize(L, &b, RSA_size(rsa)); + uint32_t result_size = 0; + + if (1 != RSA_sign(NID_sha256, sha_data, SHA256_DIGEST_LENGTH, result, &result_size, rsa)) { + RSA_free(rsa); + return luaL_error(L, "computing result size failed."); + } + + luaL_pushresultsize(&b, result_size); + + RSA_free(rsa); + + return 1; +} + +int lSha256WithRsa_verify(lua_State *L){ + + size_t text_size = 0; + const uint8_t* text = get_text(L, &text_size); + if (!text || text_size < 1) + return luaL_error(L, "Invalid text"); + + RSA* rsa = new_public_key(L); + if (!rsa) + return luaL_error(L, "Can't find valide private rsa."); + + size_t sign_size = 0; + const uint8_t *sign = (const uint8_t*)luaL_checklstring(L, 3, &sign_size); + if (!text || text_size < 1) + return luaL_error(L, "Invalid text"); + + unsigned char sha_data[SHA256_DIGEST_LENGTH]; + SHA256((const unsigned char*) text, text_size, sha_data); + + if (1 != RSA_verify(NID_sha256, sha_data, SHA256_DIGEST_LENGTH, sign, sign_size, rsa)) { + RSA_free(rsa); + return 0; + } + RSA_free(rsa); + lua_pushboolean(L, 1); + return 1; +} + +int lSha128WithRsa_sign(lua_State *L){ + + size_t text_size = 0; + const uint8_t* text = get_text(L, &text_size); + if (!text || text_size < 1) + return luaL_error(L, "Invalid text"); + + RSA* rsa = new_private_key(L); + if (!rsa) + return luaL_error(L, "Can't find valide private rsa."); + + unsigned char sha_data[SHA_DIGEST_LENGTH]; + SHA256((const unsigned char*) text, text_size, sha_data); + + luaL_Buffer b; + unsigned char* result = (unsigned char*)luaL_buffinitsize(L, &b, RSA_size(rsa)); + uint32_t result_size = 0; + + if (1 != RSA_sign(NID_sha1, sha_data, SHA_DIGEST_LENGTH, result, &result_size, rsa)) { + RSA_free(rsa); + return luaL_error(L, "computing result size failed."); + } + + luaL_pushresultsize(&b, result_size); + + RSA_free(rsa); + + return 1; +} + +int lSha128WithRsa_verify(lua_State *L){ + + size_t text_size = 0; + const uint8_t* text = get_text(L, &text_size); + if (!text || text_size < 1) + return luaL_error(L, "Invalid text"); + + RSA* rsa = new_public_key(L); + if (!rsa) + return luaL_error(L, "Can't find valide private rsa."); + + size_t sign_size = 0; + const uint8_t *sign = (const uint8_t*)luaL_checklstring(L, 3, &sign_size); + if (!text || text_size < 1) + return luaL_error(L, "Invalid text"); + + unsigned char sha_data[SHA_DIGEST_LENGTH]; + SHA256((const unsigned char*) text, text_size, sha_data); + + if (1 != RSA_verify(NID_sha1, sha_data, SHA_DIGEST_LENGTH, sign, sign_size, rsa)) { + RSA_free(rsa); + return 0; + } + RSA_free(rsa); + lua_pushboolean(L, 1); return 1; } \ No newline at end of file diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index 3937f169..ef7e74f9 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -54,6 +54,12 @@ local rsa_private_key_decode = CRYPT.rsa_private_key_decode local rsa_private_key_encode = CRYPT.rsa_private_key_encode local rsa_public_key_decode = CRYPT.rsa_public_key_decode +local sha128WithRsa_sign = CRYPT.sha128WithRsa_sign +local sha128WithRsa_verify = CRYPT.sha128WithRsa_verify + +local sha256WithRsa_sign = CRYPT.sha256WithRsa_sign +local sha256WithRsa_verify = CRYPT.sha256WithRsa_verify + local crypt = {} function crypt.md5(str, hex) @@ -316,4 +322,36 @@ function crypt.rsa_public_key_decode(text, public_key_path, b64) return rsa_public_key_decode(b64 and base64decode(text) or text, public_key_path) end + +-- sha with rsa sign/verify +function crypt.sha128_with_rsa_sign(text, private_key_path, hex) + local hash = sha128WithRsa_sign(text, private_key_path) + if hash and hex then + return hexencode(hash) + end + return hash +end + +function crypt.sha128_with_rsa_verify(text, public_key_path, sign, hex) + if hex and sign then + sign = hexdecode(sign) + end + return sha128WithRsa_verify(text, public_key_path, sign) +end + +function crypt.sha256_with_rsa_sign(text, private_key_path, hex) + local hash = sha256WithRsa_sign(text, private_key_path) + if hash and hex then + return hexencode(hash) + end + return hash +end + +function crypt.sha256_with_rsa_verify(text, public_key_path, sign, hex) + if hex and sign then + sign = hexdecode(sign) + end + return sha256WithRsa_verify(text, public_key_path, sign) +end + return crypt diff --git a/script/test_crypt.lua b/script/test_crypt.lua index c1244e1a..bab0cd67 100644 --- a/script/test_crypt.lua +++ b/script/test_crypt.lua @@ -265,6 +265,18 @@ local function test_rsa() end + local function test_sha_with_rsa( ... ) + local publick_key_path = "public1024.pem" + local private_key_path = "private1024.pem" + local text = [[{"code":200,"data":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,26,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,]}]] + + local sign = crypt.sha128_with_rsa_sign(text, private_key_path, true) + assert(crypt.sha128_with_rsa_verify(text, publick_key_path, sign, true), "sha128 with rsa签名/验证失败.") + + local sign = crypt.sha256_with_rsa_sign(text, private_key_path, true) + assert(crypt.sha256_with_rsa_verify(text, publick_key_path, sign, true), "sha256 with rsa签名/验证失败.") + end + print("----------*** 开始测试 rsa public/private encode/decode ***----------") test_rsa_1024() @@ -273,6 +285,8 @@ local function test_rsa() test_rsa_4096() + test_sha_with_rsa() + print("----------*** rsa public/private encode/decode 测试完成 ***----------") end @@ -301,7 +315,7 @@ local function main() -- test_other, - test_rsa(), + test_rsa, } @@ -311,9 +325,7 @@ local function main() end - if #examples > 0 then - Log:DEBUG("总共完成了" .. #examples .. "个测试用例.") - end + Log:DEBUG("总共完成了" .. #examples .. "个测试用例.") end From a1fb6d5a0aa0376ad1ce3ce7d241495b22014005 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Fri, 28 Feb 2020 18:38:13 +0800 Subject: [PATCH 482/956] =?UTF-8?q?=E4=B8=BAcrypt=E5=BA=93=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0uuid=20v4=E7=89=88=E7=94=9F=E6=88=90=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/Makefile | 2 +- luaclib/src/lcrypt/lcrypt.c | 1 + luaclib/src/lcrypt/lcrypt.h | 3 +++ luaclib/src/lcrypt/uuid.c | 40 +++++++++++++++++++++++++++++++++++++ lualib/crypt/init.lua | 6 ++++++ script/test_crypt.lua | 13 +++++++++++- 6 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 luaclib/src/lcrypt/uuid.c diff --git a/luaclib/src/lcrypt/Makefile b/luaclib/src/lcrypt/Makefile index 3adf3b04..a029a79d 100644 --- a/luaclib/src/lcrypt/Makefile +++ b/luaclib/src/lcrypt/Makefile @@ -16,5 +16,5 @@ INCLUDES = -I../../../ -I/usr/local/include LIBS = -L../ -L../../../ -L/usr/local/lib build: - @$(CC) -o lcrypt.so lcrypt.c aes.c des.c dh.c rsa.c sha.c hmac.c hmac_ex.c b64.c crc.c url.c hex.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) + @$(CC) -o lcrypt.so lcrypt.c aes.c des.c dh.c rsa.c sha.c hmac.c hmac_ex.c b64.c crc.c url.c hex.c uuid.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) @mv *.so ../../ diff --git a/luaclib/src/lcrypt/lcrypt.c b/luaclib/src/lcrypt/lcrypt.c index cfb57fe4..9ecf24d1 100644 --- a/luaclib/src/lcrypt/lcrypt.c +++ b/luaclib/src/lcrypt/lcrypt.c @@ -40,6 +40,7 @@ LUAMOD_API int luaopen_lcrypt(lua_State *L) { luaL_checkversion(L); luaL_Reg lcrypt[] = { + {"uuid", luuid}, { "hashkey", lhashkey }, { "randomkey", lrandomkey }, { "desencode", ldesencode }, diff --git a/luaclib/src/lcrypt/lcrypt.h b/luaclib/src/lcrypt/lcrypt.h index 8c7b5704..41708a12 100644 --- a/luaclib/src/lcrypt/lcrypt.h +++ b/luaclib/src/lcrypt/lcrypt.h @@ -6,9 +6,12 @@ #include #include #include +#include #define SMALL_CHUNK 256 +int luuid(lua_State *L); + int ltohex(lua_State *L); int lfromhex(lua_State *L); diff --git a/luaclib/src/lcrypt/uuid.c b/luaclib/src/lcrypt/uuid.c new file mode 100644 index 00000000..801329c7 --- /dev/null +++ b/luaclib/src/lcrypt/uuid.c @@ -0,0 +1,40 @@ +#include "lcrypt.h" + +#define UUID_V4_LENGTH 36 + +static inline int uuid_v4_gen(char *buffer) +{ + union { + struct { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clk_seq_hi_res; + uint8_t clk_seq_low; + uint8_t node[6]; + }; + uint8_t __rnd[16]; + } uuid; + + int rc = RAND_bytes(uuid.__rnd, sizeof(uuid)); + + uuid.clk_seq_hi_res = (uint8_t) ((uuid.clk_seq_hi_res & 0x3F) | 0x80); + uuid.time_hi_and_version = (uint16_t) ((uuid.time_hi_and_version & 0x0FFF) | 0x4000); + + snprintf(buffer, UUID_V4_LENGTH, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, + uuid.clk_seq_hi_res, uuid.clk_seq_low, + uuid.node[0], uuid.node[1], uuid.node[2], + uuid.node[3], uuid.node[4], uuid.node[5] + ); + + return rc; +} + +int luuid(lua_State *L) { + luaL_Buffer b; + char *UUID = luaL_buffinitsize(L, &b, UUID_V4_LENGTH); + uuid_v4_gen(UUID); + luaL_pushresultsize(&b, UUID_V4_LENGTH); + return 1; +} \ No newline at end of file diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index ef7e74f9..d3d8e110 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -1,6 +1,8 @@ local CRYPT = require "lcrypt" local new_tab = require("sys").new_tab +local uuid = CRYPT.uuid + local md5 = CRYPT.md5 local hmac64 = CRYPT.hmac64 local hmac_md5 = CRYPT.hmac_md5 @@ -62,6 +64,10 @@ local sha256WithRsa_verify = CRYPT.sha256WithRsa_verify local crypt = {} +function crypt.uuid() + return uuid() +end + function crypt.md5(str, hex) local hash = md5(str) if hash and hex then diff --git a/script/test_crypt.lua b/script/test_crypt.lua index bab0cd67..e691987b 100644 --- a/script/test_crypt.lua +++ b/script/test_crypt.lua @@ -291,6 +291,15 @@ local function test_rsa() end +local function test_uuid( ... ) + + print("----------*** 开始测试 uuid 生成 ***----------") + + Log:DEBUG("生成的UUID为: " .. crypt.uuid()) + + print("----------*** uuid 测试完成 ***----------") +end + local function main() local examples = { @@ -315,7 +324,9 @@ local function main() -- test_other, - test_rsa, + -- test_rsa, + + test_uuid, } From 97eb5082f38e6d302299f35660bfb722c73062b1 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 29 Feb 2020 01:36:24 +0800 Subject: [PATCH 483/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E4=B8=8E=E6=B5=8B=E8=AF=95=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Person.pb | 14 ++++++++++++++ Person.proto | 19 +++++++++++++++++++ script/test_protobuf.lua | 18 ++++++++++++++++-- 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 Person.pb create mode 100644 Person.proto diff --git a/Person.pb b/Person.pb new file mode 100644 index 00000000..e183002e --- /dev/null +++ b/Person.pb @@ -0,0 +1,14 @@ + + + Person.proto" +Person +name ( Rname +age ( Rage +hand ( 2 .Person.HandRhand +foot ( 2 .Person.FootRfoot0 +Hand +left ( Rleft +right ( Rright0 +Foot +left ( Rleft +right ( Rrightbproto3 \ No newline at end of file diff --git a/Person.proto b/Person.proto new file mode 100644 index 00000000..a0741372 --- /dev/null +++ b/Person.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +message Person +{ + message Hand + { + string left = 1; + string right = 2; + } + message Foot + { + string left = 1; + string right = 2; + } + string name = 1; + uint32 age = 2; + Hand hand = 3; + Foot foot = 4; +} diff --git a/script/test_protobuf.lua b/script/test_protobuf.lua index 23eb2c43..29cc7bf4 100644 --- a/script/test_protobuf.lua +++ b/script/test_protobuf.lua @@ -1,8 +1,22 @@ -local Log = require ("logging"):new() +local Log = require "logging" local pb = require "protobuf" -Log:DEBUG(pb.loadfile("Person.lua")) +--[[ +测试与使用指南: + + 1. 安装protobuf的命令行二进制生成工具protoc. + + 2. 使用命令 protoc Person.proto -o Person.pb 生成协议文件. + + 3. 使用loadfile读取生成的协议文件Person.pb后就完成了数据结构注册与导入. + + 4. 这有时候就可以开始使用encode/decode方法进行代码测试. + + 5. 需要注意的是: protobuf协议需要"先定义(注册), 后使用", 支持protobuf v2/v3版本语法. +]] + +Log:DEBUG(pb.loadfile("Person.pb")) local pb_string = pb.encode("Person", { name = "CandyMi", From 2380c6040f1e4c3b56bd3eb73a67d8223a5be2cd Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 29 Feb 2020 21:04:28 +0800 Subject: [PATCH 484/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0admin=E5=BA=93?= =?UTF-8?q?=E4=B8=BAutf8mb4=E5=AD=97=E7=AC=A6=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/db/database.sql | 62 +++++++++++++++--------------------- 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/lualib/admin/db/database.sql b/lualib/admin/db/database.sql index e53f8a54..90a70f98 100644 --- a/lualib/admin/db/database.sql +++ b/lualib/admin/db/database.sql @@ -13,14 +13,14 @@ /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; +/*!40101 SET NAMES utf8mb4 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -# Dump of table cfadmin_headers -# ------------------------------------------------------------ - +# ---------------------------- +# Table structure for cfadmin_headers +# ---------------------------- CREATE TABLE IF NOT EXISTS `cfadmin_headers` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', `name` varchar(255) NOT NULL COMMENT '头部名称', @@ -29,13 +29,11 @@ CREATE TABLE IF NOT EXISTS `cfadmin_headers` ( `update_at` int(11) unsigned NOT NULL COMMENT '修改时间', `active` tinyint(4) unsigned NOT NULL COMMENT '删除标志', PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - - -# Dump of table cfadmin_menus -# ------------------------------------------------------------ +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='顶部菜单表'; +# ---------------------------- +# Table structure for cfadmin_menus +# ---------------------------- CREATE TABLE IF NOT EXISTS `cfadmin_menus` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', `parent` int(11) unsigned NOT NULL COMMENT '父菜单ID', @@ -46,14 +44,12 @@ CREATE TABLE IF NOT EXISTS `cfadmin_menus` ( `update_at` int(11) unsigned NOT NULL COMMENT '更新时间', `active` tinyint(4) unsigned NOT NULL COMMENT '删除标志', PRIMARY KEY (`id`), - KEY `com_index` (`active`,`url`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - - -# Dump of table cfadmin_permissions -# ------------------------------------------------------------ + KEY `parant_index` (`parent`) USING BTREE COMMENT '父ID索引' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='侧边菜单表'; +# ---------------------------- +# Table structure for cfadmin_permissions +# ---------------------------- CREATE TABLE IF NOT EXISTS `cfadmin_permissions` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', `role_id` int(11) unsigned NOT NULL COMMENT '所属角色', @@ -63,13 +59,11 @@ CREATE TABLE IF NOT EXISTS `cfadmin_permissions` ( `active` tinyint(4) unsigned NOT NULL COMMENT '是否启用', PRIMARY KEY (`id`), KEY `com_index` (`active`,`role_id`,`menu_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - - -# Dump of table cfadmin_roles -# ------------------------------------------------------------ +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色权限表'; +# ---------------------------- +# Table structure for cfadmin_roles +# ---------------------------- CREATE TABLE IF NOT EXISTS `cfadmin_roles` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', `name` varchar(255) NOT NULL COMMENT '角色名称', @@ -78,26 +72,22 @@ CREATE TABLE IF NOT EXISTS `cfadmin_roles` ( `update_at` int(1) unsigned NOT NULL COMMENT '修改时间', `active` tinyint(4) unsigned NOT NULL COMMENT '删除标志', PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - - -# Dump of table cfadmin_tokens -# ------------------------------------------------------------ +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户角色表'; +# ---------------------------- +# Table structure for cfadmin_tokens +# ---------------------------- CREATE TABLE IF NOT EXISTS `cfadmin_tokens` ( `uid` int(11) unsigned NOT NULL COMMENT '用户ID', `name` varchar(255) NOT NULL COMMENT '用户名称', `token` varchar(255) NOT NULL COMMENT '用户TOKEN', `create_at` int(11) unsigned NOT NULL COMMENT '登录时间', PRIMARY KEY (`uid`) -) ENGINE=MEMORY DEFAULT CHARSET=utf8; - - - -# Dump of table cfadmin_users -# ------------------------------------------------------------ +) ENGINE=MEMORY DEFAULT CHARSET=utf8mb4 COMMENT='用户Token表'; +# ---------------------------- +# Table structure for cfadmin_users +# ---------------------------- CREATE TABLE IF NOT EXISTS `cfadmin_users` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', `name` varchar(255) NOT NULL COMMENT '用户名', @@ -110,7 +100,7 @@ CREATE TABLE IF NOT EXISTS `cfadmin_users` ( `update_at` int(11) unsigned NOT NULL COMMENT '修改时间', `active` tinyint(4) unsigned NOT NULL COMMENT '删除标志', PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; From 378aba2cfb0131c032d5d897b1b2e890d20a49c1 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 29 Feb 2020 21:19:59 +0800 Subject: [PATCH 485/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Cookie.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lualib/httpd/Cookie.lua b/lualib/httpd/Cookie.lua index eefc5002..3fae7333 100644 --- a/lualib/httpd/Cookie.lua +++ b/lualib/httpd/Cookie.lua @@ -46,9 +46,9 @@ end -- 设置Cookie function Cookie.setCookie (name, value, expires, notall, https) - assert(type(name) == 'string' and key ~= '', '错误的Cookie Key, 请检查参数有效性') - assert(type(value) == 'string' or type(value) == 'number', '错误的Cookie Value, 请检查参数有效性') - assert(not expires or expires > os_time(), '错误的Cookie Expires, 请检查参数有效性') + assert(type(name) == 'string' and key ~= '', 'invalide Cookie Key') + assert(type(value) == 'string' or type(value) == 'number', 'invalide Cookie Value') + assert(not expires or expires > os_time(), 'invalide Cookie Expires') local co = co_self() local cs = Cookie.server[co] if not cs then @@ -60,7 +60,7 @@ function Cookie.setCookie (name, value, expires, notall, https) value = value, expires = expires, path = '/', - httponly = notall and 'HttpOnly', + httponly = notall and 'HttpOnly' or nil, secure = https and 'Secure' } end From 70cb33c7c057b9f2ce598d8c0f4aebdb1d36d6ef Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 29 Feb 2020 21:20:44 +0800 Subject: [PATCH 486/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_crypt.lua | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/script/test_crypt.lua b/script/test_crypt.lua index e691987b..458608a3 100644 --- a/script/test_crypt.lua +++ b/script/test_crypt.lua @@ -128,18 +128,22 @@ local function test_other( ... ) end -local function test_strxor( ... ) +local function test_xor_str( ... ) + + print("----------*** 开始测试 xor_str ***----------") local rawData = "admin00000" - local key = "1234567890" + local key = "1234567890-----" - print(crypt.xor_str(key, rawData, true)) + local xor = crypt.xor_str(rawData, key) - print(crypt.xor_str(key, crypt.xor_str(key, rawData))) - - -- Log:DEBUG("xor_str 原始数据: " .. rawData, "异或key: " .. key) - - -- assert(crypt.xor_str(rawData, crypt.xor_str(rawData, key)) == rawData) + local raw = crypt.xor_str(xor, key) + + assert(raw == rawData, "转换失败") + + Log:DEBUG( "xor data = " .. crypt.hexencode(xor), "raw data = " .. raw) + + print("----------*** xor_str 测试完成 ***----------\n") end @@ -271,9 +275,11 @@ local function test_rsa() local text = [[{"code":200,"data":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,26,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,]}]] local sign = crypt.sha128_with_rsa_sign(text, private_key_path, true) + -- print(sign) assert(crypt.sha128_with_rsa_verify(text, publick_key_path, sign, true), "sha128 with rsa签名/验证失败.") local sign = crypt.sha256_with_rsa_sign(text, private_key_path, true) + -- print(sign) assert(crypt.sha256_with_rsa_verify(text, publick_key_path, sign, true), "sha256 with rsa签名/验证失败.") end @@ -287,7 +293,7 @@ local function test_rsa() test_sha_with_rsa() - print("----------*** rsa public/private encode/decode 测试完成 ***----------") + print("----------*** rsa public/private encode/decode 测试完成 ***----------\n") end @@ -297,13 +303,15 @@ local function test_uuid( ... ) Log:DEBUG("生成的UUID为: " .. crypt.uuid()) - print("----------*** uuid 测试完成 ***----------") + print("----------*** uuid 测试完成 ***----------\n") end local function main() local examples = { + -- test_xor_str, + -- test_hex, -- test_crc, @@ -326,7 +334,7 @@ local function main() -- test_rsa, - test_uuid, + -- test_uuid, } From f7aaaa35478376c941c99e65c54d474dfb4796e8 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 29 Feb 2020 21:22:20 +0800 Subject: [PATCH 487/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/crypt/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index d3d8e110..8a5242ce 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -165,8 +165,8 @@ function crypt.hmac_sha512 (key, text, hex) return hash end -function crypt.xor_str (key, text, hex) - local hash = xor_str(key, text) +function crypt.xor_str (text, key, hex) + local hash = xor_str(text, key) if hash and hex then return hexencode(hash) end From 70c919daa2c77a359832724cfa7b8a14f315e9e8 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 29 Feb 2020 21:28:27 +0800 Subject: [PATCH 488/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E4=B8=8E=E5=AD=97=E7=AC=A6=E4=B8=B2=E7=9A=84=E5=86=85=E5=AD=98?= =?UTF-8?q?=E5=8D=A0=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 2 +- lualib/crypt/init.lua | 18 ------------------ lualib/logging/init.lua | 4 ++-- 3 files changed, 3 insertions(+), 21 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index a277640c..c1d1a928 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -809,7 +809,7 @@ luaopen_tcp(lua_State *L){ luaL_checkversion(L); /* 添加SSL支持 */ SSL_library_init(); - SSL_load_error_strings(); + // SSL_load_error_strings(); // ERR_load_crypto_strings(); // OpenSSL_add_ssl_algorithms(); // CRYPTO_set_mem_functions(xmalloc, xrealloc, xfree); diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index 8a5242ce..d5c1fa74 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -15,9 +15,7 @@ local sha384 = CRYPT.sha384 local sha512 = CRYPT.sha512 local hmac_sha1 = CRYPT.hmac_sha1 --- local hmac_sha224 = CRYPT.hmac_sha224 local hmac_sha256 = CRYPT.hmac_sha256 --- local hmac_sha384 = CRYPT.hmac_sha384 local hmac_sha512 = CRYPT.hmac_sha512 local crc32 = CRYPT.crc32 @@ -133,14 +131,6 @@ function crypt.hmac_sha1 (key, text, hex) return hash end --- function crypt.hmac_sha224 (key, text, hex) --- local hash = hmac_sha224(key, text) --- if hash and hex then --- return hexencode(hash) --- end --- return hash --- end - function crypt.hmac_sha256 (key, text, hex) local hash = hmac_sha256(key, text) if hash and hex then @@ -149,14 +139,6 @@ function crypt.hmac_sha256 (key, text, hex) return hash end --- function crypt.hmac_sha384 (key, text, hex) --- local hash = hmac_sha384(key, text) --- if hash and hex then --- return hexencode(hash) --- end --- return hash --- end - function crypt.hmac_sha512 (key, text, hex) local hash = hmac_sha512(key, text) if hash and hex then diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua index 9810038a..2624db8b 100644 --- a/lualib/logging/init.lua +++ b/lualib/logging/init.lua @@ -25,9 +25,9 @@ local fmt = string.format local concat = table.concat -- 可以在这里手动设置是否使用异步日志 -local SYNC = false +local ASYNC = true -if not SYNC then +if ASYNC then if io_type(io.output()) == 'file' then io.output():setvbuf("full", 2 ^ 20) cf.at(0.5, function () From fd951a057295a76cc4d3273c101fc4ec064a0a2b Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 1 Mar 2020 12:35:05 +0800 Subject: [PATCH 489/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0compress=E4=B8=8Eunco?= =?UTF-8?q?mpress=E6=96=B9=E6=B3=95,=E6=8F=90=E4=BE=9B=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=8E=8B=E7=BC=A9=E4=B8=8E=E8=A7=A3=E5=8E=8B.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lz/lzlib.c | 65 +++++++++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/luaclib/src/lz/lzlib.c b/luaclib/src/lz/lzlib.c index 86f166bc..b6be4e44 100644 --- a/luaclib/src/lz/lzlib.c +++ b/luaclib/src/lz/lzlib.c @@ -3,22 +3,60 @@ #include "../../../src/core.h" #include - -static inline -void stream_init(z_stream *z) { +static inline void stream_init(z_stream *z) { memset(z, 0x0, sizeof(*z)); z->zalloc = Z_NULL; z->zfree = Z_NULL; z->opaque = Z_NULL; } -// static int lcompress(lua_State *L) { -// return 1; -// } +static int lcompress(lua_State *L) { + size_t in_size = 0; + const uint8_t* in = (const uint8_t*)luaL_checklstring(L, 1, &in_size); + if (in_size <= 0) + return 0; + + size_t out_size = compressBound(in_size); + uint8_t *out = lua_newuserdata(L, out_size); + memset(out, 0x0, out_size); + + if (compress(out, &out_size, in, in_size) != Z_OK) + return 0; + + lua_pushlstring(L, (const char*)out, out_size); + lua_pushinteger(L, out_size); + return 2; +} + +static int luncompress(lua_State *L) { + size_t in_size = 0; + const uint8_t* in = (const uint8_t*)luaL_checklstring(L, 1, &in_size); + if (in_size <= 0) + return 0; + + size_t out_size = in_size; + size_t offset = 1; + size_t top = lua_gettop(L); -// static int luncompress(lua_State *L) { -// return 1; -// } + for(;;) { + uint8_t *out = lua_newuserdata(L, out_size); + memset(out, 0x0, out_size); + + int ret = uncompress(out, &out_size, in, in_size); + if (ret == Z_OK || ret == Z_BUF_ERROR) { + if (ret == Z_OK){ + lua_pushlstring(L, (const char *)out, out_size); + return 1; + } + lua_settop(L, top); + offset ++; + out_size = in_size << offset; + continue; + } + break; + } + return 0; +} static int lgzip_compress(lua_State *L) { size_t in_size = 0; @@ -100,12 +138,13 @@ static int lgzip_uncompress(lua_State *L) { return 1; } -LUAMOD_API int -luaopen_lz(lua_State *L){ +LUAMOD_API int luaopen_lz(lua_State *L){ luaL_checkversion(L); luaL_Reg zlib_libs[] = { - // {"compress", lcompress}, - // {"uncompress", luncompress}, + /* LZ77压缩/解压方法 */ + {"compress", lcompress}, + {"uncompress", luncompress}, + /* gzip压缩/解压方法 */ {"gzcompress", lgzip_compress}, {"gzuncompress", lgzip_uncompress}, {NULL, NULL} From 8ff2db17e5047ac1371d50239f8d96f734c4f0da Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 1 Mar 2020 12:35:48 +0800 Subject: [PATCH 490/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0test=5Fzlib.lua?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=96=87=E4=BB=B6,=20=E7=94=A8=E4=BA=8E?= =?UTF-8?q?=E6=BC=94=E7=A4=BA=E4=B8=8E=E6=B5=8B=E8=AF=95=E5=A6=82=E4=BD=95?= =?UTF-8?q?=E4=BD=BF=E7=94=A8zlib=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_zlib.lua | 60 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 script/test_zlib.lua diff --git a/script/test_zlib.lua b/script/test_zlib.lua new file mode 100644 index 00000000..9319b45b --- /dev/null +++ b/script/test_zlib.lua @@ -0,0 +1,60 @@ +local z = require "lz" + +local LOG = require "logging" + +local text = [[ + + Lua 轻量级网络开发框架(A lua Lightweight Network Development Framework) + + 生态多 —— 集成社区库最多的框架之一, 并自行实现了一些网络协议生态. + + Multi-ecology —— Integrate most community third-party libraries and implement many protocols on their own. + + 稳定性好 —— 许多领域的企业已经开始使用,并且用户数量也在逐渐增加. + + Good stability —— Enterprises in many fields have begun to use, and users are gradually increasing. + + 高效率 —— 高效的静态语言与高效的虚拟机实现优秀的运行时框架. + + High efficiency - Efficient static language and efficient virtual machine to achieve excellent runtime framework. + + 高可维护性 —— 通俗易懂的框架编写方式可以让开发者快速适应并且上手. + + High readability —— The easy-to-understand framework writing method allows developers to quickly adapt and get started. + + 《运行》 + + cf框架在整体构建完毕后会在项目根目录产生一个可执行文件: cfadmin, 它会根据当前目录环境执行对应的入口文件(script/main.lua). + + 《命令与参数》 + + ./cfadmin, 前台执行; 使用ctrl + z、ctrl + c、ctrl + \等组合键就能让它停止执行. + + ./cfadmin -d, 后台执行; 通常你需要使用killall cfadmin与kill -9 PID这样的命令才能终止它. + + 《选择合适的运行方式》 + + cf默认情况下会将日志输出在stdout. 在开发与测试期间通常会前台运行, 这样无论是打印日志还是定位问题都会变得较为简单. + + 如果您将cf放置在容器内部, 前台运行通常会是一个比较好的选择. 有利于贴合容器日志生态, 合理配合日志收集器做集中式日志检索与管理. + + 如果您将cf放置到原生系统下, 建议将cf至于后台运行. + +]] + +local cp_text = z.compress(text) + +local raw_text = z.uncompress(cp_text) + +assert(raw_text == text, "测试LZ77压缩/解压方法失败") + +LOG:DEBUG("压缩前的文本长度为:" .. #raw_text, "压缩后的文本长度为:" .. #cp_text) + + +local cp_text = z.gzcompress(text) + +local raw_text = z.gzuncompress(cp_text) + +assert(raw_text == text, "测试gzip压缩/解压失败") + +LOG:DEBUG("压缩前的文本长度为:" .. #raw_text, "压缩后的文本长度为:" .. #cp_text) \ No newline at end of file From 58d35985f79e918fe183387bef9ef45b1d70222b Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 2 Mar 2020 00:53:03 +0800 Subject: [PATCH 491/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2=E5=AE=89=E5=85=A8=E8=BD=AC=E4=B9=89=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/DB/init.lua | 5 +++++ lualib/protocol/mysql.lua | 17 +++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index fb15874b..939c123a 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -183,6 +183,11 @@ function DB:query(query) return ret, err end +-- 字符串安全转义 +function DB.quote_to_str( str ) + return mysql.quote_to_str(str) +end + function DB:count() return self.current, self.max, #self.co_pool, #self.db_pool end diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua index 28dff364..3ebb9305 100644 --- a/lualib/protocol/mysql.lua +++ b/lualib/protocol/mysql.lua @@ -665,6 +665,23 @@ function MySQL.query(self, query, est_nrows) -- return self:read_result(est_nrows) end +local escape_map = { + ['\0'] = "\\0", + ['\b'] = "\\b", + ['\n'] = "\\n", + ['\r'] = "\\r", + ['\t'] = "\\t", + ['\26'] = "\\Z", + ['\\'] = "\\\\", + ["'"] = "\\'", + ['"'] = '\\"', +} + +-- 转义 +function MySQL.quote_sql_str( str ) + return strformat("%s", strgsub(str, "[\0\b\n\r\t\26\\\'\"]", escape_map)) +end + function MySQL.set_compact_arrays(self, value) self.compact = value end From fc262acb47ec391eaea57a1e9739523f8949a46f Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 3 Mar 2020 18:19:25 +0800 Subject: [PATCH 492/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0xml2lua=E7=89=88?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/xml2lua/XmlParser.lua | 221 +++++++++++++---------------- lualib/xml2lua/xml2lua.lua | 114 ++++++++++++--- lualib/xml2lua/xmlhandler/dom.lua | 78 ++++++---- lualib/xml2lua/xmlhandler/tree.lua | 4 +- 4 files changed, 239 insertions(+), 178 deletions(-) diff --git a/lualib/xml2lua/XmlParser.lua b/lualib/xml2lua/XmlParser.lua index a9823ea7..a47ae52a 100644 --- a/lualib/xml2lua/XmlParser.lua +++ b/lualib/xml2lua/XmlParser.lua @@ -1,33 +1,18 @@ -local type = type -local error = error -local pairs = pairs -local ipairs = ipairs -local tonumber = tonumber -local setmetatable = setmetatable -local getmetatable = getmetatable - -local strfind = string.find -local strgsub = string.gsub -local strsub = string.sub -local strchar = string.char - -local table_remove = table.remove -local table_concat = table.concat --- @module Class providing the actual XML parser. -- Available options are: --- * stripWS --- Strip non-significant whitespace (leading/trailing) +-- * stripWS +-- Strip non-significant whitespace (leading/trailing) -- and do not generate events for empty text elements --- --- * expandEntities --- Expand entities (standard entities + single char --- numeric entities only currently - could be extended +-- +-- * expandEntities +-- Expand entities (standard entities + single char +-- numeric entities only currently - could be extended -- at runtime if suitable DTD parser added elements -- to table (see obj._ENTITIES). May also be possible -- to expand multibyre entities for UTF-8 only --- +-- -- * errorHandler --- Custom error handler function +-- Custom error handler function -- -- NOTE: Boolean options must be set to 'nil' not '0' @@ -38,9 +23,9 @@ local table_concat = table.concat local function decimalToHtmlChar(code) local n = tonumber(code) if n >= 0 and n < 256 then - return strchar(n) + return string.char(n) else - return table_concat({"&#", code, ";"}) + return "&#"..code..";" end end @@ -51,9 +36,9 @@ end local function hexadecimalToHtmlChar(code) local n = tonumber(code, 16) if n >= 0 and n < 256 then - return strchar(n) + return string.char(n) else - return table_concat({"&#x", code, ";"}) + return "&#x"..code..";" end end @@ -71,7 +56,8 @@ local XmlParser = { _WS = '^%s*$', _DTD1 = '', _DTD2 = '', - _DTD3 = '', + --_DTD3 = '', + _DTD3 = '', _DTD4 = '', _DTD5 = '', @@ -82,7 +68,7 @@ local XmlParser = { --Matches a closing tag such as or the end of a openning tag such as _TAGEXT = '(%/?)>', - _errstr = { + _errstr = { xmlErr = "Error Parsing XML", declErr = "Error Parsing XMLDecl", declStartErr = "XMLDecl not at start of document", @@ -96,7 +82,7 @@ local XmlParser = { incompleteXmlErr = "Incomplete XML Document", }, - _ENTITIES = { + _ENTITIES = { ["<"] = "<", [">"] = ">", ["&"] = "&", @@ -135,10 +121,10 @@ local function fexists(table, elementName) return false end - if table[elementName] ~= nil then - return true - else + if table[elementName] == nil then return fexists(getmetatable(table), elementName) + else + return true end end @@ -151,16 +137,16 @@ end --- Removes whitespaces local function stripWS(self, s) if self.options.stripWS then - s = strgsub(s,'^%s+','') - s = strgsub(s,'%s+$','') + s = string.gsub(s,'^%s+','') + s = string.gsub(s,'%s+$','') end return s end -local function parseEntities(self, s) +local function parseEntities(self, s) if self.options.expandEntities then for k,v in pairs(self._ENTITIES) do - s = strgsub(s,k,v) + s = string.gsub(s,k,v) end end @@ -170,25 +156,25 @@ end --- Parses a string representing a tag. --@param s String containing tag text --@return a {name, attrs} table --- where name is the name of the tag and attrs +-- where name is the name of the tag and attrs -- is a table containing the atributtes of the tag local function parseTag(self, s) local tag = { - name = strgsub(s, self._TAG, '%1'), + name = string.gsub(s, self._TAG, '%1'), attrs = {} - } + } - local parseFunction = function (k, v) + local parseFunction = function (k, v) tag.attrs[k] = parseEntities(self, v) - tag.attrs._ = 1 + tag.attrs._ = 1 end - - strgsub(s, self._ATTR1, parseFunction) - strgsub(s, self._ATTR2, parseFunction) + + string.gsub(s, self._ATTR1, parseFunction) + string.gsub(s, self._ATTR2, parseFunction) if tag.attrs._ then tag.attrs._ = nil - else + else tag.attrs = nil end @@ -197,26 +183,26 @@ end local function parseXmlDeclaration(self, xml, f) -- XML Declaration - f.match, f.endMatch, f.text = strfind(xml, self._PI, f.pos) - if not f.match then + f.match, f.endMatch, f.text = string.find(xml, self._PI, f.pos) + if not f.match then err(self, self._errstr.declErr, f.pos) - end + end if f.match ~= 1 then -- Must be at start of doc if present err(self, self._errstr.declStartErr, f.pos) end - local tag = parseTag(self, f.text) + local tag = parseTag(self, f.text) -- TODO: Check if attributes are valid -- Check for version (mandatory) if tag.attrs and tag.attrs.version == nil then err(self, self._errstr.declAttrErr, f.pos) end - if fexists(self.handler, 'decl') then + if fexists(self.handler, 'decl') then self.handler:decl(tag, f.match, f.endMatch) - end + end return tag end @@ -225,14 +211,14 @@ local function parseXmlProcessingInstruction(self, xml, f) local tag = {} -- XML Processing Instruction (PI) - f.match, f.endMatch, f.text = strfind(xml, self._PI, f.pos) - if not f.match then + f.match, f.endMatch, f.text = string.find(xml, self._PI, f.pos) + if not f.match then err(self, self._errstr.piErr, f.pos) - end - if fexists(self.handler, 'pi') then + end + if fexists(self.handler, 'pi') then -- Parse PI attributes & text - tag = parseTag(self, f.text) - local pi = strsub(f.text, #tag.name + 1) + tag = parseTag(self, f.text) + local pi = string.sub(f.text, string.len(tag.name)+1) if pi ~= "" then if tag.attrs then tag.attrs._text = pi @@ -240,19 +226,19 @@ local function parseXmlProcessingInstruction(self, xml, f) tag.attrs = { _text = pi } end end - self.handler:pi(tag, f.match, f.endMatch) + self.handler:pi(tag, f.match, f.endMatch) end return tag end local function parseComment(self, xml, f) - f.match, f.endMatch, f.text = strfind(xml, self._COMMENT, f.pos) - if not f.match then + f.match, f.endMatch, f.text = string.find(xml, self._COMMENT, f.pos) + if not f.match then err(self, self._errstr.commentErr, f.pos) - end + end - if fexists(self.handler, 'comment') then + if fexists(self.handler, 'comment') then f.text = parseEntities(self, stripWS(self, f.text)) self.handler:comment(f.text, next, f.match, f.endMatch) end @@ -260,86 +246,69 @@ end local function _parseDtd(self, xml, pos) -- match,endMatch,root,type,name,uri,internal - local m,e,r,t,n,u,i + local dtdPatterns = {self._DTD1, self._DTD2, self._DTD3, self._DTD4, self._DTD5} - m,e,r,t,u,i = strfind(xml, self._DTD1,pos) - if m then - return m, e, {_root=r,_type=t,_uri=u,_internal=i} - end - - m,e,r,t,n,u,i = strfind(xml, self._DTD2,pos) - if m then - return m, e, {_root=r,_type=t,_name=n,_uri=u,_internal=i} - end - - m,e,r,i = strfind(xml, self._DTD3,pos) - if m then - return m, e, {_root=r,_internal=i} - end - - m,e,r,t,u = strfind(s,self._DTD4,pos) - if m then - return m,e,{_root=r,_type=t,_uri=u} - end - - m,e,r,t,n,u = strfind(s,self._DTD5,pos) - if m then - return m,e,{_root=r,_type=t,_name=n,_uri=u} + for i, dtd in pairs(dtdPatterns) do + local m,e,r,t,n,u,i = string.find(xml, dtd, pos) + if m then + return m, e, {_root=r, _type=t, _name=n, _uri=u, _internal=i} + end end return nil end local function parseDtd(self, xml, f) - f.match, f.endMatch, attrs = self:_parseDtd(xml, f.pos) - if not f.match then + f.match, f.endMatch, attrs = _parseDtd(self, xml, f.pos) + if not f.match then err(self, self._errstr.dtdErr, f.pos) - end + end if fexists(self.handler, 'dtd') then - self.handler:dtd(attrs._root, attrs, f.match, f.endMatch) + local tag = {name="DOCTYPE", value=string.sub(xml, f.match+10, f.endMatch-1)} + self.handler:dtd(tag, f.match, f.endMatch) end end local function parseCdata(self, xml, f) - f.match, f.endMatch, f.text = strfind(xml, self._CDATA, f.pos) - if not f.match then + f.match, f.endMatch, f.text = string.find(xml, self._CDATA, f.pos) + if not f.match then err(self, self._errstr.cdataErr, f.pos) - end + end if fexists(self.handler, 'cdata') then self.handler:cdata(f.text, nil, f.match, f.endMatch) - end + end end --- Parse a Normal tag -- Need check for embedded '>' in attribute value and extend --- match recursively if necessary eg. +-- match recursively if necessary eg. local function parseNormalTag(self, xml, f) --Check for errors while 1 do --If there isn't an attribute without closing quotes (single or double quotes) --then breaks to follow the normal processing of the tag. --Otherwise, try to find where the quotes close. - f.errStart, f.errEnd = strfind(f.tagstr, self._ATTRERR1) + f.errStart, f.errEnd = string.find(f.tagstr, self._ATTRERR1) if f.errEnd == nil then - f.errStart, f.errEnd = strfind(f.tagstr, self._ATTRERR2) + f.errStart, f.errEnd = string.find(f.tagstr, self._ATTRERR2) if f.errEnd == nil then break end end - - f.extStart, f.extEnd, f.endt2 = strfind(xml, self._TAGEXT, f.endMatch+1) - f.tagstr = f.tagstr .. strsub(xml, f.endMatch, f.extEnd-1) - if not f.match then + + f.extStart, f.extEnd, f.endt2 = string.find(xml, self._TAGEXT, f.endMatch+1) + f.tagstr = f.tagstr .. string.sub(xml, f.endMatch, f.extEnd-1) + if not f.match then err(self, self._errstr.xmlErr, f.pos) - end + end f.endMatch = f.extEnd - end + end -- Extract tag name and attrs - local tag = parseTag(self, f.tagstr) + local tag = parseTag(self, f.tagstr) if (f.endt1=="/") then if fexists(self.handler, 'endtag') then @@ -347,13 +316,13 @@ local function parseNormalTag(self, xml, f) -- Shouldn't have any attributes in endtag err(self, string.format("%s (/%s)", self._errstr.endTagErr, tag.name), f.pos) end - if table_remove(self._stack) ~= tag.name then + if table.remove(self._stack) ~= tag.name then err(self, string.format("%s (/%s)", self._errstr.unmatchedTagErr, tag.name), f.pos) end self.handler:endtag(tag, f.match, f.endMatch) end else - self._stack[#self._stack+1] = tag.name + table.insert(self._stack, tag.name) if fexists(self.handler, 'starttag') then self.handler:starttag(tag, f.match, f.endMatch) end @@ -364,7 +333,7 @@ local function parseNormalTag(self, xml, f) -- Self-Closing Tag if (f.endt2=="/") then - table_remove(self._stack) + table.remove(self._stack) if fexists(self.handler, 'endtag') then self.handler:endtag(tag, f.match, f.endMatch) end @@ -376,15 +345,15 @@ end local function parseTagType(self, xml, f) -- Test for tag type - if strfind(strsub(f.tagstr, 1, 5), "?xml%s") then + if string.find(string.sub(f.tagstr, 1, 5), "?xml%s") then parseXmlDeclaration(self, xml, f) - elseif strsub(f.tagstr, 1, 1) == "?" then + elseif string.sub(f.tagstr, 1, 1) == "?" then parseXmlProcessingInstruction(self, xml, f) - elseif strsub(f.tagstr, 1, 3) == "!--" then + elseif string.sub(f.tagstr, 1, 3) == "!--" then parseComment(self, xml, f) - elseif strsub(f.tagstr, 1, 8) == "!DOCTYPE" then + elseif string.sub(f.tagstr, 1, 8) == "!DOCTYPE" then parseDtd(self, xml, f) - elseif strsub(f.tagstr, 1, 8) == "![CDATA[" then + elseif string.sub(f.tagstr, 1, 8) == "![CDATA[" then parseCdata(self, xml, f) else parseNormalTag(self, xml, f) @@ -394,31 +363,31 @@ end --- Get next tag (first pass - fix exceptions below). --@return true if the next tag could be got, false otherwise local function getNextTag(self, xml, f) - f.match, f.endMatch, f.text, f.endt1, f.tagstr, f.endt2 = strfind(xml, self._XML, f.pos) - if not f.match then - if strfind(xml, self._WS, f.pos) then + f.match, f.endMatch, f.text, f.endt1, f.tagstr, f.endt2 = string.find(xml, self._XML, f.pos) + if not f.match then + if string.find(xml, self._WS, f.pos) then -- No more text - check document complete if #self._stack ~= 0 then err(self, self._errstr.incompleteXmlErr, f.pos) else - return false + return false end else -- Unparsable text err(self, self._errstr.xmlErr, f.pos) end - end + end f.text = f.text or '' f.tagstr = f.tagstr or '' f.match = f.match or 0 - + return f.endMatch ~= nil end --Main function which starts the XML parsing process --@param xml the XML string to parse ---@param parseAttributes indicates if tag attributes should be parsed or not. +--@param parseAttributes indicates if tag attributes should be parsed or not. -- If omitted, the default value is true. function XmlParser:parse(xml, parseAttributes) if type(self) ~= "table" or getmetatable(self) ~= XmlParser then @@ -431,15 +400,15 @@ function XmlParser:parse(xml, parseAttributes) self.handler.parseAttributes = parseAttributes - --Stores strfind results and parameters + --Stores string.find results and parameters --and other auxiliar variables local f = { - --strfind return + --string.find return match = 0, endMatch = 0, -- text, end1, tagstr, end2, - --strfind parameters and auxiliar variables + --string.find parameters and auxiliar variables pos = 1, -- startText, endText, -- errStart, errEnd, extStart, extEnd, @@ -449,11 +418,11 @@ function XmlParser:parse(xml, parseAttributes) if not getNextTag(self, xml, f) then break end - + -- Handle leading text f.startText = f.match - f.endText = f.match + #f.text - 1 - f.match = f.match + #f.text + f.endText = f.match + string.len(f.text) - 1 + f.match = f.match + string.len(f.text) f.text = parseEntities(self, stripWS(self, f.text)) if f.text ~= "" and fexists(self.handler, 'text') then self.handler:text(f.text, nil, f.match, f.endText) diff --git a/lualib/xml2lua/xml2lua.lua b/lualib/xml2lua/xml2lua.lua index 3caefe12..dc44db8f 100755 --- a/lualib/xml2lua/xml2lua.lua +++ b/lualib/xml2lua/xml2lua.lua @@ -1,8 +1,8 @@ ---- @module Module providing a non-validating XML stream parser in Lua. --- +--- @module Module providing a non-validating XML stream parser in Lua. +-- -- Features: -- ========= --- +-- -- * Tokenises well-formed XML (relatively robustly) -- * Flexible handler based event API (see below) -- * Parses all XML Infoset elements - ie. @@ -13,29 +13,29 @@ -- - XML Decl -- - Processing Instructions -- - DOCTYPE declarations --- * Provides limited well-formedness checking +-- * Provides limited well-formedness checking -- (checks for basic syntax & balanced tags only) -- * Flexible whitespace handling (selectable) -- * Entity Handling (selectable) --- +-- -- Limitations: -- ============ --- +-- -- * Non-validating --- * No charset handling --- * No namespace support +-- * No charset handling +-- * No namespace support -- * Shallow well-formedness checking only (fails -- to detect most semantic errors) --- +-- -- API: -- ==== -- --- The parser provides a partially object-oriented API with +-- The parser provides a partially object-oriented API with -- functionality split into tokeniser and handler components. --- +-- -- The handler instance is passed to the tokeniser and receives -- callbacks for each XML element processed (if a suitable handler --- function is defined). The API is conceptually similar to the +-- function is defined). The API is conceptually similar to the -- SAX API but implemented differently. -- -- XML data is passed to the parser instance through the 'parse' @@ -59,7 +59,7 @@ local function printableInternal(tb, level) if tb == nil then return end - + level = level or 1 local spaces = string.rep(' ', level*2) for k,v in pairs(tb) do @@ -69,7 +69,7 @@ local function printableInternal(tb, level) else print(spaces .. k..'='..v) end - end + end end ---Instantiates a XmlParser object to parse a XML string @@ -79,16 +79,16 @@ end -- local handler = require("xmlhandler/tree"). --@return a XmlParser object used to parse the XML --@see XmlParser -function xml2lua.parser(handler) +function xml2lua.parser(handler) if handler == xml2lua then error("You must call xml2lua.parse(handler) instead of xml2lua:parse(handler)") end - local options = { + local options = { --Indicates if whitespaces should be striped or not - stripWS = 1, + stripWS = 1, expandEntities = 1, - errorHandler = function(errMsg, pos) + errorHandler = function(errMsg, pos) error(string.format("%s [char=%d]\n", errMsg or "Parse Error", pos)) end } @@ -114,10 +114,10 @@ function xml2lua.toString(t) end for k,v in pairs(t) do - if type(v) == 'table' then + if type(v) == 'table' then v = xml2lua.toString(v) end - res = res .. sep .. string.format("%s=%s", k, v) + res = res .. sep .. string.format("%s=%s", k, v) sep = ',' end res = '{'..res..'}' @@ -134,8 +134,80 @@ function xml2lua.loadFile(xmlFilePath) --Gets the entire file content and stores into a string return f:read("*a") end - + error(e) end +---Gets an _attr element from a table that represents the attributes of an XML tag, +--and generates a XML String representing the attibutes to be inserted +--into the openning tag of the XML +-- +--@param attrTable table from where the _attr field will be got +--@return a XML String representation of the tag attributes +local function attrToXml(attrTable) + local s = "" + local attrTable = attrTable or {} + + for k, v in pairs(attrTable) do + s = s .. " " .. k .. "=" .. '"' .. v .. '"' + end + return s +end + +---Gets the first key of a given table +local function getFirstKey(tb) + if type(tb) == "table" then + for k, v in pairs(tb) do + return k + end + return nil + end + + return tb +end + +---Converts a Lua table to a XML String representation. +--@param tb Table to be converted to XML +--@param tableName Name of the table variable given to this function, +-- to be used as the root tag. +--@param level Only used internally, when the function is called recursively to print indentation +-- +--@return a String representing the table content in XML +function xml2lua.toXml(tb, tableName, level) + local level = level or 0 + local firstLevel = level + local spaces = string.rep(' ', level*2) + local xmltb = level == 0 and {'<'..tableName..'>'} or {} + + for k, v in pairs(tb) do + if type(v) == "table" then + --If the keys of the table are a number, it represents an array + if type(k) == "number" then + local attrs = attrToXml(v._attr) + v._attr = nil + table.insert(xmltb, + spaces..'<'..tableName..attrs..'>\n'..xml2lua.toXml(v, tableName, level+1).. + '\n'..spaces..'') + else + level = level + 1 + if type(getFirstKey(v)) == "number" then + table.insert(xmltb, spaces..xml2lua.toXml(v, k, level)) + else + table.insert( + xmltb, + spaces..'<'..k..'>\n'.. xml2lua.toXml(v, level+1).. + '\n'..spaces..'') + end + end + else + table.insert(xmltb, spaces..'<'..k..'>'..tostring(v)..'') + end + end + + if firstLevel == 0 then + table.insert(xmltb, '\n') + end + return table.concat(xmltb, "\n") +end + return xml2lua diff --git a/lualib/xml2lua/xmlhandler/dom.lua b/lualib/xml2lua/xmlhandler/dom.lua index b0bc56b8..cd6bbd67 100755 --- a/lualib/xml2lua/xmlhandler/dom.lua +++ b/lualib/xml2lua/xmlhandler/dom.lua @@ -1,7 +1,15 @@ ----- @module Handler to generate a DOM-like node tree structure with --- a single ROOT node parent - each node is a table comprising +local function init() + return { + options = {commentNode=1, piNode=1, dtdNode=1, declNode=1}, + current = { _children = {n=0}, _type = "ROOT" }, + _stack = {} + } +end + +--- @module Handler to generate a DOM-like node tree structure with +-- a single ROOT node parent - each node is a table comprising -- the fields below. --- +-- -- node = { _name = , -- _type = ROOT|ELEMENT|TEXT|COMMENT|PI|DECL|DTD, -- _attr = { Node attributes - see callback API }, @@ -16,7 +24,7 @@ -- -- Options -- ======= --- options.(comment|pi|dtd|decl)Node = bool +-- options.(comment|pi|dtd|decl)Node = bool -- - Include/exclude given node types -- -- License: @@ -26,23 +34,33 @@ -- --@author Paul Chakravarti (paulc@passtheaardvark.com) --@author Manoel Campos da Silva Filho -local dom = { - options = {commentNode=1, piNode=1, dtdNode=1, declNode=1}, - current = { _children = {n=0}, _type = "ROOT" }, - _stack = {} -} +local dom = init() + +---Instantiates a new handler object. +--Each instance can handle a single XML. +--By using such a constructor, you can parse +--multiple XML files in the same application. +--@return the handler instance +function dom:new() + local obj = init() + + obj.__index = self + setmetatable(obj, self) + + return obj +end ---Parses a start tag. -- @param tag a {name, attrs} table --- where name is the name of the tag and attrs +-- where name is the name of the tag and attrs -- is a table containing the atributtes of the tag function dom:starttag(tag) - local node = { _type = 'ELEMENT', - _name = tag.name, - _attr = tag.attrs, - _children = {n=0} + local node = { _type = 'ELEMENT', + _name = tag.name, + _attr = tag.attrs, + _children = {n=0} } - + if self.root == nil then self.root = node end @@ -55,7 +73,7 @@ end ---Parses an end tag. -- @param tag a {name, attrs} table --- where name is the name of the tag and attrs +-- where name is the name of the tag and attrs -- is a table containing the atributtes of the tag function dom:endtag(tag, s) --Table representing the containing tag of the current tag @@ -66,12 +84,13 @@ function dom:endtag(tag, s) end table.remove(self._stack) + self.current = self._stack[#self._stack] end ---Parses a tag content. -- @param text text to process function dom:text(text) - local node = { _type = "TEXT", + local node = { _type = "TEXT", _text = text } table.insert(self.current._children, node) @@ -81,8 +100,8 @@ end -- @param text comment text function dom:comment(text) if self.options.commentNode then - local node = { _type = "COMMENT", - _text = text + local node = { _type = "COMMENT", + _text = text } table.insert(self.current._children, node) end @@ -90,27 +109,27 @@ end --- Parses a XML processing instruction (PI) tag -- @param tag a {name, attrs} table --- where name is the name of the tag and attrs +-- where name is the name of the tag and attrs -- is a table containing the atributtes of the tag function dom:pi(tag) if self.options.piNode then - local node = { _type = "PI", + local node = { _type = "PI", _name = tag.name, - _attr = tag.attrs, - } + _attr = tag.attrs, + } table.insert(self.current._children, node) end end ---Parse the XML declaration line (the line that indicates the XML version). -- @param tag a {name, attrs} table --- where name is the name of the tag and attrs +-- where name is the name of the tag and attrs -- is a table containing the atributtes of the tag function dom:decl(tag) if self.options.declNode then - local node = { _type = "DECL", + local node = { _type = "DECL", _name = tag.name, - _attr = tag.attrs, + _attr = tag.attrs, } table.insert(self.current._children, node) end @@ -118,13 +137,13 @@ end ---Parses a DTD tag. -- @param tag a {name, attrs} table --- where name is the name of the tag and attrs +-- where name is the name of the tag and attrs -- is a table containing the atributtes of the tag function dom:dtd(tag) if self.options.dtdNode then - local node = { _type = "DTD", + local node = { _type = "DTD", _name = tag.name, - _attr = tag.attrs, + _attr = tag.attrs, } table.insert(self.current._children, node) end @@ -132,4 +151,5 @@ end ---Parses CDATA tag content. dom.cdata = dom.text +dom.__index = dom return dom diff --git a/lualib/xml2lua/xmlhandler/tree.lua b/lualib/xml2lua/xmlhandler/tree.lua index 14e29e98..edb66524 100755 --- a/lualib/xml2lua/xmlhandler/tree.lua +++ b/lualib/xml2lua/xmlhandler/tree.lua @@ -148,9 +148,9 @@ end ---Parses a tag content. -- @param t text to process -function tree:text(t) +function tree:text(text) local current = self._stack[#self._stack] - table.insert(current, t) + table.insert(current, text) end ---Parses CDATA tag content. From 9a5b6789b88fe5fca991daedc107d0b3db73ab0a Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 3 Mar 2020 20:18:57 +0800 Subject: [PATCH 493/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0httpc.raw=E6=96=B9?= =?UTF-8?q?=E6=B3=95,=20=E7=94=A8=E4=BA=8E=E6=9E=84=E9=80=A0=E7=89=B9?= =?UTF-8?q?=E6=AE=8A=E7=9A=84=E8=AF=B7=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/class.lua | 77 +++++++++++++++++++++++++++++++++++++++ lualib/httpc/init.lua | 51 ++++++++++++++++++++++++++ lualib/httpc/protocol.lua | 58 +++++++++++++++++++++++++++-- 3 files changed, 182 insertions(+), 4 deletions(-) diff --git a/lualib/httpc/class.lua b/lualib/httpc/class.lua index 8e6f06fa..db60884b 100644 --- a/lualib/httpc/class.lua +++ b/lualib/httpc/class.lua @@ -10,6 +10,7 @@ local sock_send = protocol.sock_send local sock_connect = protocol.sock_connect local httpc_response = protocol.httpc_response local splite_protocol = protocol.splite_protocol +local build_raw_req = protocol.build_raw_req local build_get_req = protocol.build_get_req local build_post_req = protocol.build_post_req local build_json_req = protocol.build_json_req @@ -58,6 +59,82 @@ function httpc:basic_authorization( ... ) return build_basic_authorization(...) end +function httpc:raw( parameter ) + local opt, err = splite_protocol(parameter.domain) + if not opt then + return nil, err + end + + if self.domain and self.domain ~= opt.domain then + return nil, "1. 不同的域名不可使用httpc对象来请求" + end + + if self.port and self.port ~= opt.port then + return nil, "2. 不同的域名不可使用httpc对象来请求" + end + + local method = type(parameter.method) == 'string' and upper(parameter.method) or nil + assert( method and ( + method == 'GET' or + method == 'POST' or + method == 'DELETE' or + method == 'PUT' + ),"invalide http method.") + + -- GET方法禁止传递body + if method == "GET" then + parameter.body = nil + end + + -- POST/PUT方法禁止传递args + if method == "POST" or method == "PUT" or method == "DELETE" then + parameter.args = nil + end + + opt.method = method + opt.body = parameter.body + opt.args = parameter.args + opt.headers = parameter.headers + + local REQ = build_raw_req(opt) + + if not self.sock then + local sock = sock_new():timeout(self.timeout) + local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) + if not ok then + sock:close() + return ok, err + end + self.sock = sock + end + + local ok, err = sock_send(self.sock, opt.protocol, REQ) + if not ok then + self.sock:close() + self.sock = nil + local sock = sock_new():timeout(self.timeout) + local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) + if not ok then + sock:close() + return ok, err + end + ok, err = sock_send(sock, opt.protocol, REQ) + if not ok then + sock:close() + return nil, err + end + self.sock = sock + end + + local code, msg = httpc_response(self.sock, opt.protocol) + if not code then + self.sock:close() + self.sock = nil + end + return code, msg + +end + -- get 请求 function httpc:get (domain, headers, args, timeout) local opt, err = splite_protocol(domain) diff --git a/lualib/httpc/init.lua b/lualib/httpc/init.lua index 23849dd4..db51fcba 100644 --- a/lualib/httpc/init.lua +++ b/lualib/httpc/init.lua @@ -15,6 +15,7 @@ local sock_send = protocol.sock_send local sock_connect = protocol.sock_connect local httpc_response = protocol.httpc_response local splite_protocol = protocol.splite_protocol +local build_raw_req = protocol.build_raw_req local build_get_req = protocol.build_get_req local build_post_req = protocol.build_post_req local build_json_req = protocol.build_json_req @@ -36,6 +37,7 @@ local split = string.sub local splite = string.gmatch local spliter = string.gsub local lower = string.lower +local upper = string.upper local insert = table.insert local concat = table.concat local toint = math.tointeger @@ -50,6 +52,54 @@ local __TIMEOUT__ = 15 local methods = {'get', 'post', 'json', 'file'} +local function raw( parameter ) + local opt, err = splite_protocol(parameter.domain) + if not opt then + return nil, err + end + + local method = type(parameter.method) == 'string' and upper(parameter.method) or nil + assert( method and ( + method == 'GET' or + method == 'POST' or + method == 'OPTIONS' or + method == 'DELETE' or + method == 'PUT' + ),"invalide http method.") + + -- GET方法禁止传递body + if parameter.method == "GET" then + parameter.body = nil + end + + -- POST/PUT方法禁止传递args + if parameter.method == "POST" or parameter.method == "PUT" or parameter.method == "DELETE" then + parameter.args = nil + end + + opt.method = method + opt.body = parameter.body + opt.args = parameter.args + opt.headers = parameter.headers + + local REQ = build_raw_req(opt) + + local sock = sock_new():timeout(timeout or __TIMEOUT__) + local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) + if not ok then + sock:close() + return ok, err + end + local ok, err = sock_send(sock, opt.protocol, REQ) + if not ok then + sock:close() + return ok, err + end + local code, msg = httpc_response(sock, opt.protocol) + sock:close() + return code, msg +end + -- HTTP GET local function get(domain, headers, args, timeout) local opt, err = splite_protocol(domain) @@ -277,6 +327,7 @@ end return { + raw = raw, get = get, post = post, delete = delete, diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua index abf941b2..5f41b982 100644 --- a/lualib/httpc/protocol.lua +++ b/lualib/httpc/protocol.lua @@ -98,16 +98,16 @@ end local function splite_protocol(domain) if type(domain) ~= 'string' then - return nil, '1. 非法的域名' + return nil, '1. invalide domain' end local protocol, domain_port, path = match(domain, '^(http[s]?)://([^/]+)(.*)') if not protocol or not domain_port or not path then - return nil, '2. 错误的url' + return nil, '2. invalide url' end if not path or path == '' then - return nil, "3. http无path需要以'/'结尾." + return nil, "3. http path need '/' in end." end local domain, port @@ -119,7 +119,7 @@ local function splite_protocol(domain) domain, port = match(domain_port, '([^:]+):(%d*)') end if not domain then - return nil, "4. 无效或者非法的主机名: "..domain_port + return nil, "4. invalide host or port: "..domain_port end port = toint(port) if not port then @@ -238,6 +238,55 @@ local function httpc_response(sock, SSL) end end +-- 对一些特殊请求的支持 +local function build_raw_req( opt ) + local request = new_tab(16, 0) + insert(request, fmt("%s %s HTTP/1.1", opt.method, opt.path)) + insert(request, fmt("Host: %s", (opt.port == 80 or opt.port == 443) and opt.domain or opt.domain..':'..opt.port)) + insert(request, fmt("User-Agent: %s", opt.server)) + insert(request, 'Accept: */*') + insert(request, 'Accept-Encoding: gzip, identity') + insert(request, 'Connection: keep-alive') + + if opt.method == 'GET' and type(opt.args) == "table" then + local args = new_tab(8, 0) + for _, arg in ipairs(opt.args) do + assert(#arg == 2, "args need key[1]->value[2] (2 values and must be string)") + insert(args, url_encode(arg[1])..'='..url_encode(arg[2])) + end + request[1] = fmt("GET %s HTTP/1.1", opt.path .. '?' .. concat(args, "&")) + end + + if type(opt.headers) == "table" then + for _, header in ipairs(opt.headers) do + assert(#header == 2, "HEADER need key[1]->value[2] (2 values)") + insert(request, header[1]..': '..header[2]) + end + end + + if opt.method == 'POST' or opt.method == 'PUT' or opt.method == 'DELETE' then + + if type(opt.body) == "table" then + local body = new_tab(8, 0) + for _, item in ipairs(opt.body) do + assert(#item == 2, "if BODY is TABLE, BODY need key[1]->value[2] (2 values)") + insert(body, url_encode(item[1])..'='..url_encode(item[2])) + end + local Body = concat(body, "&") + insert(request, #request, fmt('Content-Length: %s', #Body)) + insert(request, #request, 'Content-Type: application/x-www-form-urlencoded\r\n') + insert(request, Body) + end + + if type(opt.body) == "string" then + insert(request, fmt('Content-Length: %s\r\n', #opt.body)) + insert(request, opt.body) + end + + end + + return concat(request, CRLF) +end local function build_get_req (opt) local request = new_tab(16, 0) @@ -437,6 +486,7 @@ return { sock_connect = sock_connect, httpc_response = httpc_response, splite_protocol = splite_protocol, + build_raw_req = build_raw_req, build_get_req = build_get_req, build_post_req = build_post_req, build_json_req = build_json_req, From e7d3ccd30d7eed9be8df7fc444f3521b7833989f Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 3 Mar 2020 20:20:08 +0800 Subject: [PATCH 494/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dxml2lua=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E7=9A=84=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/xml2lua/init.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lualib/xml2lua/init.lua b/lualib/xml2lua/init.lua index 0126c212..397e1f26 100644 --- a/lualib/xml2lua/init.lua +++ b/lualib/xml2lua/init.lua @@ -30,4 +30,9 @@ function xml.load(xml_path) return xfile, error end +-- 将table序列化为xml +function xml.toxml( ... ) + return xml2lua.toXml(...) +end + return xml From 11f1535a4b2c6f7b2c518c43800d05ebbe318c95 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 4 Mar 2020 20:51:12 +0800 Subject: [PATCH 495/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0docker=E7=A4=BA?= =?UTF-8?q?=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/db/database.sql | 62 ++++++++++++++++++------------------------ docker/script/main.lua | 2 +- 2 files changed, 27 insertions(+), 37 deletions(-) diff --git a/docker/db/database.sql b/docker/db/database.sql index e53f8a54..90a70f98 100644 --- a/docker/db/database.sql +++ b/docker/db/database.sql @@ -13,14 +13,14 @@ /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; +/*!40101 SET NAMES utf8mb4 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -# Dump of table cfadmin_headers -# ------------------------------------------------------------ - +# ---------------------------- +# Table structure for cfadmin_headers +# ---------------------------- CREATE TABLE IF NOT EXISTS `cfadmin_headers` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', `name` varchar(255) NOT NULL COMMENT '头部名称', @@ -29,13 +29,11 @@ CREATE TABLE IF NOT EXISTS `cfadmin_headers` ( `update_at` int(11) unsigned NOT NULL COMMENT '修改时间', `active` tinyint(4) unsigned NOT NULL COMMENT '删除标志', PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - - -# Dump of table cfadmin_menus -# ------------------------------------------------------------ +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='顶部菜单表'; +# ---------------------------- +# Table structure for cfadmin_menus +# ---------------------------- CREATE TABLE IF NOT EXISTS `cfadmin_menus` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', `parent` int(11) unsigned NOT NULL COMMENT '父菜单ID', @@ -46,14 +44,12 @@ CREATE TABLE IF NOT EXISTS `cfadmin_menus` ( `update_at` int(11) unsigned NOT NULL COMMENT '更新时间', `active` tinyint(4) unsigned NOT NULL COMMENT '删除标志', PRIMARY KEY (`id`), - KEY `com_index` (`active`,`url`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - - -# Dump of table cfadmin_permissions -# ------------------------------------------------------------ + KEY `parant_index` (`parent`) USING BTREE COMMENT '父ID索引' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='侧边菜单表'; +# ---------------------------- +# Table structure for cfadmin_permissions +# ---------------------------- CREATE TABLE IF NOT EXISTS `cfadmin_permissions` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', `role_id` int(11) unsigned NOT NULL COMMENT '所属角色', @@ -63,13 +59,11 @@ CREATE TABLE IF NOT EXISTS `cfadmin_permissions` ( `active` tinyint(4) unsigned NOT NULL COMMENT '是否启用', PRIMARY KEY (`id`), KEY `com_index` (`active`,`role_id`,`menu_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - - -# Dump of table cfadmin_roles -# ------------------------------------------------------------ +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色权限表'; +# ---------------------------- +# Table structure for cfadmin_roles +# ---------------------------- CREATE TABLE IF NOT EXISTS `cfadmin_roles` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', `name` varchar(255) NOT NULL COMMENT '角色名称', @@ -78,26 +72,22 @@ CREATE TABLE IF NOT EXISTS `cfadmin_roles` ( `update_at` int(1) unsigned NOT NULL COMMENT '修改时间', `active` tinyint(4) unsigned NOT NULL COMMENT '删除标志', PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - - -# Dump of table cfadmin_tokens -# ------------------------------------------------------------ +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户角色表'; +# ---------------------------- +# Table structure for cfadmin_tokens +# ---------------------------- CREATE TABLE IF NOT EXISTS `cfadmin_tokens` ( `uid` int(11) unsigned NOT NULL COMMENT '用户ID', `name` varchar(255) NOT NULL COMMENT '用户名称', `token` varchar(255) NOT NULL COMMENT '用户TOKEN', `create_at` int(11) unsigned NOT NULL COMMENT '登录时间', PRIMARY KEY (`uid`) -) ENGINE=MEMORY DEFAULT CHARSET=utf8; - - - -# Dump of table cfadmin_users -# ------------------------------------------------------------ +) ENGINE=MEMORY DEFAULT CHARSET=utf8mb4 COMMENT='用户Token表'; +# ---------------------------- +# Table structure for cfadmin_users +# ---------------------------- CREATE TABLE IF NOT EXISTS `cfadmin_users` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', `name` varchar(255) NOT NULL COMMENT '用户名', @@ -110,7 +100,7 @@ CREATE TABLE IF NOT EXISTS `cfadmin_users` ( `update_at` int(11) unsigned NOT NULL COMMENT '修改时间', `active` tinyint(4) unsigned NOT NULL COMMENT '删除标志', PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; diff --git a/docker/script/main.lua b/docker/script/main.lua index cc07897f..1e0dacc6 100644 --- a/docker/script/main.lua +++ b/docker/script/main.lua @@ -14,7 +14,7 @@ local db = DB:new({ port = 3306, username = 'root', password = '123456789', - charset = 'utf8', + charset = 'utf8mb4', database = 'cfadmin', max = 100, }) From f7d182ced8d9171922c7768492f553fcca48b99e Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 4 Mar 2020 20:51:55 +0800 Subject: [PATCH 496/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/main.lua | 2 +- script/test_cfadmin.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/script/main.lua b/script/main.lua index 91df4ed0..df91a32d 100644 --- a/script/main.lua +++ b/script/main.lua @@ -14,7 +14,7 @@ local db = DB:new({ port = 3306, username = 'root', password = '123456789', - charset = 'utf8', + charset = 'utf8mb4', database = 'cfadmin', max = 100, }) diff --git a/script/test_cfadmin.lua b/script/test_cfadmin.lua index e661c913..95205453 100644 --- a/script/test_cfadmin.lua +++ b/script/test_cfadmin.lua @@ -13,7 +13,7 @@ local db = DB:new({ port = 3306, username = 'root', password = '123456789', - charset = 'utf8', + charset = 'utf8mb4', database = 'cfadmin', max = 100, }) From b08ffb75bd57fd1dbf7977471b7b15e99eb8b349 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 8 Mar 2020 22:57:05 +0800 Subject: [PATCH 497/956] =?UTF-8?q?httpc=E7=9A=84post=E4=B8=8Ejson?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E6=94=AF=E6=8C=81=E7=9B=B4=E6=8E=A5=E4=BC=A0?= =?UTF-8?q?=E9=80=92=E5=A4=96=E9=83=A8=E6=9E=84=E5=BB=BA=E5=A5=BD=E7=9A=84?= =?UTF-8?q?=E5=AD=97=E7=AC=A6=E4=B8=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/class.lua | 4 ++-- lualib/httpc/init.lua | 2 +- lualib/httpc/protocol.lua | 18 +++++++++++++++--- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/lualib/httpc/class.lua b/lualib/httpc/class.lua index db60884b..814e9938 100644 --- a/lualib/httpc/class.lua +++ b/lualib/httpc/class.lua @@ -378,8 +378,6 @@ end -- json 请求 function httpc:json (domain, headers, json, timeout) - assert(type(json) == "string", "Please passed A vaild json string.") - local opt, err = splite_protocol(domain) if not opt then return nil, err @@ -393,6 +391,8 @@ function httpc:json (domain, headers, json, timeout) return nil, "2. 不同的域名不可使用httpc对象来请求" end + assert(type(json) == "string" or type(json) == "table", "attempted passed a invalide json string or table.") + opt.json = json opt.headers = headers opt.server = self.server diff --git a/lualib/httpc/init.lua b/lualib/httpc/init.lua index db51fcba..50376a5c 100644 --- a/lualib/httpc/init.lua +++ b/lualib/httpc/init.lua @@ -225,7 +225,7 @@ local function json(domain, headers, json, timeout) return nil, err end - assert(type(json) == "string", "Please passed A vaild json string.") + assert(type(json) == "string" or type(json) == "table", "attempted passed a invalide json string or table.") opt.json = json opt.headers = headers diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua index 5f41b982..5352dd4f 100644 --- a/lualib/httpc/protocol.lua +++ b/lualib/httpc/protocol.lua @@ -329,7 +329,7 @@ local function build_post_req (opt) insert(request, header[1] .. ': ' .. header[2] .. CRLF) end end - insert(request, CRLF) + if type(opt.body) == "table" then local body = new_tab(8, 0) for _, item in ipairs(opt.body) do @@ -337,10 +337,17 @@ local function build_post_req (opt) insert(body, url_encode(item[1])..'='..url_encode(item[2])) end local Body = concat(body, "&") - insert(request, #request, fmt('Content-Length: %s\r\n', #Body)) - insert(request, #request, 'Content-Type: application/x-www-form-urlencoded\r\n') + insert(request, fmt('Content-Length: %s\r\n', #Body)) + insert(request, 'Content-Type: application/x-www-form-urlencoded\r\n\r\n') insert(request, Body) + elseif type(opt.body) == 'string' and opt.body ~= '' then + insert(request, fmt('Content-Length: %s\r\n', #opt.body)) + insert(request, 'Content-Type: application/x-www-form-urlencoded\r\n\r\n') + insert(request, opt.body) + else + insert(request, "Content-Length: 0\r\n\r\n") end + return concat(request) end @@ -383,6 +390,11 @@ local function build_json_req (opt) insert(request, 'Content-Type: application/json') insert(request, fmt("Content-Length: %s", #opt.json)) end + if type(opt.json) == 'table' then + opt.json = json_encode(opt.json) + insert(request, 'Content-Type: application/json') + insert(request, "Content-Length: " .. #opt.json) + end return concat(request, CRLF) .. CRLF2 .. opt.json end From 9479b6267f60184c6877878d623aa6f6b7ceaa60 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 11 Mar 2020 20:36:01 +0800 Subject: [PATCH 498/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dread=E5=8F=AF?= =?UTF-8?q?=E8=83=BD=E5=87=BA=E7=8E=B0=E6=A0=88=E6=BA=A2=E5=87=BA=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 494 ++++++++++++++++++++++----------------------- 1 file changed, 238 insertions(+), 256 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index c1d1a928..9c52915f 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -9,14 +9,13 @@ #define SERVER (0) #define CLIENT (1) -static inline -void SETSOCKETOPT(int sockfd, int mode){ +static inline void SETSOCKETOPT(int sockfd, int mode){ int Enable = 1; int ret = 0; - /* 设置非阻塞 */ + /* 设置非阻塞 */ non_blocking(sockfd); /* 地址重用 */ @@ -115,8 +114,7 @@ void SETSOCKETOPT(int sockfd, int mode){ } /* server fd */ -static int -create_server_fd(int port, int backlog){ +static int create_server_fd(int port, int backlog){ errno = 0; /* 建立 TCP Server Socket */ int sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); @@ -152,42 +150,40 @@ create_server_fd(int port, int backlog){ } /* client fd */ -static int -create_client_fd(const char *ipaddr, int port){ +static int create_client_fd(const char *ipaddr, int port){ errno = 0; - /* 建立 TCP Client Socket */ - int sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); - if (0 >= sockfd) { + /* 建立 TCP Client Socket */ + int sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); + if (0 >= sockfd) { LOG("ERROR", strerror(errno)); return -1; } - /* socket option set */ - SETSOCKETOPT(sockfd, CLIENT); + /* socket option set */ + SETSOCKETOPT(sockfd, CLIENT); struct sockaddr_in6 SA; memset(&SA, 0x0, sizeof(SA)); - SA.sin6_family = AF_INET6; - SA.sin6_port = htons(port); - int error = inet_pton(AF_INET6, ipaddr, &SA.sin6_addr); + SA.sin6_family = AF_INET6; + SA.sin6_port = htons(port); + int error = inet_pton(AF_INET6, ipaddr, &SA.sin6_addr); if (1 != error) { LOG("ERROR", strerror(errno)); close(sockfd); return -1; - } + } - int ret = connect(sockfd, (struct sockaddr*)&SA, sizeof(SA)); + int ret = connect(sockfd, (struct sockaddr*)&SA, sizeof(SA)); if (ret < 0 && errno != EINPROGRESS){ LOG("ERROR", strerror(errno)); close(sockfd); return -1; } - return sockfd; + return sockfd; } -static int -create_unixsock_fd(const char* path, size_t path_len, int backlog) { +static int create_server_unixsock(const char* path, size_t path_len, int backlog) { errno = 0; int sockfd = socket(AF_LOCAL, SOCK_STREAM, IPPROTO_IP); if (0 >= sockfd){ @@ -222,51 +218,49 @@ create_unixsock_fd(const char* path, size_t path_len, int backlog) { return sockfd; } -static void -TCP_IO_CB(CORE_P_ core_io *io, int revents) { - - if (revents & EV_ERROR) { - LOG("ERROR", "Recevied a core_io object internal error from libev."); - return ; - } - - lua_State *co = (lua_State *)core_get_watcher_userdata(io); - if (lua_status(co) == LUA_YIELD || lua_status(co) == LUA_OK){ - int status = CO_RESUME(co, NULL, 0); - if (status != LUA_YIELD && status != LUA_OK){ - LOG("ERROR", lua_tostring(co, -1)); - core_io_stop(CORE_LOOP_ io); - } - } +static void TCP_IO_CB(CORE_P_ core_io *io, int revents) { + + if (revents & EV_ERROR) { + LOG("ERROR", "Recevied a core_io object internal error from libev."); + return ; + } + + lua_State *co = (lua_State *)core_get_watcher_userdata(io); + if (lua_status(co) == LUA_YIELD || lua_status(co) == LUA_OK){ + int status = CO_RESUME(co, NULL, 0); + if (status != LUA_YIELD && status != LUA_OK){ + LOG("ERROR", lua_tostring(co, -1)); + core_io_stop(CORE_LOOP_ io); + } + } } -static void -IO_CONNECT(CORE_P_ core_io *io, int revents){ +static void IO_CONNECT(CORE_P_ core_io *io, int revents){ - if (revents & EV_ERROR) { - LOG("ERROR", "Recevied a core_io object internal error from libev."); - return ; - } + if (revents & EV_ERROR) { + LOG("ERROR", "Recevied a core_io object internal error from libev."); + return ; + } - if (revents & EV_WRITE){ - lua_State *co = (lua_State *)core_get_watcher_userdata(io); - if (lua_status(co) == LUA_YIELD || lua_status(co) == LUA_OK){ - int CONNECTED = 0, err = 0; + if (revents & EV_WRITE){ + lua_State *co = (lua_State *)core_get_watcher_userdata(io); + if (lua_status(co) == LUA_YIELD || lua_status(co) == LUA_OK){ + int CONNECTED = 0, err = 0; socklen_t len = sizeof(socklen_t); - if(getsockopt(io->fd, SOL_SOCKET, SO_ERROR, &err, (socklen_t*)&len) == 0 && err == 0) CONNECTED = 1; - lua_pushboolean(co, CONNECTED); - int status = CO_RESUME(co, NULL, 1); - if (status != LUA_YIELD && status != LUA_OK){ - LOG("ERROR", lua_tostring(co, -1)); - core_io_stop(CORE_LOOP_ io); - } - } - } + if(getsockopt(io->fd, SOL_SOCKET, SO_ERROR, &err, (socklen_t*)&len) == 0 && err == 0) CONNECTED = 1; + lua_pushboolean(co, CONNECTED); + int status = CO_RESUME(co, NULL, 1); + if (status != LUA_YIELD && status != LUA_OK){ + LOG("ERROR", lua_tostring(co, -1)); + core_io_stop(CORE_LOOP_ io); + } + } + } } -static void /* 接受链接 */ -IO_ACCEPT(CORE_P_ core_io *io, int revents){ +/* 接受链接 */ +static void IO_ACCEPT(CORE_P_ core_io *io, int revents){ if (revents & EV_READ){ lua_State *co = (lua_State *) core_get_watcher_userdata(io); @@ -297,12 +291,11 @@ IO_ACCEPT(CORE_P_ core_io *io, int revents){ LOG("ERROR", "Error Lua Accept Method"); core_io_stop(CORE_LOOP_ io); } - } - } + } + } } -static void -IO_ACCEPT_EX(CORE_P_ core_io *io, int revents) { +static void IO_ACCEPT_EX(CORE_P_ core_io *io, int revents) { if (revents & EV_READ){ lua_State *co = (lua_State *) core_get_watcher_userdata(io); int status = lua_status(co); @@ -410,8 +403,7 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ } } -static int -tcp_sendfile(lua_State *L){ +static int tcp_sendfile(lua_State *L){ core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); lua_State *t = lua_tothread(L, 2); const char* path = luaL_checkstring(L, 3); @@ -433,75 +425,79 @@ tcp_sendfile(lua_State *L){ return 1; } -static int -tcp_read(lua_State *L){ +static int tcp_read(lua_State *L){ - errno = 0; + errno = 0; - int fd = lua_tointeger(L, 1); - if (0 >= fd) return 0; + int fd = lua_tointeger(L, 1); + if (0 >= fd) return 0; - int bytes = lua_tointeger(L, 2); - if (0 >= bytes) return 0; + lua_Integer bytes = lua_tointeger(L, 2); + if (0 >= bytes) return 0; - do { - char str[bytes]; - int rsize = read(fd, str, bytes); + char* str = alloca(1 << 16); + if (bytes > (1 << 16)){ + str = (char*)lua_newuserdata(L, bytes); + } - if (rsize > 0) { - lua_pushlstring(L, str, rsize); - lua_pushinteger(L, rsize); - return 2; - } - if (0 > rsize) { - if (errno == EINTR) continue; + do { + int rsize = read(fd, str, bytes); + if (rsize > 0) { + lua_pushlstring(L, str, rsize); + lua_pushinteger(L, rsize); + return 2; + } + if (0 > rsize) { + if (errno == EINTR) continue; if (errno == EWOULDBLOCK) { lua_pushnil(L); lua_pushinteger(L, 0); return 2; } - } - } while(0); + } + } while(0); - return 0; + return 0; } -static int -tcp_sslread(lua_State *L){ - - errno = 0; - - SSL *ssl = lua_touserdata(L, 1); - if (!ssl) return 0; - - int bytes = lua_tointeger(L, 2); - if (0 >= bytes) return 0; - - do { - char str[bytes]; - int rsize = SSL_read(ssl, str, bytes); - if (0 < rsize) { - lua_pushlstring(L, str, rsize); - lua_pushinteger(L, rsize); - return 2; - } - if (0 > rsize){ - if (errno == EINTR) continue; - if (SSL_ERROR_WANT_READ == SSL_get_error(ssl, rsize)){ - lua_pushnil(L); - lua_pushinteger(L, 0); - return 2; - } - } - } while (0); - - return 0; +static int tcp_sslread(lua_State *L){ + + errno = 0; + + SSL *ssl = lua_touserdata(L, 1); + if (!ssl) return 0; + + lua_Integer bytes = lua_tointeger(L, 2); + if (0 >= bytes) return 0; + + char* str = alloca(1 << 16); + if (bytes > (1 << 16)){ + str = (char*)lua_newuserdata(L, bytes); + } + + do { + int rsize = SSL_read(ssl, str, bytes); + if (0 < rsize) { + lua_pushlstring(L, str, rsize); + lua_pushinteger(L, rsize); + return 2; + } + if (0 > rsize){ + if (errno == EINTR) continue; + if (SSL_ERROR_WANT_READ == SSL_get_error(ssl, rsize)){ + lua_pushnil(L); + lua_pushinteger(L, 0); + return 2; + } + } + } while (0); + + return 0; } -static int -tcp_write(lua_State *L){ +static int tcp_write(lua_State *L){ - errno = 0; + errno = 0; size_t resp_len = 0; @@ -526,72 +522,68 @@ tcp_write(lua_State *L){ } while (0); - return 0; + return 0; } -static int -tcp_sslwrite(lua_State *L){ +static int tcp_sslwrite(lua_State *L){ - SSL *ssl = lua_touserdata(L, 1); - if (!ssl) + SSL *ssl = lua_touserdata(L, 1); + if (!ssl) return 0; - const char *response = lua_tostring(L, 2); - if (!response) + const char *response = lua_tostring(L, 2); + if (!response) return luaL_error(L, "tcp_sslwrite ERROR: attempt to write an empty string."); - int resp_len = lua_tointeger(L, 3); + int resp_len = lua_tointeger(L, 3); - errno = 0; + errno = 0; - do { - int wsize = SSL_write(ssl, response, resp_len); - if (wsize > 0) { lua_pushinteger(L, wsize); return 1; } - if (wsize < 0){ - if (errno == EINTR) continue; - if (SSL_ERROR_WANT_WRITE == SSL_get_error(ssl, wsize)){ lua_pushinteger(L, 0); return 1; } - } - } while (0); + do { + int wsize = SSL_write(ssl, response, resp_len); + if (wsize > 0) { lua_pushinteger(L, wsize); return 1; } + if (wsize < 0){ + if (errno == EINTR) continue; + if (SSL_ERROR_WANT_WRITE == SSL_get_error(ssl, wsize)){ lua_pushinteger(L, 0); return 1; } + } + } while (0); - return 0; + return 0; } -static int -new_server_fd(lua_State *L){ - const char *ip = lua_tostring(L, 1); - if(!ip) return 0; +static int new_server_fd(lua_State *L){ + const char *ip = lua_tostring(L, 1); + if(!ip) return 0; - int port = lua_tointeger(L, 2); - if(!port) return 0; + int port = lua_tointeger(L, 2); + if(!port) return 0; - int backlog = lua_tointeger(L, 3); + int backlog = lua_tointeger(L, 3); - int fd = create_server_fd(port, 0 >= backlog ? 128 : backlog); - if (0 >= fd) return 0; + int fd = create_server_fd(port, 0 >= backlog ? 128 : backlog); + if (0 >= fd) return 0; - lua_pushinteger(L, fd); + lua_pushinteger(L, fd); - return 1; + return 1; } -static int -new_client_fd(lua_State *L){ - const char *ip = lua_tostring(L, 1); - if(!ip) return 0; +static int new_client_fd(lua_State *L){ + const char *ip = lua_tostring(L, 1); + if(!ip) return 0; - int port = lua_tointeger(L, 2); - if(!port) return 0; + int port = lua_tointeger(L, 2); + if(!port) return 0; - int fd = create_client_fd(ip, port); - if (0 >= fd) return 0; + int fd = create_client_fd(ip, port); + if (0 >= fd) return 0; - lua_pushinteger(L, fd); + lua_pushinteger(L, fd); - return 1; + return 1; } -static int -new_unixsock_fd(lua_State *L) { +static int new_unixsock_fd(lua_State *L) { size_t size = 0; const char* path = luaL_checklstring(L, 1, &size); if (!path) @@ -608,7 +600,7 @@ new_unixsock_fd(lua_State *L) { int backlog = luaL_checkinteger(L, 3); - int fd = create_unixsock_fd(path, size, 0 >= backlog ? 128 : backlog); + int fd = create_server_unixsock(path, size, 0 >= backlog ? 128 : backlog); if (fd <= 0) return 0; @@ -616,30 +608,28 @@ new_unixsock_fd(lua_State *L) { return 1; } -static int -tcp_listen(lua_State *L){ - core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); - if(!io) return 0; +static int tcp_listen(lua_State *L){ + core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); + if(!io) return 0; - /* socket文件描述符 */ - int fd = lua_tointeger(L, 2); - if (0 >= fd) return 0; + /* socket文件描述符 */ + int fd = lua_tointeger(L, 2); + if (0 >= fd) return 0; - /* 回调协程 */ - lua_State *co = lua_tothread(L, 3); - if (!co) return 0; + /* 回调协程 */ + lua_State *co = lua_tothread(L, 3); + if (!co) return 0; - core_set_watcher_userdata(io, co); + core_set_watcher_userdata(io, co); - core_io_init(io, IO_ACCEPT, fd, EV_READ); + core_io_init(io, IO_ACCEPT, fd, EV_READ); - core_io_start(CORE_LOOP_ io); + core_io_start(CORE_LOOP_ io); - return 1; + return 1; } -static int -tcp_listen_ex(lua_State *L) { +static int tcp_listen_ex(lua_State *L) { core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); if (!io) return 0; @@ -659,148 +649,140 @@ tcp_listen_ex(lua_State *L) { return 1; } -static int -tcp_connect(lua_State *L){ +static int tcp_connect(lua_State *L){ - core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); - if(!io) return 0; + core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); + if(!io) return 0; - /* socket文件描述符 */ - int fd = lua_tointeger(L, 2); - if (0 >= fd) return 0; + /* socket文件描述符 */ + int fd = lua_tointeger(L, 2); + if (0 >= fd) return 0; - /* 回调协程 */ - lua_State *co = lua_tothread(L, 3); - if (!co) return 0; + /* 回调协程 */ + lua_State *co = lua_tothread(L, 3); + if (!co) return 0; - core_set_watcher_userdata(io, co); + core_set_watcher_userdata(io, co); - core_io_init(io, IO_CONNECT, fd, EV_READ | EV_WRITE); + core_io_init(io, IO_CONNECT, fd, EV_READ | EV_WRITE); - core_io_start(CORE_LOOP_ io); + core_io_start(CORE_LOOP_ io); - return 0; + return 0; } -static int -tcp_sslconnect(lua_State *L){ +static int tcp_sslconnect(lua_State *L){ - SSL *ssl = (SSL*) lua_touserdata(L, 1); - if (!ssl) return 0; + SSL *ssl = (SSL*) lua_touserdata(L, 1); + if (!ssl) return 0; - int status = SSL_do_handshake(ssl); - if (status >= 1) { - lua_pushboolean(L, 1); - return 1; - } + int status = SSL_do_handshake(ssl); + if (status >= 1) { + lua_pushboolean(L, 1); + return 1; + } int ERR = SSL_get_error(ssl, status); // printf("status = %d, ERR = %d, SSL_ERROR_WANT_READ == %d, SSL_ERROR_WANT_WRITE == %d\n", status, ERR, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE); - if (SSL_ERROR_WANT_READ == ERR || SSL_ERROR_WANT_WRITE == ERR) { - lua_pushnil(L); - lua_pushinteger(L, ERR - 1); - return 2; - } - return 0; + if (SSL_ERROR_WANT_READ == ERR || SSL_ERROR_WANT_WRITE == ERR) { + lua_pushnil(L); + lua_pushinteger(L, ERR - 1); + return 2; + } + return 0; } -static int -tcp_start(lua_State *L){ +static int tcp_start(lua_State *L){ - core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); - if(!io) return 0; + core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); + if(!io) return 0; - /* socket文件描述符 */ - int fd = lua_tointeger(L, 2); - if (0 >= fd) return 0; + /* socket文件描述符 */ + int fd = lua_tointeger(L, 2); + if (0 >= fd) return 0; - /* 监听事件 */ - int events = lua_tointeger(L, 3); - if (0 >= events || events > 3) return 0; + /* 监听事件 */ + int events = lua_tointeger(L, 3); + if (0 >= events || events > 3) return 0; - /* 回调协程 */ - lua_State *co = lua_tothread(L, 4); - if (!co) return 0; + /* 回调协程 */ + lua_State *co = lua_tothread(L, 4); + if (!co) return 0; - core_set_watcher_userdata(io, co); + core_set_watcher_userdata(io, co); - core_io_init(io, TCP_IO_CB, fd, events); + core_io_init(io, TCP_IO_CB, fd, events); - core_io_start(CORE_LOOP_ io); + core_io_start(CORE_LOOP_ io); - return 0; + return 0; } -static int -ssl_new(lua_State *L){ +static int ssl_new(lua_State *L){ - int fd = lua_tointeger(L, 1); + int fd = lua_tointeger(L, 1); - SSL_CTX *ssl_ctx = SSL_CTX_new(SSLv23_method()); - if (!ssl_ctx) return 0; + SSL_CTX *ssl_ctx = SSL_CTX_new(SSLv23_method()); + if (!ssl_ctx) return 0; - SSL *ssl = SSL_new(ssl_ctx); - if (!ssl) return 0; + SSL *ssl = SSL_new(ssl_ctx); + if (!ssl) return 0; - SSL_set_fd(ssl, fd); + SSL_set_fd(ssl, fd); - SSL_set_connect_state(ssl); + SSL_set_connect_state(ssl); - lua_pushlightuserdata(L, (void*) ssl_ctx); + lua_pushlightuserdata(L, (void*) ssl_ctx); - lua_pushlightuserdata(L, (void*) ssl); + lua_pushlightuserdata(L, (void*) ssl); - return 2; + return 2; } -static int -ssl_free(lua_State *L){ +static int ssl_free(lua_State *L){ - SSL_CTX *ssl_ctx = (SSL_CTX*) lua_touserdata(L, 1); - if (ssl_ctx) SSL_CTX_free(ssl_ctx); // 销毁ctx上下文; + SSL_CTX *ssl_ctx = (SSL_CTX*) lua_touserdata(L, 1); + if (ssl_ctx) SSL_CTX_free(ssl_ctx); // 销毁ctx上下文; - SSL *ssl = (SSL*) lua_touserdata(L, 2); - if (ssl) SSL_free(ssl); // 销毁基于ctx的ssl对象; + SSL *ssl = (SSL*) lua_touserdata(L, 2); + if (ssl) SSL_free(ssl); // 销毁基于ctx的ssl对象; - return 0; + return 0; } -static int -tcp_new(lua_State *L){ +static int tcp_new(lua_State *L){ - core_io *io = (core_io *) lua_newuserdata(L, sizeof(core_io)); + core_io *io = (core_io *) lua_newuserdata(L, sizeof(core_io)); - if(!io) return 0; + if(!io) return 0; - luaL_setmetatable(L, "__TCP__"); + luaL_setmetatable(L, "__TCP__"); - return 1; + return 1; } -static int -tcp_stop(lua_State *L){ +static int tcp_stop(lua_State *L){ - core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); - if(!io) return 0; + core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); + if(!io) return 0; - core_io_stop(CORE_LOOP_ io); + core_io_stop(CORE_LOOP_ io); - return 0; + return 0; } -static int -tcp_close(lua_State *L){ +static int tcp_close(lua_State *L){ - int fd = lua_tointeger(L, 1); + int fd = lua_tointeger(L, 1); - if (fd && fd > 0) close(fd); + if (fd && fd > 0) close(fd); - return 0; + return 0; } From 4dd59873831f696b198246035a7f64762248794d Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 14 Mar 2020 22:35:30 +0800 Subject: [PATCH 499/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0crc64=E7=AE=97?= =?UTF-8?q?=E6=B3=95=E5=B0=86=E8=BF=94=E5=9B=9E=E5=AD=97=E7=AC=A6=E4=B8=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/crc.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/luaclib/src/lcrypt/crc.c b/luaclib/src/lcrypt/crc.c index 0c9eedbc..03811640 100644 --- a/luaclib/src/lcrypt/crc.c +++ b/luaclib/src/lcrypt/crc.c @@ -201,24 +201,32 @@ static uint64_t CRC64[] = { }; int lcrc32(lua_State *L){ - size_t len; + size_t len = 0; const char *str = luaL_checklstring(L, 1, &len); + if (!str || len < 1) + return luaL_error(L, "invalid string."); - uint32_t i = 0; uint32_t crc = 0xFFFFFFFF; - for (i = 0; i < len; i++) crc = CRC32[ (crc ^ str[i]) & 0xff ] ^ (crc >> 8); + for (uint32_t i = 0; i < len; i++) crc = CRC32[ (crc ^ str[i]) & 0xff ] ^ (crc >> 8); lua_pushinteger(L, crc ^ 0xFFFFFFFF); return 1; }; int lcrc64(lua_State *L){ - size_t len; + size_t len = 0; const char *str = luaL_checklstring(L, 1, &len); + if (!str || len < 1) + return luaL_error(L, "invalid string."); - uint32_t i = 0; uint64_t crc = 0x0; - for (i = 0; i < len; i++) crc = CRC64[(uint8_t)crc ^ (uint8_t)str[i]] ^ (crc >> 8); - lua_pushnumber(L, crc); + for (uint32_t i = 0; i < len; i++) + crc = CRC64[(uint8_t)crc ^ (uint8_t)str[i]] ^ (crc >> 8); + + char* buf = (char*)lua_newuserdata(L, 20); + memset(buf, 0x0, 20); + sprintf(buf, "%llu", crc); + + lua_pushlstring(L, (const char*)buf, 20); return 1; }; From 3f8fad2fab6549c02429dcc43984b6bb64f546c8 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 14 Mar 2020 22:40:59 +0800 Subject: [PATCH 500/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0httpd=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E5=86=85=E5=AE=B9=E5=A4=A7=E4=BA=8E50=E4=B8=AA?= =?UTF-8?q?=E5=AD=97=E8=8A=82=E6=89=8D=E4=BC=9A=E5=AF=B9=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=B0=9D=E8=AF=95gzip=E7=BC=A9.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index f9ecb60b..0e59625e 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -479,7 +479,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) return sock:close() end local accept_encoding = HEADER['Accept-Encoding'] - if enable_gzip and accept_encoding and find(accept_encoding, "gzip") then + if enable_gzip and type(accept_encoding) == 'string' and find(lower(accept_encoding), "gzip") and #body >= 50 then local compress_body = gzcompress(body) if compress_body then header[#header+1] = 'Content-Encoding: gzip' From 4a5feb1c81530824957850dd3b8af05a6ecf2394 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 15 Mar 2020 22:07:24 +0800 Subject: [PATCH 501/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B=E7=9A=84=E5=91=BD=E5=90=8D?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/text_xml.lua | 48 --------------------------------------------- 1 file changed, 48 deletions(-) delete mode 100644 script/text_xml.lua diff --git a/script/text_xml.lua b/script/text_xml.lua deleted file mode 100644 index 5ed16e9a..00000000 --- a/script/text_xml.lua +++ /dev/null @@ -1,48 +0,0 @@ -local xml2lua = require "xml2lua" -require "utils" - -local xml = [[ - - - 老虎 - meta - - - 狮子 - meta - - - - 水果糖 - 車先生 - - - - 买买买 - 玩玩玩 - 逛逛逛 - - 肉肉 - 小宝贝 - 小QQ - 車爪鱼 - - - - -]] - --- benchmark time: ./cfadmin 耗时:3.6xx/Sec -for i = 1, 10000 do - xml2lua.parser(xml) -end - --- 打印解析后的表结构 -local tab = xml2lua.parser(xml) -var_dump(tab) - --- 原版xml2lua打印会出现相等的情况 --- 这在cf中可能导致不可预知的情况. -local tab1 = xml2lua.parser(xml) -local tab2 = xml2lua.parser(xml) -print(tab1, tab2) From 2d4073e0281bf5e8612774242001c71e9a4d1d86 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 15 Mar 2020 22:08:01 +0800 Subject: [PATCH 502/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9test=5Fdns=E7=9A=84?= =?UTF-8?q?=E7=9A=84=E6=B5=8B=E8=AF=95=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_dns.lua | 52 ++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/script/test_dns.lua b/script/test_dns.lua index 07cc6938..04950b51 100644 --- a/script/test_dns.lua +++ b/script/test_dns.lua @@ -4,6 +4,7 @@ local dns_resolve = dns.resolve local cf = require "cf" local fork = cf.fork +local wait = cf.wait local system = require "system" local now = system.now @@ -18,7 +19,7 @@ local function delete_ipv4_prefix (ip) if system.is_ipv4(ip) then return ip end - return find(ip, '::ffff') and match(ip, "::[fF]+:([%d%.]+)") or ip + return find(ip or "", '::ffff') and match(ip, "::[fF]+:([%d%.]+)") or ip end local domains = { @@ -30,20 +31,41 @@ local domains = { ["京东"] = "www.jd.com", ["唯品会"] = "www.vip.com", ["盛大"] = "www.sdo.com", + ["测试"] = "pwaj.dwauidwa.raw" } --- 查询次数 -local min, max = 1, 10000 - -for company, domain in pairs(domains) do - local t1 = now() - for i = min, max do - fork(function (...) - local ok, ip = dns_resolve(domain) - if i == max then - local t2 = now() - LOG:DEBUG("结束解析 : ".. company .. "[".. domain .."]" , "[".. delete_ipv4_prefix(ip) .."]", "总耗时: " .. fmt("%0.8f/Sec", t2 - t1)) - end - end) +--[[ +计算方式 +(min - max + 1) * 1 为单个域名查询总次数 +(min - max + 1) * #domain 为所有域名查询总次数 +]] +local min, max = 1, 1 + +--[[ +测试方式: 每隔3秒后为每个域名建立1万个协程进行并发查询测试; +空间消耗: 内存消耗预计在250MB左右; +时间消耗: 每次网络查询应该在0.1/Sec左右, 每次缓存查询应该在0.001/Sec左右; +]] +cf.at(3, function ( ) + for company, domain in pairs(domains) do + local start = now() + local total = 0 + for i = min, max do + fork(function (...) + local t1 = now() + local ok, ip = dns_resolve(domain) + if not ok then + LOG:WARN(fmt("测试失败: <%s>[%s]的结果:[%s]", company, domain, ip)) + return + end + total = total + (now() - t1) + if i == max then + LOG:DEBUG(fmt("测试完成 : <%s>[%s]的结果:[%s]; 平均消耗: %0.5f, 总计消耗: %0.8f", company, domain, delete_ipv4_prefix(ip), total / max, now() - start)) + end + end) + end end -end + collectgarbage() +end) + +wait() \ No newline at end of file From 756af4687facd636d5d966187fc861b91fd9a5fe Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 15 Mar 2020 22:10:55 +0800 Subject: [PATCH 503/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9test=5Fxml=E7=9A=84?= =?UTF-8?q?=E5=91=BD=E5=90=8D=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_xml.lua | 48 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 script/test_xml.lua diff --git a/script/test_xml.lua b/script/test_xml.lua new file mode 100644 index 00000000..5ed16e9a --- /dev/null +++ b/script/test_xml.lua @@ -0,0 +1,48 @@ +local xml2lua = require "xml2lua" +require "utils" + +local xml = [[ + + + 老虎 + meta + + + 狮子 + meta + + + + 水果糖 + 車先生 + + + + 买买买 + 玩玩玩 + 逛逛逛 + + 肉肉 + 小宝贝 + 小QQ + 車爪鱼 + + + + +]] + +-- benchmark time: ./cfadmin 耗时:3.6xx/Sec +for i = 1, 10000 do + xml2lua.parser(xml) +end + +-- 打印解析后的表结构 +local tab = xml2lua.parser(xml) +var_dump(tab) + +-- 原版xml2lua打印会出现相等的情况 +-- 这在cf中可能导致不可预知的情况. +local tab1 = xml2lua.parser(xml) +local tab2 = xml2lua.parser(xml) +print(tab1, tab2) From a4131864082cfacc1205ed8e96903ab7224d3655 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 15 Mar 2020 22:13:26 +0800 Subject: [PATCH 504/956] =?UTF-8?q?=E4=BC=98=E5=8C=96dns=E5=BA=93=E5=9C=A8?= =?UTF-8?q?dual-stack=E4=B8=8B=E7=9A=84=E6=9F=A5=E8=AF=A2=E8=A1=A8?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/dns.lua | 47 +++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/lualib/protocol/dns.lua b/lualib/protocol/dns.lua index e96801f8..fdfc303c 100644 --- a/lualib/protocol/dns.lua +++ b/lualib/protocol/dns.lua @@ -11,6 +11,7 @@ local cf_wait = cf.wait local cf_wakeup = cf.wakeup local cf_sleep = cf.sleep +local new_tab = sys.new_tab local check_ipv4 = sys.ipv4 local check_ipv6 = sys.ipv6 @@ -76,6 +77,7 @@ local function check_ip(ip) return true, 6 end end + return false, "Not a valid IP address." end local function gen_cache() @@ -116,10 +118,10 @@ if #dns_list < 1 then gen_cache() end -local function get_dns_client() +local function get_dns_client(ip_version) if #dns_list >= 1 then - local ip = dns_list[random(1, #dns_list)] local udp = UDP:new():timeout(dns._timeout or 30) + local ip = dns_list[random(1, #dns_list)] local ok, v = check_ip(ip) if v == 4 then ip = prefix .. ip @@ -128,6 +130,9 @@ local function get_dns_client() if not ok then return nil, 'Create UDP Socket error.' end + if ip_version then + v = ip_version + end return udp, v end return nil, "Can't find system dns in /etc/resolve.conf." @@ -216,10 +221,13 @@ local function check_wait(domain, wlist, ...) cos[domain] = nil end -local function dns_query(domain) - local wlist = {} - cos[domain] = wlist - local dns_client, msg = get_dns_client() +local function dns_query(domain, ip_version) + local wlist = cos[domain] + if not wlist then + wlist = new_tab(16, 0) + cos[domain] = wlist + end + local dns_client, msg = get_dns_client(ip_version) if not dns_client then check_wait(domain, wlist, nil, msg) return nil, msg @@ -242,21 +250,28 @@ local function dns_query(domain) dns_client:close() readable = true if not dns_resp or not len or len < LIMIT_HEADER_LEN then - check_wait(domain, wlist, nil, "1. Malformed message length.") + local err = "1. Malformed message length." + check_wait(domain, wlist, nil, err) return nil, err end local answer_header, nbyte = unpack_header(dns_resp) if answer_header.qdcount ~= 1 then - check_wait(domain, wlist, nil, "2. Malformed message response.") + local err = "2. Malformed message response." + check_wait(domain, wlist, nil, err) return nil, err end if not answer_header.ancount or answer_header.ancount < 1 then - check_wait(domain, wlist, nil, "3. Unresolved domain name.") + if not ip_version then -- 如果IPv4无法解析则尝试ipv6, 反之亦然. + return dns_query(domain, msg == 4 and 6 or 4) + end + local err = "3. Unresolved domain name." + check_wait(domain, wlist, nil, err) return nil, err end local question, nbyte = unpack_question(dns_resp, nbyte) if question.name ~= domain then - check_wait(domain, wlist, nil, "4. Inconsistent query domain.") + local err = "4. Inconsistent query domain." + check_wait(domain, wlist, nil, err) return nil, err end local answer @@ -277,8 +292,12 @@ local function dns_query(domain) end local ok, v = check_ip(answer.ip) if not ok then - check_wait(domain, wlist, nil, "5. Unknown IP address." .. domain) - return nil, err..domain + if not ip_version then -- 如果IPv4无法解析则尝试ipv6, 反之亦然. + return dns_query(domain, msg == 4 and 6 or 4) + end + local err = "5. " .. v .. " (" .. (domain) .. ")" + check_wait(domain, wlist, nil, err) + return nil, err end if ok and v == 4 then answer.ip = prefix..answer.ip @@ -297,7 +316,7 @@ function dns.timeout(timeout) dns._timeout = timeout end -function dns.resolve(domain) +function dns.resolve(domain, ip_version) -- 检查参数是否有效 if type(domain) ~= 'string' or domain == '' then return nil, "attempt to pass an invalid domain." @@ -325,7 +344,7 @@ function dns.resolve(domain) insert(wlist, co) return cf_wait() end - return dns_query(domain) + return dns_query(domain, ip_version) end -- require "utils" -- var_dump(dns_cache) From a5d4d355f0bcf94b97639ca6d15262d67eaf1981 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 16 Mar 2020 14:31:46 +0800 Subject: [PATCH 505/956] =?UTF-8?q?=E8=A7=A3=E5=86=B3crc=E7=9A=84=E8=AD=A6?= =?UTF-8?q?=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/crc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/luaclib/src/lcrypt/crc.c b/luaclib/src/lcrypt/crc.c index 03811640..0e7640ab 100644 --- a/luaclib/src/lcrypt/crc.c +++ b/luaclib/src/lcrypt/crc.c @@ -1,4 +1,5 @@ #include "lcrypt.h" +#include /* CRC32 TAB */ static uint32_t CRC32[] = { @@ -225,7 +226,7 @@ int lcrc64(lua_State *L){ char* buf = (char*)lua_newuserdata(L, 20); memset(buf, 0x0, 20); - sprintf(buf, "%llu", crc); + sprintf(buf, "%"PRIu64"", crc); lua_pushlstring(L, (const char*)buf, 20); return 1; From dcfb575485fbd375a3617be0109e3553adf39ddd Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 16 Mar 2020 18:40:40 +0800 Subject: [PATCH 506/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0sys.os=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E5=88=A4=E6=96=AD=E6=93=8D=E4=BD=9C=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lsys.c | 26 ++++++++++++++++---------- src/core_sys.c | 29 +++++++++++++++++++++++------ src/core_sys.h | 3 +++ 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/luaclib/src/lsys.c b/luaclib/src/lsys.c index 2ff3564e..0d2d862d 100644 --- a/luaclib/src/lsys.c +++ b/luaclib/src/lsys.c @@ -3,30 +3,29 @@ #include "../../src/core.h" // 提供一个精确到微秒的时间戳 -static int -lnow(lua_State *L){ +static int lnow(lua_State *L){ lua_pushnumber(L, now()); return 1; } -static int /* 此方法可用于检查是否为有效ipv4地址*/ -lipv4(lua_State *L){ +/* 此方法可用于检查是否为有效ipv4地址*/ +static int lipv4(lua_State *L){ const char *IP = lua_tostring(L, 1); if (!IP) return luaL_error(L, "ipv4 error: 请至少传递一个string类型参数\n"); lua_pushboolean(L, ipv4(IP)); return 1; } -static int /* 此方法可用于检查是否为有效ipv6地址*/ -lipv6(lua_State *L){ +/* 此方法可用于检查是否为有效ipv6地址*/ +static int lipv6(lua_State *L){ const char *IP = lua_tostring(L, 1); if (!IP) return luaL_error(L, "ipv6 error: 请至少传递一个string类型参数\n"); lua_pushboolean(L, ipv6(IP)); return 1; } -static int -ldate(lua_State *L){ +/* 返回时间 */ +static int ldate(lua_State *L){ const char *fmt = lua_tostring(L, 1); if (!fmt) return luaL_error(L, "Date: 错误的格式化方法"); time_t timestamp = lua_tointeger(L, 2); @@ -37,8 +36,14 @@ ldate(lua_State *L){ return 1; } -static int -lnew_tab(lua_State *L){ +/* 返回当前操作系统类型 */ +static int los(lua_State *L){ + lua_pushstring(L, os()); + return 1; +} + +/* 创建表 */ +static int lnew_tab(lua_State *L){ lua_Integer array_size = luaL_checkinteger(L, 1); lua_Integer hash_size = luaL_checkinteger(L, 2); lua_createtable(L, array_size, hash_size); @@ -53,6 +58,7 @@ luaopen_sys(lua_State *L){ {"ipv4", lipv4}, {"ipv6", lipv6}, {"date", ldate}, + {"os", los}, {"new_tab", lnew_tab}, {NULL, NULL} }; diff --git a/src/core_sys.c b/src/core_sys.c index e3c40de8..1715805a 100644 --- a/src/core_sys.c +++ b/src/core_sys.c @@ -1,24 +1,41 @@ #include "core_sys.h" -double /* 此方法提供一个精确到微秒级的时间戳 */ -now(void){ +#ifdef __MSYS__ + const char *__O_S__ = "Windows"; +#endif + +#if defined(__linux) || defined(__linux__) + const char *__O_S__ = "Linux"; +#endif + +#if defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) + const char *__O_S__ = "Unix"; +#endif + + /* 此方法提供一个精确到微秒级的时间戳 */ +double now(void){ struct timespec now = {}; clock_gettime(CLOCK_REALTIME, &now); return now.tv_sec + now.tv_nsec * 1e-9; } -int /* 此方法可用于检查是否为有效ipv4地址*/ -ipv4(const char *IP){ +/* 此方法可用于检查是否为有效ipv4地址*/ +int ipv4(const char *IP){ if (!IP) return 0; struct in_addr addr = {}; if (inet_pton(AF_INET, IP, &addr) == 1) return 1; return 0; } -int /* 此方法可用于检查是否为有效ipv6地址*/ -ipv6(const char *IP){ +/* 此方法可用于检查是否为有效ipv6地址*/ +int ipv6(const char *IP){ if (!IP) return 0; struct in6_addr addr = {}; if (inet_pton(AF_INET6, IP, &addr) == 1) return 1; return 0; } + +/* 返回当前操作系统类型 */ +const char* os(void) { + return __O_S__; +} \ No newline at end of file diff --git a/src/core_sys.h b/src/core_sys.h index 80b66524..2d0f722b 100644 --- a/src/core_sys.h +++ b/src/core_sys.h @@ -70,4 +70,7 @@ int ipv4(const char *IP); /* 检查是否为有效ipv6地址 */ int ipv6(const char *IP); +/* 返回当前操作系统类型 */ +const char* os(void); + #endif From 31286f7039166dbbc3572386fd34b2813a501c05 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 16 Mar 2020 19:02:12 +0800 Subject: [PATCH 507/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E5=AE=8F=E5=AE=9A=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_ev.h | 4 ++-- src/core_sys.c | 16 ++++++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/core_ev.h b/src/core_ev.h index 7f6c43bb..774fb3f0 100644 --- a/src/core_ev.h +++ b/src/core_ev.h @@ -30,7 +30,7 @@ #define EV_NO_THREADS 1 /* eventfd 与 signalfd */ -#if defined(__linux) || defined(__linux__) +#if !defined(__MSYS__) && (defined(__linux) || defined(__linux__)) // #define EV_USE_LINUXAIO 1 /* 待完整支持AIO后再根据情况开启 */ #define EV_USE_EPOLL 1 #define EV_USE_INOTIFY 1 @@ -42,7 +42,7 @@ #define EV_USE_KQUEUE 1 #endif -#if !defined(EV_USE_KQUEUE) && !defined(EV_USE_EPOLL) +#ifdef __MSYS__ #define EV_USE_SELECT 1 #endif diff --git a/src/core_sys.c b/src/core_sys.c index 1715805a..be2c9dbb 100644 --- a/src/core_sys.c +++ b/src/core_sys.c @@ -1,15 +1,19 @@ #include "core_sys.h" #ifdef __MSYS__ - const char *__O_S__ = "Windows"; + const char *__OS__ = "Windows"; #endif -#if defined(__linux) || defined(__linux__) - const char *__O_S__ = "Linux"; +#if !defined(__MSYS__) && (defined(__linux) || defined(__linux__)) + const char *__OS__ = "Linux"; #endif -#if defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) - const char *__O_S__ = "Unix"; +#ifdef __APPLE__ + const char *__OS__ = "Apple"; +#endif + +#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) + const char *__OS__ = "Unix"; #endif /* 此方法提供一个精确到微秒级的时间戳 */ @@ -37,5 +41,5 @@ int ipv6(const char *IP){ /* 返回当前操作系统类型 */ const char* os(void) { - return __O_S__; + return __OS__; } \ No newline at end of file From 484ad1f825cfee59fd30b9aebeaa9fedb4873608 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 16 Mar 2020 20:45:56 +0800 Subject: [PATCH 508/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dhttp=5Fparser?= =?UTF-8?q?=E7=9A=84=E4=B8=80=E4=B8=AA=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lhttpparser/httpparser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/luaclib/src/lhttpparser/httpparser.c b/luaclib/src/lhttpparser/httpparser.c index 57d8c4ea..fdb431c1 100644 --- a/luaclib/src/lhttpparser/httpparser.c +++ b/luaclib/src/lhttpparser/httpparser.c @@ -387,7 +387,7 @@ int phr_parse_request(const char *buf_start, size_t len, const char **method, si { const char *buf = buf_start, *buf_end = buf_start + len; size_t max_headers = *num_headers; - int r; + int r = 0; *method = NULL; *method_len = 0; From 1f25f98eccb2f0921497b75dea3c0c593406814e Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 16 Mar 2020 20:46:20 +0800 Subject: [PATCH 509/956] =?UTF-8?q?=E5=B1=8F=E8=94=BDpbc=E5=9C=A8=E4=BD=8E?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E7=BC=96=E8=AF=91=E5=99=A8=E7=9A=84=E8=AD=A6?= =?UTF-8?q?=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lpbc/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/luaclib/src/lpbc/Makefile b/luaclib/src/lpbc/Makefile index ea2e74f3..7be4f627 100644 --- a/luaclib/src/lpbc/Makefile +++ b/luaclib/src/lpbc/Makefile @@ -12,7 +12,7 @@ CC = cc INCLUDES += -I../../../src -I/usr/local/include LIBS = -L../ -L../../../ -L/usr/local/lib -CFLAGS = -O3 -Wall -shared -fPIC +CFLAGS = -O3 -Wall -shared -fPIC -fno-strict-aliasing DLL = -lcore -llua build: From 5c81d1b06569d6dcc2ac8fa0aae37a302a1ad6ec Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 16 Mar 2020 20:46:46 +0800 Subject: [PATCH 510/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0sendfile=E5=AE=8F?= =?UTF-8?q?=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 9c52915f..ebe48f69 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -119,7 +119,7 @@ static int create_server_fd(int port, int backlog){ /* 建立 TCP Server Socket */ int sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); if (0 >= sockfd){ - LOG("DEBUG", strerror(errno)); + LOG("ERROR", strerror(errno)); return -1; } /* socket option set */ @@ -374,7 +374,7 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ } #endif -#ifdef EV_USE_SELECT +#ifdef __MSYS__ char buf[sf->offset]; for (;;) { memset(buf, 0x0, sf->offset); From f0ef5d8565fb1259706be97689ff4a1dcb4a46e5 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 16 Mar 2020 20:58:20 +0800 Subject: [PATCH 511/956] =?UTF-8?q?=E5=8F=96=E6=B6=88mysql=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE=E7=9A=84=E8=AF=BB=E5=8F=96=E6=95=B0=E6=8D=AE=E9=99=90?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/crc.c | 6 ++++-- luaclib/src/lhttpparser/httpparser.c | 4 ++-- lualib/protocol/mysql.lua | 8 ++++---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/luaclib/src/lcrypt/crc.c b/luaclib/src/lcrypt/crc.c index 0e7640ab..2cdd73d8 100644 --- a/luaclib/src/lcrypt/crc.c +++ b/luaclib/src/lcrypt/crc.c @@ -207,9 +207,10 @@ int lcrc32(lua_State *L){ if (!str || len < 1) return luaL_error(L, "invalid string."); + uint32_t i; uint32_t crc = 0xFFFFFFFF; - for (uint32_t i = 0; i < len; i++) crc = CRC32[ (crc ^ str[i]) & 0xff ] ^ (crc >> 8); + for (i = 0; i < len; i++) crc = CRC32[ (crc ^ str[i]) & 0xff ] ^ (crc >> 8); lua_pushinteger(L, crc ^ 0xFFFFFFFF); return 1; }; @@ -220,8 +221,9 @@ int lcrc64(lua_State *L){ if (!str || len < 1) return luaL_error(L, "invalid string."); + uint32_t i; uint64_t crc = 0x0; - for (uint32_t i = 0; i < len; i++) + for (i = 0; i < len; i++) crc = CRC64[(uint8_t)crc ^ (uint8_t)str[i]] ^ (crc >> 8); char* buf = (char*)lua_newuserdata(L, 20); diff --git a/luaclib/src/lhttpparser/httpparser.c b/luaclib/src/lhttpparser/httpparser.c index fdb431c1..6d2fb285 100644 --- a/luaclib/src/lhttpparser/httpparser.c +++ b/luaclib/src/lhttpparser/httpparser.c @@ -447,7 +447,7 @@ int phr_parse_response(const char *buf_start, size_t len, int *minor_version, in { const char *buf = buf_start, *buf_end = buf + len; size_t max_headers = *num_headers; - int r; + int r = 0; *minor_version = -1; *status = 0; @@ -472,7 +472,7 @@ int phr_parse_headers(const char *buf_start, size_t len, struct phr_header *head { const char *buf = buf_start, *buf_end = buf + len; size_t max_headers = *num_headers; - int r; + int r = 0; *num_headers = 0; diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua index 3ebb9305..975f50fb 100644 --- a/lualib/protocol/mysql.lua +++ b/lualib/protocol/mysql.lua @@ -166,9 +166,9 @@ local function _recv_packet(self) return nil, nil, "empty packet" end - if len > self._max_packet_size then - return nil, nil, "packet size too big: " .. len - end + -- if len > self._max_packet_size then + -- return nil, nil, "packet size too big: " .. len + -- end local num = strbyte(data, pos) @@ -415,7 +415,7 @@ function MySQL.connect(self, opts) local max_packet_size = opts.max_packet_size if not max_packet_size then - max_packet_size = 4 * 1024 * 1024 -- default 4 MB + max_packet_size = 16 * 1024 * 1024 -- default 4 MB end self._max_packet_size = max_packet_size From c237e46e98c03b7c57fd4783d35acbcd27ca84c9 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 16 Mar 2020 21:56:20 +0800 Subject: [PATCH 512/956] =?UTF-8?q?=E4=B8=BAhttpd=E5=A2=9E=E5=8A=A0deflate?= =?UTF-8?q?=E6=94=AF=E6=8C=81.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index 0e59625e..f226aa6a 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -14,6 +14,7 @@ local new_tab = require("sys").new_tab local insert = table.insert local lz = require "lz" +local decompress = lz.compress local gzcompress = lz.gzcompress local form = require "httpd.Form" @@ -479,11 +480,25 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) return sock:close() end local accept_encoding = HEADER['Accept-Encoding'] - if enable_gzip and type(accept_encoding) == 'string' and find(lower(accept_encoding), "gzip") and #body >= 50 then - local compress_body = gzcompress(body) - if compress_body then - header[#header+1] = 'Content-Encoding: gzip' - body = compress_body + if enable_gzip and type(accept_encoding) == 'string' and #body >= 50 then + local YES = false + -- 如果支持deflate则使用compress压缩 + if find(lower(accept_encoding), "deflate") then + local compress_body = decompress(body) + if compress_body then + header[#header+1] = 'Content-Encoding: deflate' + body = compress_body + YES = true + end + end + -- 如果支持gzip就使用gzip压缩后返回 + if not YES and find(lower(accept_encoding), "gzip") then + local compress_body = gzcompress(body) + if compress_body then + header[#header+1] = 'Content-Encoding: gzip' + body = compress_body + YES = true + end end end header[#header+1] = 'Content-Length: ' .. #body From c482cfca14419eed954f3ddae661fa18b6c94fec Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 17 Mar 2020 14:19:37 +0800 Subject: [PATCH 513/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=B7=AF=E7=94=B1?= =?UTF-8?q?=E6=B3=A8=E5=86=8C=E4=B8=8E=E6=9F=A5=E6=89=BE=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Router.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index f036dc9e..9088b2cb 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -49,7 +49,7 @@ end -- 主要用作分割hash路由查找 local function to_route(route) - return spliter(route, slash, '') + return spliter(route, slash2, '/') end -- 检查是路径回退是否超出静态文件根目录 @@ -90,6 +90,7 @@ local function registery_router (route, class, route_type) end local function find_route (method, path) + print(path) path = url_decode(split(path, 1, (find(path, '?') or 0) - 1)) local t = routes[to_route(path)] if t then From aa191d84e6f0da263dfd0c6ed47f84014eb28eab Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 17 Mar 2020 14:42:11 +0800 Subject: [PATCH 514/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=B7=AF=E7=94=B1?= =?UTF-8?q?=E6=9F=A5=E6=89=BE=E4=B8=8E=E6=B3=A8=E5=86=8C=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Router.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index 9088b2cb..af6cb44d 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -42,14 +42,16 @@ local static = {} -- 静态文件路由 local function hex_route(route) local tab = new_tab(32, 0) for r in splite(route, '/([^ /%?]+)') do - tab[#tab + 1] = r + if r ~= '' then + tab[#tab + 1] = r + end end return tab end -- 主要用作分割hash路由查找 local function to_route(route) - return spliter(route, slash2, '/') + return spliter(route, "([/]+)", '/') end -- 检查是路径回退是否超出静态文件根目录 @@ -90,7 +92,6 @@ local function registery_router (route, class, route_type) end local function find_route (method, path) - print(path) path = url_decode(split(path, 1, (find(path, '?') or 0) - 1)) local t = routes[to_route(path)] if t then From 88fd08ec664aa7c1feca81e61d454a09fccfa175 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 18 Mar 2020 12:34:14 +0800 Subject: [PATCH 515/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_start.c | 98 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 82 insertions(+), 16 deletions(-) diff --git a/src/core_start.c b/src/core_start.c index ccfef1fd..87906b69 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -1,10 +1,48 @@ #include "core.h" -#define MAX_ENTRY_LENGTH (1 << 8) +#define MAX_ENTRY_LENGTH (1 << 10) + +#ifdef __MSYS__ + const char *__OS__ = "Windows"; +#endif + +#if !defined(__MSYS__) && (defined(__linux) || defined(__linux__)) + const char *__OS__ = "Linux"; +#endif + +#ifdef __APPLE__ + const char *__OS__ = "Apple"; +#endif + +#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) + const char *__OS__ = "Unix"; +#endif + +#define __CFADMIN_VERSION__ "1.0" static char script_entry[MAX_ENTRY_LENGTH] = "script/main.lua"; -void write_pid(const char *filename) { +/* 打印使用指南 */ +void usage_print() { + printf("cfadmin System : %s(%s)\n", __OS__, __VERSION__ ); + printf("\n"); + printf("cfadmin Version : %s\n", __CFADMIN_VERSION__ ); + printf("\n"); + printf( + "cfadmin Usage:\n" \ + "\n" \ + " -h \"Print cfadmin usage.\"\n" \ + "\n" \ + " -d \"Make cfadmin run in daemon mode.\"\n" \ + "\n" \ + " -e \"Specify lua entry file name.\"\n" \ + "\n" \ + " -p \"Specify the process Pid write file name.\"\n" \ + ); +} + +/* 建立Pid文件 */ +void write_pid_file(const char *filename) { errno = 0; FILE *f = fopen(filename, "w"); if (!f) { @@ -16,24 +54,52 @@ void write_pid(const char *filename) { fclose(f); } +/* 指定入口文件路径 */ +void specify_entry_file(const char *filename) { + memset(script_entry, 0x0, MAX_ENTRY_LENGTH); + memmove(script_entry, filename, strlen(filename)); +} + +/* 后台运行 */ +void specify_process_daemon() { + daemon(1, 0); +} + void check_args(int argc, char const *argv[]) { - if (argc > 1) { - uint32_t index; - for (index = 0; index < argc; index ++) { - if (!strcmp("-d", argv[index])){ - daemon(1, 0); + int opt = -1; + opterr = 0; + while ((opt = getopt(argc, argv, "hde:p:" )) != -1) { + switch(opt) { + case 'h': + usage_print(); + printf("\n"); + exit(0); + break; + case 'e': + if (!optarg){ + printf("-e need lua entry filename\n"); + exit(0); + } + specify_entry_file(optarg); continue; - } - if (!strcmp("-e", argv[index])) { - if (argc > index){ - memset(script_entry, 0x0, MAX_ENTRY_LENGTH); - memmove(script_entry, argv[index + 1], strlen(argv[index + 1])); + exit(0); + break; + case 'p': + if (!optarg){ + printf("-e need lua entry filename\n"); + exit(0); } + write_pid_file(optarg); continue; - } - + case 'd': + specify_process_daemon(); + continue; + case '?': + default : + exit(0); } } + return; } int main(int argc, char const *argv[]) @@ -41,8 +107,8 @@ int main(int argc, char const *argv[]) check_args(argc, argv); - /* 建立Pid文件 */ - write_pid("cfadmin.pid"); + + // write_pid("cfadmin.pid"); /* 系统初始化 */ core_sys_init(script_entry); From 543f8ef607dd35250fd79edcca257654374308b6 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 18 Mar 2020 13:23:52 +0800 Subject: [PATCH 516/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_start.c | 47 ++++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/core_start.c b/src/core_start.c index 87906b69..741b8412 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -1,7 +1,5 @@ #include "core.h" -#define MAX_ENTRY_LENGTH (1 << 10) - #ifdef __MSYS__ const char *__OS__ = "Windows"; #endif @@ -20,14 +18,23 @@ #define __CFADMIN_VERSION__ "1.0" + +#define MAX_ENTRY_LENGTH (1 << 10) + static char script_entry[MAX_ENTRY_LENGTH] = "script/main.lua"; +static char pid_filename[MAX_ENTRY_LENGTH] = "cfadmin.pid"; + /* 打印使用指南 */ -void usage_print() { +void usage_print(const char * ext) { printf("cfadmin System : %s(%s)\n", __OS__, __VERSION__ ); printf("\n"); printf("cfadmin Version : %s\n", __CFADMIN_VERSION__ ); printf("\n"); + if (ext) { + printf("%s\n", ext); + return; + } printf( "cfadmin Usage:\n" \ "\n" \ @@ -38,6 +45,7 @@ void usage_print() { " -e \"Specify lua entry file name.\"\n" \ "\n" \ " -p \"Specify the process Pid write file name.\"\n" \ + "\n" \ ); } @@ -60,6 +68,12 @@ void specify_entry_file(const char *filename) { memmove(script_entry, filename, strlen(filename)); } +/* 指定pid文件路径 */ +void specify_pid_file(const char *filename) { + memset(pid_filename, 0x0, MAX_ENTRY_LENGTH); + memmove(pid_filename, filename, strlen(filename)); +} + /* 后台运行 */ void specify_process_daemon() { daemon(1, 0); @@ -68,48 +82,35 @@ void specify_process_daemon() { void check_args(int argc, char const *argv[]) { int opt = -1; opterr = 0; - while ((opt = getopt(argc, argv, "hde:p:" )) != -1) { + while ((opt = getopt(argc, argv, "hde:p:")) != -1) { switch(opt) { - case 'h': - usage_print(); - printf("\n"); - exit(0); - break; case 'e': - if (!optarg){ - printf("-e need lua entry filename\n"); - exit(0); - } specify_entry_file(optarg); continue; - exit(0); - break; case 'p': - if (!optarg){ - printf("-e need lua entry filename\n"); - exit(0); - } - write_pid_file(optarg); + specify_pid_file(optarg); continue; case 'd': specify_process_daemon(); continue; case '?': + case 'h': default : + usage_print(optarg); exit(0); } } + + write_pid_file(pid_filename); return; } int main(int argc, char const *argv[]) { + /* 参数检查 */ check_args(argc, argv); - - // write_pid("cfadmin.pid"); - /* 系统初始化 */ core_sys_init(script_entry); From 0c2d7a334e7fc826e598d136911307b67d2f9426 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 18 Mar 2020 13:24:04 +0800 Subject: [PATCH 517/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=BF=BD=E7=95=A5?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 64af1fd8..745d5cc7 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,7 @@ # PID And Log *.log *.PID +*.pid # Code Edit .vscode From 7e35eaba21d375df6ef09eb967e2da35691fa6ab Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 18 Mar 2020 13:31:28 +0800 Subject: [PATCH 518/956] =?UTF-8?q?=E5=AE=8C=E5=96=84cfadmin=E8=BE=93?= =?UTF-8?q?=E5=87=BAUsage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_start.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/core_start.c b/src/core_start.c index 741b8412..e1776b02 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -26,25 +26,21 @@ static char script_entry[MAX_ENTRY_LENGTH] = "script/main.lua"; static char pid_filename[MAX_ENTRY_LENGTH] = "cfadmin.pid"; /* 打印使用指南 */ -void usage_print(const char * ext) { +void usage_print() { printf("cfadmin System : %s(%s)\n", __OS__, __VERSION__ ); printf("\n"); printf("cfadmin Version : %s\n", __CFADMIN_VERSION__ ); printf("\n"); - if (ext) { - printf("%s\n", ext); - return; - } printf( - "cfadmin Usage:\n" \ + "cfadmin Usage: ./cfadmin [options]\n" \ "\n" \ - " -h \"Print cfadmin usage.\"\n" \ + " -h \"Print cfadmin usage.\"\n" \ "\n" \ - " -d \"Make cfadmin run in daemon mode.\"\n" \ + " -d \"Make cfadmin run in daemon mode.\"\n" \ "\n" \ - " -e \"Specify lua entry file name.\"\n" \ + " -e \"Specify lua entry file name.\"\n" \ "\n" \ - " -p \"Specify the process Pid write file name.\"\n" \ + " -p \"Specify the process Pid write file name.\"\n" \ "\n" \ ); } @@ -96,7 +92,7 @@ void check_args(int argc, char const *argv[]) { case '?': case 'h': default : - usage_print(optarg); + usage_print(); exit(0); } } From 714174e920d4be5170257999997701fad6c19b81 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 18 Mar 2020 14:28:09 +0800 Subject: [PATCH 519/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dmysql=E5=8F=AF?= =?UTF-8?q?=E8=83=BD=E5=87=BA=E7=8E=B0=E8=AF=BB=E5=8F=96=E4=B8=8D=E5=AE=8C?= =?UTF-8?q?=E5=85=A8=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/mysql.lua | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua index 975f50fb..e5c0b1ef 100644 --- a/lualib/protocol/mysql.lua +++ b/lualib/protocol/mysql.lua @@ -13,6 +13,8 @@ local sha1= crypt.sha1 local setmetatable = setmetatable local error = error local tonumber = tonumber +local concat = table.concat + local new_tab = require("sys").new_tab local CHARSET_MAP = { @@ -146,11 +148,26 @@ local function _send_packet(self, req, size) return sock:send(packet) end +local function sock_recv(sock, byte) + local buffers = new_tab(32, 0) + while 1 do + local buf = sock:recv(byte) + if not buf then + return nil, "MySQL Server closed." + end + buffers[#buffers+1] = buf + byte = byte - #buf + if byte == 0 then + return concat(buffers) + end + end +end + local function _recv_packet(self) local sock = self.sock - local data, err = sock:recv(4) -- packet header + local data, err = sock_recv(sock, 4) -- packet header if not data then self.state = nil return nil, nil, "failed to receive packet header: "..(err or "nil") @@ -176,7 +193,7 @@ local function _recv_packet(self) self.packet_no = num - data, err = sock:recv(len) + data, err = sock_recv(sock, len) --print("receive returned") @@ -185,9 +202,7 @@ local function _recv_packet(self) return nil, nil, "failed to read packet content: "..(err or "nil") end - local field_count = strbyte(data, 1) - - local typ + local field_count, typ = strbyte(data, 1) if field_count == 0x00 then typ = "OK" elseif field_count == 0xff then From de95af1d84185e31c6e89b209a74e076ff24b66b Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 19 Mar 2020 16:42:33 +0800 Subject: [PATCH 520/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0compress/uncompress2?= =?UTF-8?q?=E7=94=A8=E6=9D=A5=E5=AE=9E=E7=8E=B0deflate=E5=8E=8B=E7=BC=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lz/lzlib.c | 112 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 107 insertions(+), 5 deletions(-) diff --git a/luaclib/src/lz/lzlib.c b/luaclib/src/lz/lzlib.c index b6be4e44..93792d3a 100644 --- a/luaclib/src/lz/lzlib.c +++ b/luaclib/src/lz/lzlib.c @@ -24,7 +24,7 @@ static int lcompress(lua_State *L) { return 0; lua_pushlstring(L, (const char*)out, out_size); - lua_pushinteger(L, out_size); + lua_pushinteger(L, in_size); return 2; } @@ -35,10 +35,17 @@ static int luncompress(lua_State *L) { return 0; size_t out_size = in_size; + + /* 若能传递压缩前的大小, 优先使用此数值 */ + int is_sum = 0; + lua_Integer before_size = lua_tointegerx(L, 2, &is_sum); + if (is_sum && before_size > 0 ) + out_size = before_size; + size_t offset = 1; size_t top = lua_gettop(L); - for(;;) { + do { uint8_t *out = lua_newuserdata(L, out_size); memset(out, 0x0, out_size); @@ -53,8 +60,7 @@ static int luncompress(lua_State *L) { out_size = in_size << offset; continue; } - break; - } + } while(0); return 0; } @@ -67,7 +73,7 @@ static int lgzip_compress(lua_State *L) { z_stream z; stream_init(&z); - int ok = deflateInit2(&z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + 16, 8, Z_DEFAULT_STRATEGY); + int ok = deflateInit2(&z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + 16, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); if (Z_OK != ok) return 0; @@ -130,6 +136,12 @@ static int lgzip_uncompress(lua_State *L) { inflateEnd(&z); return 0; } + /* 防止内存溢出 */ + if (out_size > in_size * 256){ + luaL_pushresultsize(&B, 0); + inflateEnd(&z); + return 0; + } inflateReset(&z); out_size <<= 1; luaL_pushresultsize(&B, 0); @@ -138,12 +150,102 @@ static int lgzip_uncompress(lua_State *L) { return 1; } +static int lcompress2(lua_State *L) { + size_t in_size = 0; + const char* in = luaL_checklstring(L, 1, &in_size); + if (in_size <= 0) + return 0; + + z_stream z; + stream_init(&z); + + int ok = deflateInit2(&z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS * -1, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); + if (Z_OK != ok) + return 0; + + z.next_in = (uint8_t *)in; + z.avail_in = in_size; + + luaL_Buffer B; + z.avail_out = deflateBound(&z, in_size); + z.next_out = (uint8_t *)luaL_buffinitsize(L, &B, z.avail_out); + memset(z.next_out, 0x0, z.avail_out); + + int ret = deflate(&z, Z_FINISH); + if (ret != Z_STREAM_END) { + luaL_pushresultsize(&B, 0); + deflateEnd(&z); + return 0; + } + if (deflateEnd(&z) != Z_OK){ + luaL_pushresultsize(&B, 0); + return 0; + } + luaL_pushresultsize(&B, z.total_out); + lua_pushinteger(L, in_size); + return 2; +} + +static int luncompress2(lua_State *L) { + size_t in_size = 0; + const char* in = luaL_checklstring(L, 1, &in_size); + if (in_size <= 0) + return 0; + + z_stream z; + stream_init(&z); + + int ok = inflateInit2(&z, MAX_WBITS * -1); + if (Z_OK != ok) + return 0; + + size_t out_size = in_size * 4; + int top = lua_gettop(L); + + for(;;) { + luaL_Buffer B; + z.next_in = (uint8_t *)in; + z.avail_in = in_size; + z.avail_out = out_size; + z.next_out = (uint8_t *)luaL_buffinitsize(L, &B, out_size); + memset(z.next_out, 0x0, z.avail_out); + + int ret = inflate(&z, Z_FINISH); + if (ret == Z_STREAM_END) { + int ok = inflateEnd(&z); + if (ok != Z_OK) + return 0; + luaL_pushresultsize(&B, z.total_out); + break; + } + if (ret != Z_BUF_ERROR) { + luaL_pushresultsize(&B, 0); + inflateEnd(&z); + return 0; + } + /* 防止内存溢出 */ + if (out_size > in_size * 256){ + luaL_pushresultsize(&B, 0); + inflateEnd(&z); + return 0; + } + inflateReset(&z); + out_size *= 2; + luaL_pushresultsize(&B, 0); + lua_settop(L, top); + } + return 1; +} + LUAMOD_API int luaopen_lz(lua_State *L){ luaL_checkversion(L); luaL_Reg zlib_libs[] = { /* LZ77压缩/解压方法 */ {"compress", lcompress}, {"uncompress", luncompress}, + /* 原生压缩方法 */ + {"compress2", lcompress2}, + {"uncompress2", luncompress2}, /* gzip压缩/解压方法 */ {"gzcompress", lgzip_compress}, {"gzuncompress", lgzip_uncompress}, From ad36650a89adb1fe637d6a36a54bd122aea2eefc Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 19 Mar 2020 17:39:09 +0800 Subject: [PATCH 521/956] =?UTF-8?q?=E4=B8=BAwebsocket=E5=BA=93=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0permessage-deflate=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 9 +++- lualib/protocol/websocket/client.lua | 15 +++--- lualib/protocol/websocket/protocol.lua | 72 ++++++++++++++++++-------- lualib/protocol/websocket/server.lua | 23 ++++---- 4 files changed, 81 insertions(+), 38 deletions(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index f226aa6a..bac6a197 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -241,12 +241,19 @@ local function Switch_Protocol(http, cls, sock, header, method, version, path, i if protocol then -- 仅支持协议回传 response[#response+1] = "Sec-Websocket-Protocol: "..tostring(protocol) end + local ext = nil + if type(header['Sec-WebSocket-Extensions']) == 'string' and find(header['Sec-WebSocket-Extensions'], "permessage%-deflate") then + response[#response+1] = "Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover" + ext = "deflate" + end + -- require "utils" + -- var_dump(header) http:tolog(101, path, header['X-Real-IP'] or ip, X_Forwarded_FORMAT(header['X-Forwarded-For'] or ip), method, now() - start_time) local ok = sock:send(concat(response, CRLF)..CRLF2) if not ok then return end - return wsserver.start {cls = cls, sock = sock} + return wsserver.start { cls = cls, sock = sock, ext = ext } end local function send_header (sock, header) diff --git a/lualib/protocol/websocket/client.lua b/lualib/protocol/websocket/client.lua index 660f8f59..fd9ca20e 100644 --- a/lualib/protocol/websocket/client.lua +++ b/lualib/protocol/websocket/client.lua @@ -106,6 +106,9 @@ local function check_response (self, secure) sock_close(self) return nil, '错误: Sec-WebSocket-Accept验证失败' end + if type(headers['Sec-WebSocket-Extensions']) == 'string' and find(headers['Sec-WebSocket-Extensions'], "permessage%-deflate") then + self.ext = 'deflate' + end return true end end @@ -120,7 +123,6 @@ local function do_handshake (self) return nil, err end - local GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' local key = char( byte('c'), byte('f'), byte('a'), byte('d'), byte('m'), byte('i'), byte('n'), random(256) - 1, random(256) - 1, random(256) - 1, @@ -135,10 +137,11 @@ local function do_handshake (self) fmt('Host: %s:%s', self.domain, self.port), fmt('Sec-WebSocket-Key: %s', sec_key), 'Origin: http://'..self.domain, - 'Sec-WebSocket-Version: 13', 'Upgrade: websocket', 'Connection: Upgrade', + 'Sec-WebSocket-Version: 13', 'User-Agent: cf-websocket/0.1', + 'Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits', CRLF } local ok, err = sock_send(self, concat(req, CRLF)) @@ -147,7 +150,7 @@ local function do_handshake (self) return ok, err end - return check_response(self, base64encode(sha1(sec_key..GUID))) + return check_response(self, base64encode(sha1(sec_key .. '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'))) end local function url_split (self) @@ -248,7 +251,7 @@ function websocket:send (data, is_binary) return nil, '未连接' end local func = function (...) - return _send_frame(self.sock, true, is_binary and 0x2 or 0x1, data, self.max_payload_len, self.send_masked) + return _send_frame(self.sock, true, is_binary and 0x2 or 0x1, data, self.max_payload_len, self.send_masked, self.ext) end if not self.queue then self.queue = { func } @@ -271,7 +274,7 @@ function websocket:ping(data) return nil, '未连接' end local func = function (...) - return _send_frame(self.sock, true, 0x9, data, self.max_payload_len, self.send_masked) + return _send_frame(self.sock, true, 0x9, data, self.max_payload_len, self.send_masked, self.ext) end if not self.queue then self.queue = { func } @@ -294,7 +297,7 @@ function websocket:pong(data) return nil, '未连接' end local func = function (...) - return _send_frame(self.sock, true, 0xA, data, self.max_payload_len, self.send_masked) + return _send_frame(self.sock, true, 0xA, data, self.max_payload_len, self.send_masked, self.ext) end if not self.queue then self.queue = { func } diff --git a/lualib/protocol/websocket/protocol.lua b/lualib/protocol/websocket/protocol.lua index cb1c281a..8833ab97 100644 --- a/lualib/protocol/websocket/protocol.lua +++ b/lualib/protocol/websocket/protocol.lua @@ -4,6 +4,7 @@ local byte = string.byte local char = string.char local sub = string.sub +local gsub = string.gsub local concat = table.concat local str_char = string.char local rand = math.random @@ -14,6 +15,9 @@ local assert = assert local new_tab = require("sys").new_tab +local lz = require "lz" +local compress2 = lz.compress2 +local uncompress2 = lz.uncompress2 local _M = new_tab(0, 5) @@ -66,6 +70,16 @@ local function sock_send (sock, data) return sock:send(data) end +-- 压缩数据 +local function compress_data(data) + data = compress2(data) + return gsub(data, ".", char(byte(data) - 1), 1) +end + +-- 解压数据 +local function uncompress_data(data) + return uncompress2(gsub(data, ".", char(byte(data) + 1), 1)) +end function _M.recv_frame(sock, max_payload_len, force_masking) local data, err = sock_recv(sock, 2) @@ -77,7 +91,7 @@ function _M.recv_frame(sock, max_payload_len, force_masking) local fin = fst & 0x80 ~= 0 - if fst & 0x70 ~= 0 then + if fst & 0x70 ~= 0 and fst & 0x40 ~= 0x40 then return nil, nil, "bad RSV1, RSV2, or RSV3 bits" end @@ -114,11 +128,7 @@ function _M.recv_frame(sock, max_payload_len, force_masking) .. (err or "unknown") end - if byte(data, 1) ~= 0 - or byte(data, 2) ~= 0 - or byte(data, 3) ~= 0 - or byte(data, 4) ~= 0 - then + if byte(data, 1) ~= 0 or byte(data, 2) ~= 0 or byte(data, 3) ~= 0 or byte(data, 4) ~= 0 then return nil, nil, "payload len too large" end @@ -200,7 +210,15 @@ function _M.recv_frame(sock, max_payload_len, force_masking) msg = "" end end - + if fst & 0x40 == 0x40 and #msg > 0 then + -- print("压缩后的数据长度为:" .. #msg) + local data = uncompress_data(msg) + if not data then + return data, types[opcode], "invalide deflate data." + end + msg = data + -- print("压缩前的数据长度为:" .. #msg) + end return msg, "close" end return "", "close", nil @@ -218,16 +236,24 @@ function _M.recv_frame(sock, max_payload_len, force_masking) else msg = data end - + if fst & 0x40 == 0x40 and #msg > 0 then + -- print("压缩后的数据长度为:" .. #msg) + local data = uncompress_data(msg) + if not data then + return msg, types[opcode], "invalide deflate data." + end + msg = data + -- print("压缩前的数据长度为:" .. #msg) + end return msg, types[opcode], not fin and "again" or nil end -local function build_frame(fin, opcode, payload_len, payload, masking) +local function build_frame(fin, opcode, payload_len, payload, masking, ext) local fst if fin then - fst = 0x80 | opcode + fst = 0x80 | (ext and 0x40 or 0) | opcode else fst = opcode end @@ -248,11 +274,7 @@ local function build_frame(fin, opcode, payload_len, payload, masking) snd = 127 - extra_len_bytes = char(0, 0, 0, 0, - (payload_len >> 24) & 0xff, - (payload_len >> 16) & 0xff, - (payload_len >> 8) & 0xff, - payload_len & 0xff) + extra_len_bytes = char(0, 0, 0, 0, (payload_len >> 24) & 0xff, (payload_len >> 16) & 0xff, (payload_len >> 8) & 0xff, payload_len & 0xff) end local masking_key @@ -278,24 +300,32 @@ end _M.build_frame = build_frame -function _M.send_frame(sock, fin, opcode, payload, max_payload_len, masking) +function _M.send_frame(sock, fin, opcode, payload, max_payload_len, masking, ext) - assert(type(payload) == 'string' and #payload <= max_payload_len, "无效的数据类型或长度超出预期") + assert(type(payload) == 'string' and #payload <= max_payload_len, "Invalid data type or length exceeds expected") local payload_len = #payload if opcode & 0x8 ~= 0 then if payload_len > 125 then - return error("控制帧的有效载荷长度太多") + return error("The payload length of the control frame is too long.") end if not fin then - return error("畸形的控制帧") + return error("Invalid control frame.") end end - local frame, err = build_frame(fin, opcode, payload_len, payload, masking) + -- 支持permessage-deflate压缩 + if ext == 'deflate' and payload_len > 0 then + -- print("压缩前的数据长度为:" .. #payload) + payload = assert(compress_data(payload), "deflate compress data error.") + payload_len = #payload + -- print("压缩后的数据长度为:" .. #payload) + end + + local frame, err = build_frame(fin, opcode, payload_len, payload, masking, ext) if not frame then - return error("错误的数据帧:"..err) + return error("Invalid data frame: " .. err) end return sock_send(sock, frame) end diff --git a/lualib/protocol/websocket/server.lua b/lualib/protocol/websocket/server.lua index 66ffa288..18c246d5 100644 --- a/lualib/protocol/websocket/server.lua +++ b/lualib/protocol/websocket/server.lua @@ -6,11 +6,12 @@ local wbproto = require "protocol.websocket.protocol" local _recv_frame = wbproto.recv_frame local _send_frame = wbproto.send_frame -local Log = require "logging":new({ dump = true, path = 'protocol-websocket-server'}) +local Log = require "logging":new { dump = true, path = 'protocol-websocket-server'} local type = type local pcall = pcall local ipairs = ipairs +local assert = assert local insert = table.insert local char = string.char @@ -22,6 +23,7 @@ function ws:ctor(opt) self.sock = opt.sock self.send_masked = nil self.max_payload_len = 65535 + self.ext = opt.ext end -- 设置发送掩码 @@ -59,9 +61,9 @@ function ws:send (data, binary) if self.closed then return end - assert(type(data) == 'string' and data ~= '', "websoket error: send发送的消息应该是string类型.") + assert(type(data) == 'string' and data ~= '', "websoket error: send need string data.") self:add_to_queue(function () - return _send_frame(self.sock, true, binary and 0x2 or 0x1, data, self.max_payload_len, self.send_masked) + return _send_frame(self.sock, true, binary and 0x2 or 0x1, data, self.max_payload_len, self.send_masked, self.ext) end) end @@ -72,7 +74,7 @@ function ws:close(data) end self.closed = true self:add_to_queue(function () - return _send_frame(self.sock, true, 0x8, char(((1000 >> 8) & 0xff), (1000 & 0xff))..(type(data) == 'string' and data or ""), self.max_payload_len, self.send_masked) + return _send_frame(self.sock, true, 0x8, char(((1000 >> 8) & 0xff), (1000 & 0xff))..(type(data) == 'string' and data or ""), self.max_payload_len, self.send_masked, self.ext) end) self:add_to_queue(function () return self.sock:close() @@ -84,13 +86,14 @@ local Websocket = { __Version__ = 1.0 } -- Websocket Server 事件循环 function Websocket.start(opt) local sock = opt.sock - local w = ws:new { sock = sock } + local ext = opt.ext + local w = ws:new { sock = sock, ext = ext } local cls = opt.cls:new { ws = w } - local on_open = cls.on_open - local on_message = cls.on_message - local on_error = cls.on_error - local on_close = cls.on_close + local on_open = assert(type(cls.on_open) == 'function' and cls.on_open, "'on_open' method is not implemented.") + local on_message = assert(type(cls.on_message) == 'function' and cls.on_message, "'on_message' method is not implemented.") + local on_error = assert(type(cls.on_error) == 'function' and cls.on_error, "'on_error' method is not implemented.") + local on_close = assert(type(cls.on_close) == 'function' and cls.on_close, "'on_close' method is not implemented.") sock._timeout = cls.timeout or nil local send_masked = cls.send_masked or nil @@ -119,7 +122,7 @@ function Websocket.start(opt) break end if typ == 'ping' then - w:add_to_queue(function () return _send_frame(sock, true, 0xA, data or '', max_payload_len, send_masked) end) + w:add_to_queue(function () return _send_frame(sock, true, 0xA, data or '', max_payload_len, send_masked, ext) end) end if typ == 'text' or typ == 'binary' then cf_fork(on_message, cls, data, typ == 'binary') From 7f1604ceadbadfdda2a39f4f28eb2da5557a48b8 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Fri, 20 Mar 2020 01:09:13 +0800 Subject: [PATCH 522/956] =?UTF-8?q?=E8=A1=A5=E5=85=85websocket=E7=9A=84'x-?= =?UTF-8?q?webkit-deflate-frame'=E5=85=BC=E5=AE=B9=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index bac6a197..ec5c8433 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -242,9 +242,15 @@ local function Switch_Protocol(http, cls, sock, header, method, version, path, i response[#response+1] = "Sec-Websocket-Protocol: "..tostring(protocol) end local ext = nil - if type(header['Sec-WebSocket-Extensions']) == 'string' and find(header['Sec-WebSocket-Extensions'], "permessage%-deflate") then - response[#response+1] = "Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover" - ext = "deflate" + local extension = header['Sec-WebSocket-Extensions'] + if type(extension) == 'string' and extension ~= '' then + if find(header['Sec-WebSocket-Extensions'], "permessage%-deflate") then + response[#response+1] = "Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits=15; server_no_context_takeover; client_no_context_takeover" + ext = "deflate" + elseif find(header['Sec-WebSocket-Extensions'], "x%-webkit%-deflate%-frame") then + response[#response+1] = "Sec-WebSocket-Extensions: x-webkit-deflate-frame; no_context_takeover" + ext = "deflate" + end end -- require "utils" -- var_dump(header) From 0f9920b04058b4c0f67fd8323a4013b180f2f438 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 24 Mar 2020 15:26:49 +0800 Subject: [PATCH 523/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lsys.c | 2 +- luaclib/src/ltask.c | 2 +- luaclib/src/ltcp.c | 2 +- luaclib/src/ltimer.c | 2 +- luaclib/src/ludp.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/luaclib/src/lsys.c b/luaclib/src/lsys.c index 0d2d862d..e4da253a 100644 --- a/luaclib/src/lsys.c +++ b/luaclib/src/lsys.c @@ -1,6 +1,6 @@ #define LUA_LIB -#include "../../src/core.h" +#include // 提供一个精确到微秒的时间戳 static int lnow(lua_State *L){ diff --git a/luaclib/src/ltask.c b/luaclib/src/ltask.c index 114f423c..c6143bfe 100644 --- a/luaclib/src/ltask.c +++ b/luaclib/src/ltask.c @@ -1,6 +1,6 @@ #define LUA_LIB -#include "../../src/core.h" +#include static void TASK_CB(CORE_P_ core_task *task, int revents){ diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index ebe48f69..87a19701 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -1,6 +1,6 @@ #define LUA_LIB -#include "../../src/core.h" +#include #include #include #include diff --git a/luaclib/src/ltimer.c b/luaclib/src/ltimer.c index 5e828c45..5b59fd6b 100644 --- a/luaclib/src/ltimer.c +++ b/luaclib/src/ltimer.c @@ -1,6 +1,6 @@ #define LUA_LIB -#include "../../src/core.h" +#include /* === 定时器 === */ static void diff --git a/luaclib/src/ludp.c b/luaclib/src/ludp.c index 35c26fb0..37873ec9 100644 --- a/luaclib/src/ludp.c +++ b/luaclib/src/ludp.c @@ -1,6 +1,6 @@ #define LUA_LIB -#include "../../src/core.h" +#include static inline void SETSOCKETOPT(int sockfd) { From b3c2ab2c0a2b6385dfaa2e5e6c366ad7f2a8599b Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 24 Mar 2020 15:27:35 +0800 Subject: [PATCH 524/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 15 +++++++++++---- clean.sh | 4 ++-- luaclib/Makefile | 2 ++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/build.sh b/build.sh index 3592e42c..2f5792fc 100755 --- a/build.sh +++ b/build.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Run this file to install libev and lua; if you already have lua and libev in your environment, you can ignore this file and try to compile directly using makefile. -# 运行这个文件可以安装libev与lua; 如果您的环境中已经有了lua与libev后可以忽略此文件并且直接使用makefile尝试编译. +# 运行这个文件可以安装libev与lua; 如果您的环境中已经有了lua/libeio/libev后可以忽略此文件并且直接使用makefile尝试编译. # This file must be executed in the current folder directory, otherwise the installation will be wrong. Beginners need to keep in mind. # 必须在当前文件夹目录执行此文件, 否则安装将会出错. 初学者需要谨记. @@ -17,14 +17,21 @@ current=`pwd` rm -rf build && mkdir build && cd build git clone https://github.com/CandyMi/lua -b v5.3.5 + +git clone https://github.com/CandyMi/libeio + git clone https://github.com/CandyMi/libev -b v4.25 +echo "========== build lua ==========" && + cd ${current}/build/lua && make posix MYCFLAGS="-fPIC -DLUA_USE_DLOPEN -DLUA_USE_READLINE" MYLIBS="-ldl -lreadline" && + cp lua.h luaconf.h lualib.h lauxlib.h ${current}/src && cp liblua.* ${current}/ + echo "========== build libev ==========" && cd ${current}/build/libev && sh autogen.sh && ./configure --prefix=/usr/local && make && cp e*.h ${current}/src && cd .libs && cp $(printf "%s" "`ls | grep libev | grep -v la`") ${current}/ -echo "========== build lua ==========" && - cd ${current}/build/lua && make posix MYCFLAGS="-fPIC -DLUA_USE_DLOPEN -DLUA_USE_READLINE" MYLIBS="-ldl -lreadline" && - cp lua.h luaconf.h lualib.h lauxlib.h ${current}/src && cp liblua.* ${current}/ +echo "========== build libeio ==========" && + cd ${current}/build/libeio && sh autogen.sh && ./configure --prefix=/usr/local && + make && cp e*.h ${current}/src && cd .libs && cp $(printf "%s" "`ls | grep libeio | grep -v la`") ${current}/ echo "========== clean build ==========" && cd ${current} && rm -rf build diff --git a/clean.sh b/clean.sh index d40a9b58..67947fdd 100755 --- a/clean.sh +++ b/clean.sh @@ -1,5 +1,5 @@ # This script is used to clean up build.sh to install the header files and library files brought by libev and lua. You can ignore this file when you already have lua and libev in your environment. # 此脚本为清理build.sh安装libev与lua带来的头文件与库文件, 当您的环境中已经有了lua与libev后可以忽略此文件. -rm -rf libev* liblua* -rm -rf src/l* src/ev* +rm -rf libev* libeio* liblua* +rm -rf src/l* src/e*.h diff --git a/luaclib/Makefile b/luaclib/Makefile index 1fa65955..e777399d 100644 --- a/luaclib/Makefile +++ b/luaclib/Makefile @@ -24,6 +24,8 @@ internal : @$(CC) -o task.so src/ltask.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua @echo "CC - ltimer" @$(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua + @echo "CC - laio" + @$(CC) -o laio.so src/laio.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -llua -leio @echo "CC - ltcp" @$(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore -llua From 8c53930a20337b2870c4c61fea5a91ea1152cc9e Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 24 Mar 2020 15:28:09 +0800 Subject: [PATCH 525/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0aio=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/laio.c | 379 ++++++++++++++++++++++++++++++++++++++++++++ lualib/aio/init.lua | 142 +++++++++++++++++ 2 files changed, 521 insertions(+) create mode 100644 luaclib/src/laio.c create mode 100644 lualib/aio/init.lua diff --git a/luaclib/src/laio.c b/luaclib/src/laio.c new file mode 100644 index 00000000..5278b2e6 --- /dev/null +++ b/luaclib/src/laio.c @@ -0,0 +1,379 @@ +#define LUA_LIB + +#include +#include + +#define EIO_STACKSIZE (1 << 16) + +/* 最小线程数量 */ +#define AIO_MAX_NTHREADS 8 + +#define req_data_to_coroutine(req) (req->data) + +#define luaL_push_string_integer(L, k, v) ({ lua_pushliteral(L, k); lua_pushinteger(L, (v)); lua_rawset(L, -3); }) +#define luaL_push_string_string(L, k, v) ({ lua_pushliteral(L, k); lua_pushstring(L, (v)); lua_rawset(L, -3); }) + + #ifndef S_ISDIR + #define S_ISDIR(mode) (mode & _S_IFDIR) + #endif + #ifndef S_ISREG + #define S_ISREG(mode) (mode & _S_IFREG) + #endif + #ifndef S_ISLNK + #define S_ISLNK(mode) (0) + #endif + #ifndef S_ISSOCK + #define S_ISSOCK(mode) (0) + #endif + #ifndef S_ISFIFO + #define S_ISFIFO(mode) (0) + #endif + #ifndef S_ISCHR + #define S_ISCHR(mode) (mode&_S_IFCHR) + #endif + #ifndef S_ISBLK + #define S_ISBLK(mode) (0) + #endif + +static const char *mode2string (mode_t mode) { + if ( S_ISREG(mode) ) + return "file"; + else if ( S_ISDIR(mode) ) + return "directory"; + else if ( S_ISLNK(mode) ) + return "link"; + else if ( S_ISSOCK(mode) ) + return "socket"; + else if ( S_ISFIFO(mode) ) + return "named pipe"; + else if ( S_ISCHR(mode) ) + return "char device"; + else if ( S_ISBLK(mode) ) + return "block device"; + else + return "other"; +} + +static const char *perm2string (mode_t mode) { + static char perms[10] = "---------"; + int i; + for (i=0;i<9;i++) perms[i]='-'; + if (mode & S_IRUSR) perms[0] = 'r'; + if (mode & S_IWUSR) perms[1] = 'w'; + if (mode & S_IXUSR) perms[2] = 'x'; + if (mode & S_IRGRP) perms[3] = 'r'; + if (mode & S_IWGRP) perms[4] = 'w'; + if (mode & S_IXGRP) perms[5] = 'x'; + if (mode & S_IROTH) perms[6] = 'r'; + if (mode & S_IWOTH) perms[7] = 'w'; + if (mode & S_IXOTH) perms[8] = 'x'; + return perms; +} + +static inline void luaL_push_stat(lua_State *co, eio_req *req) { + struct stat *st = (struct stat *)req->ptr2; + luaL_push_string_string(co, "mode", mode2string(st->st_mode)); + luaL_push_string_integer(co, "dev", st->st_dev); + luaL_push_string_integer(co, "ino", st->st_ino); + luaL_push_string_integer(co, "nlink", st->st_nlink); + luaL_push_string_integer(co, "uid", st->st_uid); + luaL_push_string_integer(co, "gid", st->st_gid); + luaL_push_string_integer(co, "rdev", st->st_rdev); + luaL_push_string_integer(co, "access", st->st_atime); + luaL_push_string_integer(co, "change", st->st_ctime); + luaL_push_string_integer(co, "modification", st->st_mtime); + luaL_push_string_integer(co, "size", st->st_size); + luaL_push_string_integer(co, "blocks", st->st_blocks); + luaL_push_string_integer(co, "blksize", st->st_blksize); + luaL_push_string_string(co, "permissions", perm2string(st->st_mode)); +} + +static int sp[2]; + +static core_io io_watcher; + +// static int myindex = 1; + +/* AIO方法只需要简单返回状态时, 可以使用这个回调 */ +int AIO_RESPONSE(eio_req* req) { + lua_State* co = (lua_State*)req_data_to_coroutine(req); + // printf("当前线程ID为: %d, 协程状态: (%p)%d, index = %d, n = %d\n", pthread_self(), co, lua_status(co), myindex++, eio_npending()); + if (EIO_RESULT (req)){ + lua_pushboolean(co, 0); + lua_pushstring(co, strerror(req-> errorno)); + }else{ + lua_pushboolean(co, 1); + } + if (LUA_OK != CO_RESUME(co, NULL, lua_gettop(co) - 1)) { + LOG("ERROR", lua_tostring(co, -1)); + } + return 0; +} + +/* AIO调用stat是需要使用此回调 */ +int AIO_RESPONSE_STAT(eio_req* req) { + lua_State* co = (lua_State*)req_data_to_coroutine(req); + if (EIO_RESULT (req) != -1){ + lua_createtable(co, 0, 16); + luaL_push_stat(co, req); + } + if (LUA_OK != CO_RESUME(co, NULL, lua_gettop(co) - 1)) { + LOG("ERROR", lua_tostring(co, -1)); + } + return 0; +} + +/* AIO调用需要循环检查文件名称必须使用此回调 */ +int AIO_RESPONSE_DIR(eio_req* req) { + lua_State* co = (lua_State*)req_data_to_coroutine(req); + if (EIO_RESULT (req) >= 0){ + lua_createtable(co, EIO_RESULT (req), 0); + char *buf = (char *)EIO_BUF (req); + for (int i = 0; i < EIO_RESULT (req); i++) { + lua_pushlstring(co, buf, strlen(buf)); + lua_rawseti(co, -2, i + 1); + buf += strlen(buf) + 1; + } + } + if (LUA_OK != CO_RESUME(co, NULL, lua_gettop(co) - 1)) { + LOG("ERROR", lua_tostring(co, -1)); + } + return 0; +} + +/* AIO调用需要循环检查文件名称必须使用此回调 */ +int AIO_RESPONSE_PATH(eio_req* req) { + lua_State* co = (lua_State*)req_data_to_coroutine(req); + if (EIO_RESULT (req) >= 0){ + /* 文档中说明:成功后 req->result 为 req->ptr2 指针长度*/ + lua_pushlstring(co, req->ptr2, EIO_RESULT (req)); + } + if (LUA_OK != CO_RESUME(co, NULL, lua_gettop(co) - 1)) { + LOG("ERROR", lua_tostring(co, -1)); + } + return 0; +} + +static void AIO_WANT_POLL(void) { + // printf("AIO_WANT_POLL Called. 主线程ID为: %d\n", pthread_self()); + char event = '1'; + write(sp[1], &event, 1); + } + +static void AIO_DONE_POLL(void) { + // printf("AIO_DONE_POLL Called. 主线程ID为: %d\n", pthread_self()); + char event = '2'; + read(sp[0], &event, 1); +} + +static void AIO_EVENT(CORE_P_ core_io *io, int revents) { + if (revents & EV_ERROR) { + LOG("ERROR", "Recevied a core_io object internal error from libev."); + return ; + } + if (revents & EV_READ){ + /* 根据边缘触发规则, 只要还有请求则会不断检查 */ + while (eio_npending() && !eio_poll ()); + } +} + +int pip_init() { + + /* 创建管道 */ + if (-1 == socketpair(AF_LOCAL, SOCK_STREAM, 0, sp)) + return -1; + + /* 非阻塞 */ + non_blocking(sp[0]); + + /* 将写socket设置为阻塞操作, 这样能防止问题进一步扩散 */ + non_blocking(sp[1]); + + memset(&io_watcher, 0x0, sizeof(core_io)); + + core_io_init(&io_watcher, AIO_EVENT, sp[0], EV_READ); + + core_io_start(CORE_LOOP_ &io_watcher); + + return 0; + +} + +int aio_init() { + + /* 初始化eio内部数据 */ + if (eio_init(AIO_WANT_POLL, AIO_DONE_POLL)) + return -1; + + /* 创建并初始化通讯管道 */ + if (pip_init()) + return -1; + + /* 设置工作线程数量 */ + eio_set_min_parallel(AIO_MAX_NTHREADS); + eio_set_max_parallel(AIO_MAX_NTHREADS); + eio_set_max_idle(AIO_MAX_NTHREADS); + + return 0; + +} + +static int laio_truncate(lua_State* L) { + lua_State *t = lua_tothread(L, 1); + if (!t) + return luaL_error(L, "Invalid lua coroutine."); + + size_t path_size = 0; + const char *path = luaL_checklstring(L, 2, &path_size); + if (!path || path_size < 1){ + return luaL_error(L, "Invalid aio truncate [path]."); + } + + eio_truncate(path, lua_tointeger(L, 3), 0, AIO_RESPONSE, (void*)t); + return 1; +} + +/* aio.realpath 将相对路径转换为绝对路径 */ +static int laio_readpath(lua_State* L) { + + lua_State *t = lua_tothread(L, 1); + if (!t) + return luaL_error(L, "Invalid lua coroutine."); + + size_t path_size = 0; + const char *path = luaL_checklstring(L, 2, &path_size); + if (!path || path_size < 1){ + return luaL_error(L, "Invalid aio readpath [path]."); + } + eio_realpath (path, 0, AIO_RESPONSE_PATH, (void*)t); + return 1; +} + +/* aio.readdir 读取文件夹内容 */ +static int laio_readdir(lua_State* L) { + + lua_State *t = lua_tothread(L, 1); + if (!t) + return luaL_error(L, "Invalid lua coroutine."); + + size_t path_size = 0; + const char *path = luaL_checklstring(L, 2, &path_size); + if (!path || path_size < 1){ + return luaL_error(L, "Invalid aio readdir [path]."); + } + + eio_readdir (path, EIO_READDIR_DIRS_FIRST, 0, AIO_RESPONSE_DIR, (void*)t); + return 1; +} + +/* aio.rename 重命名文件/文件夹 */ +static int laio_rename(lua_State* L) { + + lua_State *t = lua_tothread(L, 1); + if (!t) + return luaL_error(L, "Invalid lua coroutine."); + + size_t old_path_size = 0; + const char *old_path = luaL_checklstring(L, 2, &old_path_size); + if (!old_path || old_path_size < 1){ + return luaL_error(L, "Invalid aio rename [old path]."); + } + + size_t new_path_size = 0; + const char *new_path = luaL_checklstring(L, 3, &new_path_size); + if (!new_path || new_path_size < 1){ + return luaL_error(L, "Invalid aio rename [new path]."); + } + + eio_rename (old_path, new_path, 0, AIO_RESPONSE, (void*)t); + return 1; +} + + +/* aio.stat 获取文件/文件夹状态 */ +static int laio_stat(lua_State* L) { + + lua_State *t = lua_tothread(L, 1); + if (!t) + return luaL_error(L, "Invalid lua coroutine."); + + size_t path_size = 0; + const char *path = luaL_checklstring(L, 2, &path_size); + if (!path || path_size < 1){ + return luaL_error(L, "Invalid aio stat [path]."); + } + + eio_stat (path, 0, AIO_RESPONSE_STAT, (void*)t); + return 1; +} + +/* aio.create 创建文件 */ +static int laio_create(lua_State* L) { + + lua_State *t = lua_tothread(L, 1); + if (!t) + return luaL_error(L, "Invalid lua coroutine."); + + size_t path_size = 0; + const char *path = luaL_checklstring(L, 2, &path_size); + if (!path || path_size < 1){ + return luaL_error(L, "Invalid aio create [path]."); + } + eio_open(path, O_CREAT, 0755, 0, AIO_RESPONSE, (void*)t); + return 1; +} + +/* aio.mkdir 创建文件夹 */ +static int laio_mkdir(lua_State* L) { + + lua_State *t = lua_tothread(L, 1); + if (!t) + return luaL_error(L, "Invalid lua coroutine."); + + size_t path_size = 0; + const char *path = luaL_checklstring(L, 2, &path_size); + if (!path || path_size < 1){ + return luaL_error(L, "Invalid aio mkdir [path]."); + } + + eio_mkdir (path, 0755, 0, AIO_RESPONSE, (void*)t); + return 1; +} + +/* aio.rmdir 删除文件夹 */ +static int laio_rmdir(lua_State* L) { + + lua_State *t = lua_tothread(L, 1); + if (!t) + return luaL_error(L, "Invalid lua coroutine."); + + size_t path_size = 0; + const char *path = luaL_checklstring(L, 2, &path_size); + if (!path || path_size < 1){ + return luaL_error(L, "Invalid aio rmdir [path]."); + } + + eio_rmdir (path, 0, AIO_RESPONSE, (void*)t); + return 1; +} + +LUAMOD_API int luaopen_laio(lua_State* L){ + // printf("主线程ID为: %d\n", pthread_self()); + luaL_checkversion(L); + if (aio_init()){ + return luaL_error(L, "aio init error."); + } + luaL_Reg aio_libs[] = { + { "mkdir", laio_mkdir }, + { "rmdir", laio_rmdir }, + { "stat", laio_stat }, + { "create", laio_create }, + { "rename", laio_rename }, + { "readdir", laio_readdir }, + { "readpath", laio_readpath }, + { "truncate", laio_truncate }, + {NULL, NULL}, + }; + luaL_newlib(L, aio_libs); + return 1; +} diff --git a/lualib/aio/init.lua b/lualib/aio/init.lua new file mode 100644 index 00000000..6ff88eb9 --- /dev/null +++ b/lualib/aio/init.lua @@ -0,0 +1,142 @@ +local laio = require "laio" + +local new_tab = require "sys".new_tab + +local cf = require "cf" +local co_new = coroutine.create +local co_self = cf.self +local co_wait = cf.wait +local co_wakeup = cf.wakeup + +local type = type +local assert = assert +local toint = math.tointeger + + +local aio = new_tab(0, 1 << 16) + +-- 创建指定文件 +function aio.create(filename) + filename = assert(type(filename) == 'string' and filename ~= '' and filename, "Invalide filename.") + local t = {} + t.current_co = co_self() + t.event_co = co_new(function ( ok, err ) + aio[t] = nil + return co_wakeup(t.current_co, ok, err) + end) + aio[t] = true + laio.create(event_co, filename) + return co_wait() +end + +-- 创建指定目录 +function aio.mkdir(dir) + dir = assert(type(dir) == 'string' and dir ~= '' and dir, "Invalid folder.") + local t = {} + t.current_co = co_self() + t.event_co = co_new(function ( ok, err ) + aio[t] = nil + return co_wakeup(t.current_co, ok, err) + end) + aio[t] = true + laio.mkdir(t.event_co, dir) + return co_wait() +end + +-- 删除指定目录 +function aio.rmdir(dir) + local t = {} + t.current_co = co_self() + t.event_co = co_new(function ( ok, err ) + aio[t] = nil + return co_wakeup(t.current_co, ok, err) + end) + aio[t] = true + laio.rmdir(t.event_co, assert(type(dir) == 'string' and dir ~= '' and dir, "Invalid folder.")) + return co_wait() +end + +-- 获取文件/目录状态 +function aio.attributes(path) + return aio.stat(path) +end + +-- 获取文件/目录状态 +function aio.stat(path) + local t = {} + t.current_co = co_self() + t.event_co = co_new(function ( list ) + aio[t] = nil + return co_wakeup(t.current_co, list) + end) + aio[t] = true + laio.stat(t.event_co, assert(type(path) == 'string' and path ~= '' and path, "Invalid path.")) + return co_wait() +end + +-- 获取目录下所有文件 +function aio.dir(path) + path = assert(type(path) == 'string' and path ~= '' and path, "Invalid path.") + local t = {} + t.current_co = co_self() + t.event_co = co_new(function ( dirs ) + aio[t] = nil + return co_wakeup(t.current_co, dirs ) + end) + aio[t] = true + laio.readdir(t.event_co, path) + return co_wait() +end + +-- 创建指定文件 +function aio.rename(old_name, new_name) + old_name = assert(type(old_name) == 'string' and old_name ~= '' and old_name, "Invalid old_name.") + new_name = assert(type(new_name) == 'string' and new_name ~= '' and new_name, "Invalid new_name.") + local t = {} + t.current_co = co_self() + t.event_co = co_new(function ( ok ) + aio[t] = nil + return co_wakeup(t.current_co, ok) + end) + aio[t] = true + laio.rename(t.event_co, old_name, new_name) + return co_wait() +end + +-- 获取当前目录完整路径 +function aio.currentdir(...) + return aio.readpath() +end + +-- 获取指定目录完整路径 +function aio.readpath(path) + local t = {} + t.current_co = co_self() + t.event_co = co_new(function ( path ) + aio[t] = nil + return co_wakeup(t.current_co, path ) + end) + aio[t] = true + if type(path) ~= 'string' or path == '' then + path = "." + end + laio.readpath(t.event_co, path) + return co_wait() +end + +-- 清空文件或者缩减文件大小. 当length为0或者nil的时候将会清空文件. +-- 注意: 这个操作是非常危险的, 您需要非常清楚自己的做什么. +function aio.truncate(filename, length) + filename = assert(type(filename) == 'string' and filename ~= '' and filename, "Invalid filename.") + local t = {} + t.current_co = co_self() + t.event_co = co_new(function ( ok, err ) + aio[t] = nil + return co_wakeup(t.current_co, ok, err) + end) + aio[t] = true + laio.truncate(t.event_co, filename, (toint(length) and toint(length) > 0) and toint(length) or 0) + return co_wait() +end + +return aio \ No newline at end of file From d04fac6ec1d497d746049eb507a25439f698b993 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 24 Mar 2020 15:29:04 +0800 Subject: [PATCH 526/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BF=BD=E7=95=A5?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 745d5cc7..ecb59e07 100644 --- a/.gitignore +++ b/.gitignore @@ -51,7 +51,7 @@ .vscode *.la *.lai -src/ev*.h +src/e*.h src/l*.h cfadmin From bb1453e4d68489fe1e59165057df73436002aa78 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 24 Mar 2020 15:29:50 +0800 Subject: [PATCH 527/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0aio=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_aio.lua | 104 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 script/test_aio.lua diff --git a/script/test_aio.lua b/script/test_aio.lua new file mode 100644 index 00000000..929051b6 --- /dev/null +++ b/script/test_aio.lua @@ -0,0 +1,104 @@ +local LOG = require "logging" +local aio = require "aio" +local cf = require "cf" + +local function run(func_name, ...) + local ret, err = aio[func_name](...) + return ret, err +end + +--[[ + + 众所周知! 线程之间是"并行执行"的. 当mkdir与rmdir运行在不同的协程后, 即使是最简单工作, 我们也不保证哪个协程先运行, 哪个任务先结束. + + 如果你需要aio库进行串行化的工作时, 请至少将aio的方法按顺序写在同一个协程当中. 这样至少能将其运行在"逻辑顺序性"的情况下. + + example_1展示就了一个错误使用示例! 它将不同的任务放在不同的协程中并让其乱序执行. 这样将打乱你原本的计划. 因为它可能正确(也可能不正确). + + 软件设计其中一项重点就是对软件设计质量的把控! 当程序运行在没有人知道会发生什么的情况下(包括作者本人), 那么这就是错误的问题思考方式. + +]] + + + +-- -- example_1 +-- for i = 1, 100 do + +-- cf.fork(function ( ... ) +-- LOG:DEBUG("开始删除: logs/" .. i) +-- print(run("rmdir", "logs/" .. i)) +-- LOG:DEBUG("结束删除: logs/" .. i) +-- end) + +-- cf.fork(function ( ... ) +-- LOG:DEBUG("开始创建: logs/" .. i) +-- print(run("mkdir", "logs/" .. i)) +-- LOG:DEBUG("结束创建") +-- end) + +-- end + + +--[[ + + 如果您已经看到这里! 说明已经接受上述建议开始尝试编写"正确"的代码! 那么如何编写逻辑正确的异步IO呢? 下面有些建议供大家参考. + + example_2示例描述了正确的使用方法, 同时也是最可能大家使用到的代码. 它绝大部分场景应该都能工作的很好, 并且不会因为文件IO导致阻塞. + + 我们可以看到, 当example_2运行在相同的协程的时候. 他们可以工作的非常好. 因为aio底层已经将异步回调代码修改为同步非阻塞(至少看起来是这样). + + 这样的编码可以让底层处于串行化工作领域范围内:"即创建完毕之后才会执行删除操作", 既保留了异步IO的能力, 也保证了逻辑正确性. + +]] + + +-- -- example_2 +-- for i = 1, 100 do +-- LOG:DEBUG("开始创建: logs/" .. i) +-- print(run("mkdir", "logs/" .. i)) +-- LOG:DEBUG("结束创建") +-- LOG:DEBUG("开始删除: logs/" .. i) +-- print(run("rmdir", "logs/" .. i)) +-- LOG:DEBUG("结束删除: logs/" .. i) +-- end + + +--[[ + + example_3在批量任务中派上了用场了! 协程与异步IO的并发使用场景就是:"让多个无相关性的任务提交, 然后统一等待任务完成的那个时刻到来." + + 这在执行批量任务的时候优势极其明显. 即使其实际运行结果中并不是按照1->2->3 ... ->100的顺序执行, 但是我们的目的是一致的. + + 因为当过程不重要的时候, 只需要保证结果的正确性即可. 就类似编译器指令重排, 虽然顺序不一致但是结果是一致的. + + 注意: example_3实现的代码一样与其他两种的场景意义不一样. + +]] + + +-- local index = 1 +-- for i = 1, 100 do +-- cf.fork(function ( ... ) +-- run("mkdir", "logs/" .. i) +-- index = index + 1 +-- if index == 100 then +-- print("所有文件夹创建完成.") +-- end +-- end) +-- end + +-- cf.sleep(3) + +-- local index = 1 +-- for i = 1, 100 do +-- cf.fork(function ( ... ) +-- run("rmdir", "logs/" .. i) +-- index = index + 1 +-- if index == 100 then +-- print("所有文件夹删除完成.") +-- end +-- end) +-- end + + +cf.wait() \ No newline at end of file From cea268c8286df62641d1c6ea3600a498909a5882 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 25 Mar 2020 01:20:03 +0800 Subject: [PATCH 528/956] =?UTF-8?q?aio=E5=BA=93=E5=8E=BB=E9=99=A4create?= =?UTF-8?q?=E6=96=B9=E6=B3=95,=E5=A2=9E=E5=8A=A0open=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E8=BF=94=E5=9B=9EAIO=20File=E5=AF=B9=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/laio.c | 190 +++++++++++++++++++++++++++++++++-------- lualib/aio/init.lua | 201 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 334 insertions(+), 57 deletions(-) diff --git a/luaclib/src/laio.c b/luaclib/src/laio.c index 5278b2e6..1f777948 100644 --- a/luaclib/src/laio.c +++ b/luaclib/src/laio.c @@ -3,6 +3,10 @@ #include #include +/* 初始化 */ +static int INITIALIZATION = 0; + +/* 工作线程最大使用堆栈 */ #define EIO_STACKSIZE (1 << 16) /* 最小线程数量 */ @@ -10,7 +14,6 @@ #define req_data_to_coroutine(req) (req->data) -#define luaL_push_string_integer(L, k, v) ({ lua_pushliteral(L, k); lua_pushinteger(L, (v)); lua_rawset(L, -3); }) #define luaL_push_string_string(L, k, v) ({ lua_pushliteral(L, k); lua_pushstring(L, (v)); lua_rawset(L, -3); }) #ifndef S_ISDIR @@ -35,7 +38,14 @@ #define S_ISBLK(mode) (0) #endif -static const char *mode2string (mode_t mode) { +static inline void luaL_push_string_integer(lua_State* L, const char* k, int v) { + lua_pushstring(L, k); + lua_pushinteger(L, v); + lua_rawset(L, -3); +} + +/* 文件类型转字符串 */ +static inline const char *mode2string (mode_t mode) { if ( S_ISREG(mode) ) return "file"; else if ( S_ISDIR(mode) ) @@ -54,7 +64,8 @@ static const char *mode2string (mode_t mode) { return "other"; } -static const char *perm2string (mode_t mode) { +/* 权限转字符串 */ +static inline const char *perm2string (mode_t mode) { static char perms[10] = "---------"; int i; for (i=0;i<9;i++) perms[i]='-'; @@ -88,19 +99,12 @@ static inline void luaL_push_stat(lua_State *co, eio_req *req) { luaL_push_string_string(co, "permissions", perm2string(st->st_mode)); } -static int sp[2]; - -static core_io io_watcher; - -// static int myindex = 1; - /* AIO方法只需要简单返回状态时, 可以使用这个回调 */ int AIO_RESPONSE(eio_req* req) { lua_State* co = (lua_State*)req_data_to_coroutine(req); - // printf("当前线程ID为: %d, 协程状态: (%p)%d, index = %d, n = %d\n", pthread_self(), co, lua_status(co), myindex++, eio_npending()); if (EIO_RESULT (req)){ lua_pushboolean(co, 0); - lua_pushstring(co, strerror(req-> errorno)); + lua_pushstring(co, strerror(req->errorno)); }else{ lua_pushboolean(co, 1); } @@ -110,6 +114,52 @@ int AIO_RESPONSE(eio_req* req) { return 0; } +/* AIO方法需要返回数值和fd时, 可以使用这个回调 */ +int AIO_RESPONSE_FD(eio_req* req) { + lua_State* co = (lua_State*)req_data_to_coroutine(req); + if (EIO_RESULT (req) == -1){ + lua_pushboolean(co, 0); + lua_pushstring(co, strerror(req->errorno)); + }else{ + lua_pushinteger(co, EIO_RESULT (req)); + } + if (LUA_OK != CO_RESUME(co, NULL, lua_gettop(co) - 1)) { + LOG("ERROR", lua_tostring(co, -1)); + } + return 0; +} + +/* AIO调用读取数据则需要使用此回调 */ +int AIO_RESPONSE_READ(eio_req* req) { + lua_State* co = (lua_State*)req_data_to_coroutine(req); + if (EIO_RESULT (req) == -1){ + lua_pushboolean(co, 0); + lua_pushstring(co, strerror(req->errorno)); + }else{ + lua_pushlstring(co, EIO_BUF (req), EIO_RESULT (req)); + lua_pushinteger(co, EIO_RESULT (req)); + } + if (LUA_OK != CO_RESUME(co, NULL, lua_gettop(co) - 1)) { + LOG("ERROR", lua_tostring(co, -1)); + } + return 0; +} + +/* AIO调用写入数据则需要使用此回调 */ +int AIO_RESPONSE_WRITE(eio_req* req) { + lua_State* co = (lua_State*)req_data_to_coroutine(req); + if (EIO_RESULT (req) == -1){ + lua_pushboolean(co, 0); + lua_pushstring(co, strerror(req->errorno)); + }else{ + lua_pushinteger(co, EIO_RESULT (req)); + } + if (LUA_OK != CO_RESUME(co, NULL, lua_gettop(co) - 1)) { + LOG("ERROR", lua_tostring(co, -1)); + } + return 0; +} + /* AIO调用stat是需要使用此回调 */ int AIO_RESPONSE_STAT(eio_req* req) { lua_State* co = (lua_State*)req_data_to_coroutine(req); @@ -154,6 +204,8 @@ int AIO_RESPONSE_PATH(eio_req* req) { return 0; } +static int sp[2]; + static void AIO_WANT_POLL(void) { // printf("AIO_WANT_POLL Called. 主线程ID为: %d\n", pthread_self()); char event = '1'; @@ -177,6 +229,8 @@ static void AIO_EVENT(CORE_P_ core_io *io, int revents) { } } +static core_io io_watcher; + int pip_init() { /* 创建管道 */ @@ -218,6 +272,77 @@ int aio_init() { } + + +/* 打开文件描述符 */ +static int laio_open(lua_State* L) { + lua_State *t = lua_tothread(L, 1); + if (!t) + return luaL_error(L, "Invalid lua coroutine."); + + size_t path_size = 0; + const char *path = luaL_checklstring(L, 2, &path_size); + if (!path || path_size < 1){ + return luaL_error(L, "Invalid aio truncate [path]."); + } + + eio_open(path, O_CREAT | O_RDWR, 0755, EIO_PRI_DEFAULT, AIO_RESPONSE_FD, (void*)t); + + return 1; +} + +/* aio.write 从文件内读取数据 */ +static int laio_read(lua_State* L) { + lua_State *t = lua_tothread(L, 1); + if (!t) + return luaL_error(L, "Invalid lua coroutine."); + + /* 当offset大于0使用pread, 否则使用read */ + eio_read(lua_tointeger(L, 2), 0, lua_tointeger(L, 3),lua_tointeger(L, 4), EIO_PRI_DEFAULT, AIO_RESPONSE_READ, (void*)t); + return 1; +} + +/* aio.write 写入数据到文件内 */ +static int laio_write(lua_State* L) { + lua_State *t = lua_tothread(L, 1); + if (!t) + return luaL_error(L, "Invalid lua coroutine."); + + int fd = lua_tointeger(L, 2); + + size_t buffer_size = 0; + const char *buffer = luaL_checklstring(L, 3, &buffer_size); + if (!buffer || buffer_size < 1){ + return luaL_error(L, "Invalid aio truncate [path]."); + } + + /* 当offset大于0使用pwrite, 否则使用pwrite */ + eio_write(fd, (void*)buffer, buffer_size, lua_tointeger(L, 4), EIO_PRI_DEFAULT, AIO_RESPONSE_WRITE, (void*)t); + return 1; +} + +/* aio.flush 将文件内存数据刷新到磁盘 */ +static int laio_flush(lua_State* L) { + lua_State *t = lua_tothread(L, 1); + if (!t) + return luaL_error(L, "Invalid lua coroutine."); + + eio_fsync(lua_tointeger(L, 2), EIO_PRI_DEFAULT, AIO_RESPONSE, (void*)t); + + return 1; +} + +/* aio.close 关闭文件描述符 */ +static int laio_close(lua_State* L) { + lua_State *t = lua_tothread(L, 1); + if (!t) + return luaL_error(L, "Invalid lua coroutine."); + + eio_close(lua_tointeger(L, 2), EIO_PRI_DEFAULT, AIO_RESPONSE, (void*)t); + + return 1; +} + static int laio_truncate(lua_State* L) { lua_State *t = lua_tothread(L, 1); if (!t) @@ -229,7 +354,7 @@ static int laio_truncate(lua_State* L) { return luaL_error(L, "Invalid aio truncate [path]."); } - eio_truncate(path, lua_tointeger(L, 3), 0, AIO_RESPONSE, (void*)t); + eio_truncate(path, lua_tointeger(L, 3), EIO_PRI_DEFAULT, AIO_RESPONSE, (void*)t); return 1; } @@ -245,7 +370,7 @@ static int laio_readpath(lua_State* L) { if (!path || path_size < 1){ return luaL_error(L, "Invalid aio readpath [path]."); } - eio_realpath (path, 0, AIO_RESPONSE_PATH, (void*)t); + eio_realpath (path, EIO_PRI_DEFAULT, AIO_RESPONSE_PATH, (void*)t); return 1; } @@ -262,7 +387,7 @@ static int laio_readdir(lua_State* L) { return luaL_error(L, "Invalid aio readdir [path]."); } - eio_readdir (path, EIO_READDIR_DIRS_FIRST, 0, AIO_RESPONSE_DIR, (void*)t); + eio_readdir (path, EIO_READDIR_DIRS_FIRST, EIO_PRI_DEFAULT, AIO_RESPONSE_DIR, (void*)t); return 1; } @@ -285,7 +410,7 @@ static int laio_rename(lua_State* L) { return luaL_error(L, "Invalid aio rename [new path]."); } - eio_rename (old_path, new_path, 0, AIO_RESPONSE, (void*)t); + eio_rename (old_path, new_path, EIO_PRI_DEFAULT, AIO_RESPONSE, (void*)t); return 1; } @@ -303,23 +428,7 @@ static int laio_stat(lua_State* L) { return luaL_error(L, "Invalid aio stat [path]."); } - eio_stat (path, 0, AIO_RESPONSE_STAT, (void*)t); - return 1; -} - -/* aio.create 创建文件 */ -static int laio_create(lua_State* L) { - - lua_State *t = lua_tothread(L, 1); - if (!t) - return luaL_error(L, "Invalid lua coroutine."); - - size_t path_size = 0; - const char *path = luaL_checklstring(L, 2, &path_size); - if (!path || path_size < 1){ - return luaL_error(L, "Invalid aio create [path]."); - } - eio_open(path, O_CREAT, 0755, 0, AIO_RESPONSE, (void*)t); + eio_stat (path, EIO_PRI_DEFAULT, AIO_RESPONSE_STAT, (void*)t); return 1; } @@ -336,7 +445,7 @@ static int laio_mkdir(lua_State* L) { return luaL_error(L, "Invalid aio mkdir [path]."); } - eio_mkdir (path, 0755, 0, AIO_RESPONSE, (void*)t); + eio_mkdir (path, 0755, EIO_PRI_DEFAULT, AIO_RESPONSE, (void*)t); return 1; } @@ -353,25 +462,32 @@ static int laio_rmdir(lua_State* L) { return luaL_error(L, "Invalid aio rmdir [path]."); } - eio_rmdir (path, 0, AIO_RESPONSE, (void*)t); + eio_rmdir (path, EIO_PRI_DEFAULT, AIO_RESPONSE, (void*)t); return 1; } LUAMOD_API int luaopen_laio(lua_State* L){ // printf("主线程ID为: %d\n", pthread_self()); luaL_checkversion(L); - if (aio_init()){ - return luaL_error(L, "aio init error."); + if (!INITIALIZATION){ + if (aio_init()){ + return luaL_error(L, "aio init error."); + } + INITIALIZATION = 1; } luaL_Reg aio_libs[] = { { "mkdir", laio_mkdir }, { "rmdir", laio_rmdir }, { "stat", laio_stat }, - { "create", laio_create }, { "rename", laio_rename }, { "readdir", laio_readdir }, { "readpath", laio_readpath }, { "truncate", laio_truncate }, + { "open", laio_open}, + { "read", laio_read }, + { "write", laio_write }, + { "flush", laio_flush }, + { "close", laio_close }, {NULL, NULL}, }; luaL_newlib(L, aio_libs); diff --git a/lualib/aio/init.lua b/lualib/aio/init.lua index 6ff88eb9..be4c39ae 100644 --- a/lualib/aio/init.lua +++ b/lualib/aio/init.lua @@ -1,6 +1,4 @@ -local laio = require "laio" - -local new_tab = require "sys".new_tab +local class = require "class" local cf = require "cf" local co_new = coroutine.create @@ -8,6 +6,21 @@ local co_self = cf.self local co_wait = cf.wait local co_wakeup = cf.wakeup +local laio = require "laio" +local aio_open = laio.open +local aio_stat = laio.stat +local aio_flush = laio.flush +local aio_read = laio.read +local aio_write = laio.write +local aio_rmdir = laio.rmdir +local aio_mkdir = laio.mkdir +local aio_rename = laio.rename +local aio_readdir = laio.readdir +local aio_readpath = laio.readpath +local aio_truncate = laio.truncate + +local new_tab = require "sys".new_tab + local type = type local assert = assert local toint = math.tointeger @@ -15,18 +28,166 @@ local toint = math.tointeger local aio = new_tab(0, 1 << 16) --- 创建指定文件 -function aio.create(filename) - filename = assert(type(filename) == 'string' and filename ~= '' and filename, "Invalide filename.") +local File = class("__AIO__") + +function File:ctor(opt) + self.fd = opt.fd + self.path = opt.path + self.stat = opt.stat + self.status = "open" +end + +-- 触发gc时检查是否回收self.fd +function File:__gc() + if self.fd and self.status == "open" then + return self:close() + end + return true +end + +-- 读取文件指定大小内容 +function File:read( bytes ) + if self.status == "close" then + return nil, "File already Closed." + end + bytes = toint(bytes) + if not bytes or bytes <= 0 then + return nil, "Invalid file read bytes." + end + assert(self.__READ__, "File:read方法不可以在多个协程中并发调用.") + if not self.read_offset then + self.read_offset = 0 + end + self.__READ__ = { current_co = co_self() } + self.__READ__.event_co = co_new(function ( data, size ) + local current_co = self.__READ__.current_co + if type(data) == 'string' then + self.read_offset = self.read_offset + size + end + self.__READ__ = nil + return co_wakeup(current_co, data, size) + end) + aio_read(self.__READ__.event_co, self.fd, bytes, self.read_offset) + return co_wait() +end + +-- 读取文件所有内容 +function File:readall() + if self.status == "close" then + return nil, "File already Closed." + end + local bytes = toint(self.stat.size) + if not bytes or bytes < 1 then + return "" + end + assert(self.__READ__, "File:readall方法不可以在多个协程中并发调用.") + self.read_offset = 0 + self.__READ__ = { current_co = co_self() } + self.__READ__.event_co = co_new(function ( data, size ) + local current_co = self.__READ__.current_co + if type(data) == 'string' then + self.read_offset = self.read_offset + size + end + self.__READ__ = nil + return co_wakeup(current_co, data, size) + end) + aio_read(self.__READ__.event_co, self.fd, bytes, self.read_offset) + return co_wait() +end + +-- 写入文件 +function File:write( data ) + if self.status == "close" then + return nil, "File already Closed." + end + assert(self.__WRITE__, "File:write方法不可以在多个协程中并发调用.") + if type(data) ~= 'string' or data == "" then + return nil, "Invalid file write data." + end + self.__WRITE__ = { current_co = co_self() } + self.__WRITE__.event_co = co_new(function ( data, err ) + local current_co = self.__WRITE__.current_co + self.__WRITE__ = nil + return co_wakeup(current_co, data, err) + end) + aio_write(self.__WRITE__.event_co, self.fd, data, self.stat.size) + self.stat.size = self.stat.size + #data + return co_wait() +end + +-- 刷新缓存 +function File:flush() + if self.status == "close" then + return nil, "File already Closed." + end + self.__FLUSH__ = { current_co = co_self() } + self.__FLUSH__.event_co = co_new(function ( ok, err ) + local current_co = self.__FLUSH__.current_co + self.__FLUSH__ = nil + return co_wakeup(current_co, ok, err) + end) + aio_flush(self.__FLUSH__.event_co, self.fd) + return co_wait() +end + +-- 清空文件 +function File:clean() + local ok, err = aio.truncate(self.path, 0) + if not ok then + return nil, err + end + local stat, err = aio.stat(self.path) + if type(stat) ~= 'table' then + return nil, err + end + self.stat = stat + return true +end + +-- 关闭文件描述符 +function File:close( ... ) + if self.status ~= "open" then + return true + end + self.__CLOSE__ = { current_co = co_self()} + self.__CLOSE__.event_co = co_new(function ( ok, err ) + local current_co = self.__CLOSE__.current_co + self.__CLOSE__ = nil + return co_wakeup(current_co, ok, err) + end) + aio_close(self.__CLOSE__.event_co, self.fd) + self.status = "closed"; self.fd = nil; + return co_wait() +end + +-- 打开文件(始终以rw模式打开, 没有则会创建) +function aio.open(filename) + filename = assert(type(filename) == 'string' and filename ~= '' and filename ~= '.' and filename ~= '..' and filename, "Invalid filename.") local t = {} t.current_co = co_self() - t.event_co = co_new(function ( ok, err ) + t.event_co = co_new(function ( fd, err) aio[t] = nil - return co_wakeup(t.current_co, ok, err) + return co_wakeup(t.current_co, fd, err) end) aio[t] = true - laio.create(event_co, filename) - return co_wait() + aio_open(t.event_co, filename) + local fd, err = co_wait() + if not fd then + return nil, err + end + local stat, err = aio.stat(filename) + if not stat then + local t = {} + t.current_co = co_self() + t.event_co = co_new(function ( ok, err ) + aio[t] = nil + return co_wakeup(t.current_co, ok, err) + end) + aio[t] = true + aio_close(t.event_co, filename) + return co_wait() + end + return File:new { fd = fd, path = filename, stat = stat } end -- 创建指定目录 @@ -39,7 +200,7 @@ function aio.mkdir(dir) return co_wakeup(t.current_co, ok, err) end) aio[t] = true - laio.mkdir(t.event_co, dir) + aio_mkdir(t.event_co, dir) return co_wait() end @@ -52,7 +213,7 @@ function aio.rmdir(dir) return co_wakeup(t.current_co, ok, err) end) aio[t] = true - laio.rmdir(t.event_co, assert(type(dir) == 'string' and dir ~= '' and dir, "Invalid folder.")) + aio_rmdir(t.event_co, assert(type(dir) == 'string' and dir ~= '' and dir, "Invalid folder.")) return co_wait() end @@ -70,7 +231,7 @@ function aio.stat(path) return co_wakeup(t.current_co, list) end) aio[t] = true - laio.stat(t.event_co, assert(type(path) == 'string' and path ~= '' and path, "Invalid path.")) + aio_stat(t.event_co, assert(type(path) == 'string' and path ~= '' and path, "Invalid path.")) return co_wait() end @@ -84,7 +245,7 @@ function aio.dir(path) return co_wakeup(t.current_co, dirs ) end) aio[t] = true - laio.readdir(t.event_co, path) + aio_readdir(t.event_co, path) return co_wait() end @@ -99,13 +260,13 @@ function aio.rename(old_name, new_name) return co_wakeup(t.current_co, ok) end) aio[t] = true - laio.rename(t.event_co, old_name, new_name) + aio_rename(t.event_co, old_name, new_name) return co_wait() end -- 获取当前目录完整路径 function aio.currentdir(...) - return aio.readpath() + return aio.readpath(".") end -- 获取指定目录完整路径 @@ -117,10 +278,10 @@ function aio.readpath(path) return co_wakeup(t.current_co, path ) end) aio[t] = true - if type(path) ~= 'string' or path == '' then - path = "." + if type(path) ~= 'string' or path == "" then + return nil, "Invalid path" end - laio.readpath(t.event_co, path) + aio_readpath(t.event_co, path) return co_wait() end @@ -135,7 +296,7 @@ function aio.truncate(filename, length) return co_wakeup(t.current_co, ok, err) end) aio[t] = true - laio.truncate(t.event_co, filename, (toint(length) and toint(length) > 0) and toint(length) or 0) + aio_truncate(t.event_co, filename, (toint(length) and toint(length) > 0) and toint(length) or 0) return co_wait() end From ab687017187156db115c61dd074290081449fd78 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 25 Mar 2020 01:23:44 +0800 Subject: [PATCH 529/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/aio/init.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lualib/aio/init.lua b/lualib/aio/init.lua index be4c39ae..8afe409c 100644 --- a/lualib/aio/init.lua +++ b/lualib/aio/init.lua @@ -140,6 +140,9 @@ function File:clean() if type(stat) ~= 'table' then return nil, err end + if toint(self.read_offset) then + self.read_offset = 0 + end self.stat = stat return true end From 449ae40306151e20116e4e6b46fcd4470f194ce6 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 25 Mar 2020 01:30:47 +0800 Subject: [PATCH 530/956] =?UTF-8?q?=E4=B8=BAaio=E5=BA=93=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=B9=B6=E5=8F=91=E9=99=90=E5=88=B6=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/aio/init.lua | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/lualib/aio/init.lua b/lualib/aio/init.lua index 8afe409c..1469ae95 100644 --- a/lualib/aio/init.lua +++ b/lualib/aio/init.lua @@ -12,6 +12,7 @@ local aio_stat = laio.stat local aio_flush = laio.flush local aio_read = laio.read local aio_write = laio.write +local aio_close = laio.close local aio_rmdir = laio.rmdir local aio_mkdir = laio.mkdir local aio_rename = laio.rename @@ -80,7 +81,7 @@ function File:readall() if not bytes or bytes < 1 then return "" end - assert(self.__READ__, "File:readall方法不可以在多个协程中并发调用.") + assert(not self.__READ__, "File:readall方法不可以在多个协程中并发调用.") self.read_offset = 0 self.__READ__ = { current_co = co_self() } self.__READ__.event_co = co_new(function ( data, size ) @@ -100,7 +101,7 @@ function File:write( data ) if self.status == "close" then return nil, "File already Closed." end - assert(self.__WRITE__, "File:write方法不可以在多个协程中并发调用.") + assert(not self.__WRITE__, "File:write方法不可以在多个协程中并发调用.") if type(data) ~= 'string' or data == "" then return nil, "Invalid file write data." end @@ -120,6 +121,7 @@ function File:flush() if self.status == "close" then return nil, "File already Closed." end + assert(not self.__FLUSH__, "File:flush方法不可以在多个协程中并发调用.") self.__FLUSH__ = { current_co = co_self() } self.__FLUSH__.event_co = co_new(function ( ok, err ) local current_co = self.__FLUSH__.current_co @@ -132,25 +134,32 @@ end -- 清空文件 function File:clean() + if self.status == "close" then + return nil, "File already Closed." + end + self.__CLEAN__ = assert(not self.__CLEAN__ and true, "File:flush方法不可以在多个协程中并发调用.") local ok, err = aio.truncate(self.path, 0) if not ok then + self.__CLEAN__ = nil return nil, err end local stat, err = aio.stat(self.path) if type(stat) ~= 'table' then + self.__CLEAN__ = nil return nil, err end if toint(self.read_offset) then self.read_offset = 0 end self.stat = stat + self.__CLEAN__ = nil return true end -- 关闭文件描述符 function File:close( ... ) - if self.status ~= "open" then - return true + if self.status == "close" then + return nil, "File already Closed." end self.__CLOSE__ = { current_co = co_self()} self.__CLOSE__.event_co = co_new(function ( ok, err ) From ba94f95f4c44bde1877d5ab189b96c3e87510080 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 25 Mar 2020 01:37:31 +0800 Subject: [PATCH 531/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Daio.stat=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E8=BF=94=E5=9B=9E=E9=94=99=E8=AF=AF=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/laio.c | 3 +++ lualib/aio/init.lua | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/luaclib/src/laio.c b/luaclib/src/laio.c index 1f777948..9a631e88 100644 --- a/luaclib/src/laio.c +++ b/luaclib/src/laio.c @@ -166,6 +166,9 @@ int AIO_RESPONSE_STAT(eio_req* req) { if (EIO_RESULT (req) != -1){ lua_createtable(co, 0, 16); luaL_push_stat(co, req); + }else{ + lua_pushboolean(co, 0); + lua_pushstring(co, strerror(req->errorno)); } if (LUA_OK != CO_RESUME(co, NULL, lua_gettop(co) - 1)) { LOG("ERROR", lua_tostring(co, -1)); diff --git a/lualib/aio/init.lua b/lualib/aio/init.lua index 1469ae95..49e21c6f 100644 --- a/lualib/aio/init.lua +++ b/lualib/aio/init.lua @@ -238,9 +238,9 @@ end function aio.stat(path) local t = {} t.current_co = co_self() - t.event_co = co_new(function ( list ) + t.event_co = co_new(function ( list, err ) aio[t] = nil - return co_wakeup(t.current_co, list) + return co_wakeup(t.current_co, list, err) end) aio[t] = true aio_stat(t.event_co, assert(type(path) == 'string' and path ~= '' and path, "Invalid path.")) From ad09bebf7b40360acaadb0f65bd8ae4daeb65d4e Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 25 Mar 2020 10:16:10 +0800 Subject: [PATCH 532/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E7=BC=96=E8=AF=91=E5=99=A8=E5=85=BC=E5=AE=B9=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/laio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/luaclib/src/laio.c b/luaclib/src/laio.c index 9a631e88..902ad206 100644 --- a/luaclib/src/laio.c +++ b/luaclib/src/laio.c @@ -182,7 +182,8 @@ int AIO_RESPONSE_DIR(eio_req* req) { if (EIO_RESULT (req) >= 0){ lua_createtable(co, EIO_RESULT (req), 0); char *buf = (char *)EIO_BUF (req); - for (int i = 0; i < EIO_RESULT (req); i++) { + int i + for (i = 0; i < EIO_RESULT (req); i++) { lua_pushlstring(co, buf, strlen(buf)); lua_rawseti(co, -2, i + 1); buf += strlen(buf) + 1; From 1ffe783ad70a71525a5299640eece27fbc2488dd Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 25 Mar 2020 10:16:54 +0800 Subject: [PATCH 533/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E7=BC=96=E8=AF=91=E5=99=A8=E5=85=BC=E5=AE=B9=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/laio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/luaclib/src/laio.c b/luaclib/src/laio.c index 902ad206..99fd766c 100644 --- a/luaclib/src/laio.c +++ b/luaclib/src/laio.c @@ -182,7 +182,7 @@ int AIO_RESPONSE_DIR(eio_req* req) { if (EIO_RESULT (req) >= 0){ lua_createtable(co, EIO_RESULT (req), 0); char *buf = (char *)EIO_BUF (req); - int i + int i; for (i = 0; i < EIO_RESULT (req); i++) { lua_pushlstring(co, buf, strlen(buf)); lua_rawseti(co, -2, i + 1); From c2ddb1fa97b9681c289fe712cd40c417548643cf Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 25 Mar 2020 10:36:08 +0800 Subject: [PATCH 534/956] =?UTF-8?q?aio=E5=88=9D=E5=A7=8B=E5=8C=96=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E4=BF=AE=E6=94=B9=E4=B8=BA=E9=9D=99=E6=80=81=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/laio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/luaclib/src/laio.c b/luaclib/src/laio.c index 99fd766c..3cbf9ef7 100644 --- a/luaclib/src/laio.c +++ b/luaclib/src/laio.c @@ -235,7 +235,7 @@ static void AIO_EVENT(CORE_P_ core_io *io, int revents) { static core_io io_watcher; -int pip_init() { +static int pip_init() { /* 创建管道 */ if (-1 == socketpair(AF_LOCAL, SOCK_STREAM, 0, sp)) @@ -257,7 +257,7 @@ int pip_init() { } -int aio_init() { +static int aio_init() { /* 初始化eio内部数据 */ if (eio_init(AIO_WANT_POLL, AIO_DONE_POLL)) From 58f7169787b82141fbcf77ba1ce9a15a87a1b940 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 25 Mar 2020 12:13:46 +0800 Subject: [PATCH 535/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/luaclib/Makefile b/luaclib/Makefile index e777399d..5b97b49f 100644 --- a/luaclib/Makefile +++ b/luaclib/Makefile @@ -9,9 +9,9 @@ default : INCLUDES += -I../src -I/usr/local/include LIBS += -L./ -L../ -L/usr/local/lib -# CFLAGS = -Wall -O3 -fPIC --shared -DJEMALLOC -ljemalloc -# CFLAGS = -Wall -O3 -fPIC --shared -DTCMALLOC -ltcmalloc -CFLAGS = -Wall -O3 -fPIC --shared +# CFLAGS = -Wall -O3 -fPIC --shared -DJEMALLOC -ljemalloc -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib +# CFLAGS = -Wall -O3 -fPIC --shared -DTCMALLOC -ltcmalloc -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib +CFLAGS = -Wall -O3 -fPIC --shared -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib internal : From f6e14a01137dad9f144ec62134c6c352b31424e2 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 25 Mar 2020 12:58:14 +0800 Subject: [PATCH 536/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Duuid.c=E8=AD=A6?= =?UTF-8?q?=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/uuid.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/luaclib/src/lcrypt/uuid.c b/luaclib/src/lcrypt/uuid.c index 801329c7..e9aac8d2 100644 --- a/luaclib/src/lcrypt/uuid.c +++ b/luaclib/src/lcrypt/uuid.c @@ -21,7 +21,7 @@ static inline int uuid_v4_gen(char *buffer) uuid.clk_seq_hi_res = (uint8_t) ((uuid.clk_seq_hi_res & 0x3F) | 0x80); uuid.time_hi_and_version = (uint16_t) ((uuid.time_hi_and_version & 0x0FFF) | 0x4000); - snprintf(buffer, UUID_V4_LENGTH, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + snprintf(buffer, UUID_V4_LENGTH + 1, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, uuid.clk_seq_hi_res, uuid.clk_seq_low, uuid.node[0], uuid.node[1], uuid.node[2], @@ -32,9 +32,8 @@ static inline int uuid_v4_gen(char *buffer) } int luuid(lua_State *L) { - luaL_Buffer b; - char *UUID = luaL_buffinitsize(L, &b, UUID_V4_LENGTH); + char* UUID = lua_newuserdata(L, UUID_V4_LENGTH + 1); uuid_v4_gen(UUID); - luaL_pushresultsize(&b, UUID_V4_LENGTH); + lua_pushlstring(L, UUID, UUID_V4_LENGTH); return 1; } \ No newline at end of file From 353e8fdc29d9e744d55f0e9be2777863d6c46d1b Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 25 Mar 2020 13:15:05 +0800 Subject: [PATCH 537/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Daio=E6=A0=87=E5=BF=97?= =?UTF-8?q?=E4=BD=8D=E5=88=A4=E6=96=AD=E4=B8=8D=E5=87=86=E7=A1=AE=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/aio/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lualib/aio/init.lua b/lualib/aio/init.lua index 49e21c6f..921ad37c 100644 --- a/lualib/aio/init.lua +++ b/lualib/aio/init.lua @@ -55,7 +55,7 @@ function File:read( bytes ) if not bytes or bytes <= 0 then return nil, "Invalid file read bytes." end - assert(self.__READ__, "File:read方法不可以在多个协程中并发调用.") + assert(not self.__READ__, "File:read方法不可以在多个协程中并发调用.") if not self.read_offset then self.read_offset = 0 end @@ -137,7 +137,7 @@ function File:clean() if self.status == "close" then return nil, "File already Closed." end - self.__CLEAN__ = assert(not self.__CLEAN__ and true, "File:flush方法不可以在多个协程中并发调用.") + self.__CLEAN__ = assert(not self.__CLEAN__, "File:clean方法不可以在多个协程中并发调用.") local ok, err = aio.truncate(self.path, 0) if not ok then self.__CLEAN__ = nil From bbdfb7bc36bd50883bb200d392125cce519c8adf Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 26 Mar 2020 01:07:42 +0800 Subject: [PATCH 538/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=88=A4=E6=96=AD=E6=9D=A5=E5=87=8F=E5=B0=91aio=E5=87=BA?= =?UTF-8?q?=E9=94=99=E7=9A=84=E5=8F=AF=E8=83=BD=E6=80=A7=E4=B8=8E=E5=B0=BD?= =?UTF-8?q?=E6=97=A9=E6=8A=9B=E5=87=BA=E9=94=99=E8=AF=AF=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/laio.c | 24 ++++++++++++------------ lualib/aio/init.lua | 12 +++++++++++- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/luaclib/src/laio.c b/luaclib/src/laio.c index 3cbf9ef7..2b9af3c3 100644 --- a/luaclib/src/laio.c +++ b/luaclib/src/laio.c @@ -1,14 +1,14 @@ #define LUA_LIB +/* 工作线程最大使用堆栈 */ +#define EIO_STACKSIZE (1 << 16) + #include #include /* 初始化 */ static int INITIALIZATION = 0; -/* 工作线程最大使用堆栈 */ -#define EIO_STACKSIZE (1 << 16) - /* 最小线程数量 */ #define AIO_MAX_NTHREADS 8 @@ -100,7 +100,7 @@ static inline void luaL_push_stat(lua_State *co, eio_req *req) { } /* AIO方法只需要简单返回状态时, 可以使用这个回调 */ -int AIO_RESPONSE(eio_req* req) { +static int AIO_RESPONSE(eio_req* req) { lua_State* co = (lua_State*)req_data_to_coroutine(req); if (EIO_RESULT (req)){ lua_pushboolean(co, 0); @@ -115,7 +115,7 @@ int AIO_RESPONSE(eio_req* req) { } /* AIO方法需要返回数值和fd时, 可以使用这个回调 */ -int AIO_RESPONSE_FD(eio_req* req) { +static int AIO_RESPONSE_FD(eio_req* req) { lua_State* co = (lua_State*)req_data_to_coroutine(req); if (EIO_RESULT (req) == -1){ lua_pushboolean(co, 0); @@ -130,7 +130,7 @@ int AIO_RESPONSE_FD(eio_req* req) { } /* AIO调用读取数据则需要使用此回调 */ -int AIO_RESPONSE_READ(eio_req* req) { +static int AIO_RESPONSE_READ(eio_req* req) { lua_State* co = (lua_State*)req_data_to_coroutine(req); if (EIO_RESULT (req) == -1){ lua_pushboolean(co, 0); @@ -146,7 +146,7 @@ int AIO_RESPONSE_READ(eio_req* req) { } /* AIO调用写入数据则需要使用此回调 */ -int AIO_RESPONSE_WRITE(eio_req* req) { +static int AIO_RESPONSE_WRITE(eio_req* req) { lua_State* co = (lua_State*)req_data_to_coroutine(req); if (EIO_RESULT (req) == -1){ lua_pushboolean(co, 0); @@ -161,7 +161,7 @@ int AIO_RESPONSE_WRITE(eio_req* req) { } /* AIO调用stat是需要使用此回调 */ -int AIO_RESPONSE_STAT(eio_req* req) { +static int AIO_RESPONSE_STAT(eio_req* req) { lua_State* co = (lua_State*)req_data_to_coroutine(req); if (EIO_RESULT (req) != -1){ lua_createtable(co, 0, 16); @@ -177,7 +177,7 @@ int AIO_RESPONSE_STAT(eio_req* req) { } /* AIO调用需要循环检查文件名称必须使用此回调 */ -int AIO_RESPONSE_DIR(eio_req* req) { +static int AIO_RESPONSE_DIR(eio_req* req) { lua_State* co = (lua_State*)req_data_to_coroutine(req); if (EIO_RESULT (req) >= 0){ lua_createtable(co, EIO_RESULT (req), 0); @@ -211,7 +211,7 @@ int AIO_RESPONSE_PATH(eio_req* req) { static int sp[2]; static void AIO_WANT_POLL(void) { - // printf("AIO_WANT_POLL Called. 主线程ID为: %d\n", pthread_self()); + // printf("AIO_WANT_POLL Called. 工作线程ID为: %d\n", pthread_self()); char event = '1'; write(sp[1], &event, 1); } @@ -278,7 +278,7 @@ static int aio_init() { -/* 打开文件描述符 */ +/* aio.open 打开文件描述符 */ static int laio_open(lua_State* L) { lua_State *t = lua_tothread(L, 1); if (!t) @@ -295,7 +295,7 @@ static int laio_open(lua_State* L) { return 1; } -/* aio.write 从文件内读取数据 */ +/* aio.read 从文件内读取数据 */ static int laio_read(lua_State* L) { lua_State *t = lua_tothread(L, 1); if (!t) diff --git a/lualib/aio/init.lua b/lualib/aio/init.lua index 921ad37c..b0b41a15 100644 --- a/lualib/aio/init.lua +++ b/lualib/aio/init.lua @@ -82,7 +82,17 @@ function File:readall() return "" end assert(not self.__READ__, "File:readall方法不可以在多个协程中并发调用.") - self.read_offset = 0 + if not self.read_offset then + self.read_offset = 0 -- 如果没有读取过, 则一次性全部读取完毕. + else + -- 如果调用这个之前有调用过read, 那么将使用read_offset将之后的字节全部读取出来 + -- 如果已经读到末尾, 则直接返回空字符串并且调整read_offset确保一致性. + bytes = bytes > self.read_offset and bytes - self.read_offset or 0 + if bytes == 0 then + self.read_offset = self.stat.size + return "" + end + end self.__READ__ = { current_co = co_self() } self.__READ__.event_co = co_new(function ( data, size ) local current_co = self.__READ__.current_co From 57b21a240a39e3534a3eb529bc50c9764cd7d8c9 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 26 Mar 2020 01:40:50 +0800 Subject: [PATCH 539/956] =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E5=86=85=E6=A0=B8=E5=AE=8F=E7=9A=84=E8=A1=A5=E5=85=85=E4=B8=8E?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/laio.c | 116 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 91 insertions(+), 25 deletions(-) diff --git a/luaclib/src/laio.c b/luaclib/src/laio.c index 2b9af3c3..ac34becc 100644 --- a/luaclib/src/laio.c +++ b/luaclib/src/laio.c @@ -16,27 +16,93 @@ static int INITIALIZATION = 0; #define luaL_push_string_string(L, k, v) ({ lua_pushliteral(L, k); lua_pushstring(L, (v)); lua_rawset(L, -3); }) - #ifndef S_ISDIR - #define S_ISDIR(mode) (mode & _S_IFDIR) - #endif - #ifndef S_ISREG - #define S_ISREG(mode) (mode & _S_IFREG) - #endif - #ifndef S_ISLNK - #define S_ISLNK(mode) (0) - #endif - #ifndef S_ISSOCK - #define S_ISSOCK(mode) (0) - #endif - #ifndef S_ISFIFO - #define S_ISFIFO(mode) (0) - #endif - #ifndef S_ISCHR - #define S_ISCHR(mode) (mode&_S_IFCHR) - #endif - #ifndef S_ISBLK - #define S_ISBLK(mode) (0) - #endif +/* --- 文件(夹)类型 --- */ + +/* 内核宏 判断类型是否为目录 */ +#ifndef S_ISDIR + #define S_ISDIR(mode) (mode & _S_IFDIR) +#endif + +/* 内核宏 判断类型是否为常规文件 */ +#ifndef S_ISREG + #define S_ISREG(mode) (mode & _S_IFREG) +#endif + +/* 内核宏 判断是否为链接 */ +#ifndef S_ISLNK + #define S_ISLNK(mode) (0) +#endif + +/* 内核宏 判断是否为域套接字 */ +#ifndef S_ISSOCK + #define S_ISSOCK(mode) (0) +#endif + +/* 内核宏 判断是否为命名管道 */ +#ifndef S_ISFIFO + #define S_ISFIFO(mode) (0) +#endif + +/* 内核宏 判断是否为字符设备 */ +#ifndef S_ISCHR + #define S_ISCHR(mode) (mode & _S_IFCHR) +#endif + +/* 内核宏 判断是否为块设备 */ +#ifndef S_ISBLK + #define S_ISBLK(mode) (0) +#endif + +/* --- 文件(夹)类型 --- */ + +/* --- 文件(夹)权限 --- */ + +/* 拥有者是否有读权限 */ +#ifndef S_IRUSR + #define S_IRUSR (1 << 8) +#endif + +/* 拥有者是否有写权限 */ +#ifndef S_IWUSR + #define S_IWUSR (1 << 7) +#endif + +/* 拥有者是否有执行权限 */ +#ifndef S_IXUSR + #define S_IXUSR (1 << 6) +#endif + +/* 用户组是否有读权限 */ +#ifndef S_IRGRP + #define S_IRGRP (1 << 5) +#endif + +/* 用户组是否有写权限 */ +#ifndef S_IWGRP + #define S_IWGRP (1 << 4) +#endif + +/* 用户组是否有执行权限 */ +#ifndef S_IXGRP + #define S_IXGRP (1 << 3) +#endif + +/* 其他人是否有读权限 */ +#ifndef S_IROTH + #define S_IROTH (1 << 2) +#endif + +/* 其他人是否有写权限 */ +#ifndef S_IWOTH + #define S_IWOTH (1 << 1) +#endif + +/* 其他人是否有执行权限 */ +#ifndef S_IXOTH + #define S_IXOTH (1 << 0) +#endif + +/* --- 文件(夹)权限 --- */ static inline void luaL_push_string_integer(lua_State* L, const char* k, int v) { lua_pushstring(L, k); @@ -65,8 +131,7 @@ static inline const char *mode2string (mode_t mode) { } /* 权限转字符串 */ -static inline const char *perm2string (mode_t mode) { - static char perms[10] = "---------"; +static inline const char *perm2string (mode_t mode, char* perms) { int i; for (i=0;i<9;i++) perms[i]='-'; if (mode & S_IRUSR) perms[0] = 'r'; @@ -82,6 +147,7 @@ static inline const char *perm2string (mode_t mode) { } static inline void luaL_push_stat(lua_State *co, eio_req *req) { + char permissions[10] = "---------"; struct stat *st = (struct stat *)req->ptr2; luaL_push_string_string(co, "mode", mode2string(st->st_mode)); luaL_push_string_integer(co, "dev", st->st_dev); @@ -96,7 +162,7 @@ static inline void luaL_push_stat(lua_State *co, eio_req *req) { luaL_push_string_integer(co, "size", st->st_size); luaL_push_string_integer(co, "blocks", st->st_blocks); luaL_push_string_integer(co, "blksize", st->st_blksize); - luaL_push_string_string(co, "permissions", perm2string(st->st_mode)); + luaL_push_string_string(co, "permissions", perm2string(st->st_mode, permissions)); } /* AIO方法只需要简单返回状态时, 可以使用这个回调 */ @@ -320,7 +386,7 @@ static int laio_write(lua_State* L) { return luaL_error(L, "Invalid aio truncate [path]."); } - /* 当offset大于0使用pwrite, 否则使用pwrite */ + /* 当offset大于等于0使用pwrite, 否则使用write */ eio_write(fd, (void*)buffer, buffer_size, lua_tointeger(L, 4), EIO_PRI_DEFAULT, AIO_RESPONSE_WRITE, (void*)t); return 1; } From 4bec297e3539c59712eace62a7dcb0e816a5511d Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 26 Mar 2020 15:49:11 +0800 Subject: [PATCH 540/956] =?UTF-8?q?=E5=B0=86sendfile=E6=97=B6=E6=89=8D?= =?UTF-8?q?=E4=BC=9A=E5=88=9B=E5=BB=BA/=E5=85=B3=E9=97=AD=E7=9A=84?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=8F=8F=E8=BF=B0=E7=AC=A6(fd)=E4=BA=A4?= =?UTF-8?q?=E7=94=B1aio=E5=BA=93=E6=9D=A5=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 17 +++++------------ lualib/aio/init.lua | 30 +++++++++++++++++++++++++++++- lualib/internal/TCP.lua | 15 +++++++++++++-- 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 87a19701..26ff6e42 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -399,28 +399,21 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ LOG("ERROR", lua_tostring(sf->L, -1)); LOG("ERROR", "Error Lua SENDFILE Method"); } - close(sf->fd); xfree(sf); + xfree(sf); } } static int tcp_sendfile(lua_State *L){ core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); - lua_State *t = lua_tothread(L, 2); - const char* path = luaL_checkstring(L, 3); - lua_Integer iofd = luaL_checkinteger(L, 4); - lua_Integer offset = luaL_checkinteger(L, 5); - - int fd = open(path, O_RDONLY); - if (0 > fd) return luaL_error(L, strerror(errno)); struct io_sendfile *sf = xmalloc(sizeof(struct io_sendfile)); + sf->L = lua_tothread(L, 2); + sf->fd = luaL_checkinteger(L, 3); + sf->offset = luaL_checkinteger(L, 5); sf->pos = 0; - sf->fd = fd; - sf->offset = offset; - sf->L = t; core_set_watcher_userdata(io, sf); - core_io_init(io, IO_SENDFILE, iofd, EV_WRITE); + core_io_init(io, IO_SENDFILE, luaL_checkinteger(L, 4), EV_WRITE); core_io_start(CORE_LOOP_ io); return 1; } diff --git a/lualib/aio/init.lua b/lualib/aio/init.lua index b0b41a15..6693f936 100644 --- a/lualib/aio/init.lua +++ b/lualib/aio/init.lua @@ -206,12 +206,40 @@ function aio.open(filename) return co_wakeup(t.current_co, ok, err) end) aio[t] = true - aio_close(t.event_co, filename) + aio_close(t.event_co, fd) return co_wait() end return File:new { fd = fd, path = filename, stat = stat } end +-- 仅返回fd +function aio._open(filename) + filename = assert(type(filename) == 'string' and filename ~= '' and filename ~= '.' and filename ~= '..' and filename, "Invalid filename.") + local t = {} + t.current_co = co_self() + t.event_co = co_new(function ( fd, err) + aio[t] = nil + return co_wakeup(t.current_co, fd, err) + end) + aio[t] = true + aio_open(t.event_co, filename) + return co_wait() +end + +-- 仅关闭fd +function aio._close(fd) + fd = assert(toint(fd) and toint(fd) >= 0 and toint(fd), "Invalid fd.") + local t = {} + t.current_co = co_self() + t.event_co = co_new(function ( ok, err ) + aio[t] = nil + return co_wakeup(t.current_co, ok, err) + end) + aio[t] = true + aio_close(t.event_co, fd) + return co_wait() +end + -- 创建指定目录 function aio.mkdir(dir) dir = assert(type(dir) == 'string' and dir ~= '' and dir, "Invalid folder.") diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index 1a9d1c2c..e0d0cb25 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -22,6 +22,10 @@ local co_wait = coroutine.yield local ti = require "internal.Timer" local ti_timeout = ti.timeout +local aio = require "aio" +local aio_close = aio._close +local aio_open = aio._open + local tcp = require "tcp" local tcp_new = tcp.new local tcp_start = tcp.start @@ -88,8 +92,13 @@ function TCP:set_backlog(backlog) return self end +-- sendfile的文件fd使用aio库来打开与关闭可以减少阻塞. function TCP:sendfile (filename, offset) if type(filename) == 'string' and filename ~= '' then + local fd, err = aio_open(filename) + if not fd then + return nil, err + end local co = co_self() self.SEND_IO = tcp_pop() self.sendfile_current_co = co_self() @@ -101,8 +110,10 @@ function TCP:sendfile (filename, offset) self.sendfile_current_co = nil return co_wakeup(co, ok) end) - tcp_sendfile(self.SEND_IO, self.sendfile_co, filename, self.fd, offset or 4096) - return co_wait() + tcp_sendfile(self.SEND_IO, self.sendfile_co, fd, self.fd, offset or 65535) + local ok = co_wait() + aio_close(fd) + return ok end end From fd4661b05808f5d7e422b92ea234bd685690bd2a Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 26 Mar 2020 16:07:59 +0800 Subject: [PATCH 541/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9httpd=E5=BA=93?= =?UTF-8?q?=E7=9A=84=E9=9D=99=E6=80=81=E6=96=87=E4=BB=B6=E6=9F=A5=E6=89=BE?= =?UTF-8?q?=E6=97=B6,=20=E5=AF=B9=E6=96=87=E4=BB=B6=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=E5=88=A4=E6=96=AD=E9=87=87=E7=94=A8aio=E5=BA=93=E5=AE=8C?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Router.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index af6cb44d..7792c7e0 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -1,6 +1,9 @@ local log = require "logging" local Log = log:new({dump = true, path = 'httpd-Router'}) +local aio = require "aio" +local aio_stat = aio.stat + local url = require "url" local url_decode = url.decode -- local url_encode = url.encode @@ -20,7 +23,7 @@ local next = next local ipairs = ipairs local tonumber = tonumber local tostring = tostring -local io_open = io.open +-- local io_open = io.open local slash = '\x2f' -- '/' local slash2 = '\x2f\x2f' -- '//' @@ -111,16 +114,13 @@ local function find_route (method, path) return end if not load_file then - load_file = function (path) + load_file = function ( path ) local filepath = prefix .. url_decode(path) - -- 使用r+测试是否可读可写; 如果filepath是目录则无法被打开, 但单独的r模式可以. - local f, _ = io_open(filepath, 'r+') - if not f then + local stat = aio_stat(filepath) + if type(stat) ~= 'table' or stat.mode ~= 'file' then return end - local body_len = f:seek('end') - f:close() - return body_len, filepath, match(path, '.+%.([%a]+)') + return stat.size, filepath, match(path, '.+%.([%a]+)') end end return load_file, typ From b8b800efc5da4c6137fc0011a351529b2e8f0a2e Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 26 Mar 2020 16:19:42 +0800 Subject: [PATCH 542/956] =?UTF-8?q?=E5=B0=86template.lua=E7=9A=84=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E4=BD=BF=E7=94=A8aio=E5=BA=93=E6=9D=A5=E5=AE=8C?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/template.lua | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/lualib/template.lua b/lualib/template.lua index 79378113..fed434ea 100644 --- a/lualib/template.lua +++ b/lualib/template.lua @@ -1,3 +1,6 @@ +local aio = require "aio" +local aio_open = aio.open + local setmetatable = setmetatable local loadstring = loadstring local loadchunk @@ -11,7 +14,6 @@ local prefix local write = io.write local pcall = pcall local phase -local open = io.open local load = load local type = type local dump = string.dump @@ -92,9 +94,11 @@ local function escaped(view, s) end local function readfile(path) - local file = open(path, "rb") - if not file then return nil end - local content = file:read "*a" + local file = aio_open(path) + if not file then + return nil + end + local content = file:readall() file:close() return content end @@ -226,9 +230,9 @@ end function template.precompile(view, path, strip) local chunk = dump(template.compile(view), strip ~= false) if path then - local file = open(path, "wb") - file:write(chunk) - file:close() + local file = aio_open(path) + file:write(chunk) + file:close() end return chunk end From 6e10efa70888ee3c4f81a733150ac4b539bde5dc Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 26 Mar 2020 16:23:40 +0800 Subject: [PATCH 543/956] =?UTF-8?q?=E4=B8=80=E4=BA=9B=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E7=B2=BE=E7=AE=80=E4=B8=8E=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/crypt/init.lua | 1 - lualib/json.lua | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index d5c1fa74..a3a99a16 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -1,5 +1,4 @@ local CRYPT = require "lcrypt" -local new_tab = require("sys").new_tab local uuid = CRYPT.uuid diff --git a/lualib/json.lua b/lualib/json.lua index 940f5de5..c640e8b5 100644 --- a/lualib/json.lua +++ b/lualib/json.lua @@ -1,5 +1,8 @@ local cjson = require "cjson" +local pcall = pcall +local setmetatable = setmetatable + local cjson_encode = cjson.encode local cjson_decode = cjson.decode local cjson_array_mt = cjson.array_mt From 5d7beca6587374cc82f9c1da6625a2541d78b8c6 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 26 Mar 2020 17:00:47 +0800 Subject: [PATCH 544/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Daio=E5=BA=93=E7=9A=84?= =?UTF-8?q?close=E5=88=A4=E6=96=AD=E9=97=AE=E9=A2=98=E4=B8=8E=E7=B2=BE?= =?UTF-8?q?=E7=AE=80=E4=B8=80=E4=BA=9B=E6=97=A0=E7=94=A8=E7=9A=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/aio/init.lua | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/lualib/aio/init.lua b/lualib/aio/init.lua index 6693f936..d2e2eecc 100644 --- a/lualib/aio/init.lua +++ b/lualib/aio/init.lua @@ -48,7 +48,7 @@ end -- 读取文件指定大小内容 function File:read( bytes ) - if self.status == "close" then + if self.status == "closed" then return nil, "File already Closed." end bytes = toint(bytes) @@ -74,7 +74,7 @@ end -- 读取文件所有内容 function File:readall() - if self.status == "close" then + if self.status == "closed" then return nil, "File already Closed." end local bytes = toint(self.stat.size) @@ -108,7 +108,7 @@ end -- 写入文件 function File:write( data ) - if self.status == "close" then + if self.status == "closed" then return nil, "File already Closed." end assert(not self.__WRITE__, "File:write方法不可以在多个协程中并发调用.") @@ -128,7 +128,7 @@ end -- 刷新缓存 function File:flush() - if self.status == "close" then + if self.status == "closed" then return nil, "File already Closed." end assert(not self.__FLUSH__, "File:flush方法不可以在多个协程中并发调用.") @@ -144,7 +144,7 @@ end -- 清空文件 function File:clean() - if self.status == "close" then + if self.status == "closed" then return nil, "File already Closed." end self.__CLEAN__ = assert(not self.__CLEAN__, "File:clean方法不可以在多个协程中并发调用.") @@ -168,32 +168,18 @@ end -- 关闭文件描述符 function File:close( ... ) - if self.status == "close" then + if self.status == "closed" then return nil, "File already Closed." end - self.__CLOSE__ = { current_co = co_self()} - self.__CLOSE__.event_co = co_new(function ( ok, err ) - local current_co = self.__CLOSE__.current_co - self.__CLOSE__ = nil - return co_wakeup(current_co, ok, err) - end) - aio_close(self.__CLOSE__.event_co, self.fd) - self.status = "closed"; self.fd = nil; - return co_wait() + local fd = self.fd + self.fd = nil + self.status = "closed" + return aio._close(fd) end -- 打开文件(始终以rw模式打开, 没有则会创建) function aio.open(filename) - filename = assert(type(filename) == 'string' and filename ~= '' and filename ~= '.' and filename ~= '..' and filename, "Invalid filename.") - local t = {} - t.current_co = co_self() - t.event_co = co_new(function ( fd, err) - aio[t] = nil - return co_wakeup(t.current_co, fd, err) - end) - aio[t] = true - aio_open(t.event_co, filename) - local fd, err = co_wait() + local fd, err = aio._open(filename) if not fd then return nil, err end From 9a28fe798a75caa4f367c60dbaf2272964ced6ff Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Fri, 27 Mar 2020 00:05:10 +0800 Subject: [PATCH 545/956] =?UTF-8?q?laio=E6=96=B0=E5=A2=9Ecreate=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/laio.c | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/luaclib/src/laio.c b/luaclib/src/laio.c index ac34becc..cf3a14dd 100644 --- a/luaclib/src/laio.c +++ b/luaclib/src/laio.c @@ -10,7 +10,7 @@ static int INITIALIZATION = 0; /* 最小线程数量 */ -#define AIO_MAX_NTHREADS 8 +#define AIO_MAX_NTHREADS (8) #define req_data_to_coroutine(req) (req->data) @@ -344,7 +344,7 @@ static int aio_init() { -/* aio.open 打开文件描述符 */ +/* aio.open 打开一个文件(不存在则创建) */ static int laio_open(lua_State* L) { lua_State *t = lua_tothread(L, 1); if (!t) @@ -353,7 +353,7 @@ static int laio_open(lua_State* L) { size_t path_size = 0; const char *path = luaL_checklstring(L, 2, &path_size); if (!path || path_size < 1){ - return luaL_error(L, "Invalid aio truncate [path]."); + return luaL_error(L, "Invalid aio open [path]."); } eio_open(path, O_CREAT | O_RDWR, 0755, EIO_PRI_DEFAULT, AIO_RESPONSE_FD, (void*)t); @@ -361,6 +361,23 @@ static int laio_open(lua_State* L) { return 1; } +/* aio.create 创建一个文件(存在则返回错误) */ +static int laio_create(lua_State* L) { + lua_State *t = lua_tothread(L, 1); + if (!t) + return luaL_error(L, "Invalid lua coroutine."); + + size_t path_size = 0; + const char *path = luaL_checklstring(L, 2, &path_size); + if (!path || path_size < 1){ + return luaL_error(L, "Invalid aio create [path]."); + } + + eio_open(path, O_RDWR | O_CREAT | O_EXCL, 0755, EIO_PRI_DEFAULT, AIO_RESPONSE_FD, (void*)t); + + return 1; +} + /* aio.read 从文件内读取数据 */ static int laio_read(lua_State* L) { lua_State *t = lua_tothread(L, 1); @@ -383,7 +400,7 @@ static int laio_write(lua_State* L) { size_t buffer_size = 0; const char *buffer = luaL_checklstring(L, 3, &buffer_size); if (!buffer || buffer_size < 1){ - return luaL_error(L, "Invalid aio truncate [path]."); + return luaL_error(L, "Invalid aio write [buffer]."); } /* 当offset大于等于0使用pwrite, 否则使用write */ @@ -413,6 +430,20 @@ static int laio_close(lua_State* L) { return 1; } +/* aio.fileno 文件指针转换为fd */ +static int laio_fileno(lua_State* L) { + luaL_Stream* f = (luaL_Stream*)luaL_checkudata(L, 1, LUA_FILEHANDLE); + if (!f) + return luaL_error(L, "Invalide luaL_Stream."); + + lua_Integer fd = fileno((FILE *)f); + if (fd < 0) + return 0; + + lua_pushinteger(L, fd); + return 1; +} + static int laio_truncate(lua_State* L) { lua_State *t = lua_tothread(L, 1); if (!t) @@ -558,6 +589,8 @@ LUAMOD_API int luaopen_laio(lua_State* L){ { "write", laio_write }, { "flush", laio_flush }, { "close", laio_close }, + { "create", laio_create }, + { "fileno", laio_fileno }, {NULL, NULL}, }; luaL_newlib(L, aio_libs); From 7069aec33c4b1b6abf0c298736122b6b13979a5f Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Fri, 27 Mar 2020 11:33:16 +0800 Subject: [PATCH 546/956] =?UTF-8?q?=E5=8E=BB=E9=99=A4aio.fileno,=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0aio.fflush=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/laio.c | 64 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 16 deletions(-) diff --git a/luaclib/src/laio.c b/luaclib/src/laio.c index cf3a14dd..3de373c9 100644 --- a/luaclib/src/laio.c +++ b/luaclib/src/laio.c @@ -274,6 +274,32 @@ int AIO_RESPONSE_PATH(eio_req* req) { return 0; } +typedef struct aio_file{ + FILE *f; + lua_State *L; +}aio_file; + +static int AIO_RESPONSE_FFLUSH(eio_req* req) { + aio_file* afile = (aio_file*)req_data_to_coroutine(req); + if (EIO_RESULT (req) == -1){ + lua_pushboolean(afile->L, 0); + lua_pushstring(afile->L, strerror(req->errorno)); + }else { + lua_pushboolean(afile->L, 1); + } + if (LUA_OK != CO_RESUME(afile->L, NULL, lua_gettop(afile->L) - 1)) { + LOG("ERROR", lua_tostring(afile->L, -1)); + } + return 0; +} + +static void AIO_FFLUSH(eio_req *req) { + aio_file* afile = (aio_file*)req->data; + if ((req->result = fflush(afile->f)) == -1) { + req->errorno = errno; + } +} + static int sp[2]; static void AIO_WANT_POLL(void) { @@ -419,28 +445,34 @@ static int laio_flush(lua_State* L) { return 1; } -/* aio.close 关闭文件描述符 */ -static int laio_close(lua_State* L) { - lua_State *t = lua_tothread(L, 1); - if (!t) +/* aio.fflush 将文件内存数据刷新到磁盘 */ +static int laio_fflush(lua_State* L) { + + aio_file* afile = lua_newuserdata(L, sizeof(aio_file)); + + afile->L = lua_tothread(L, 1); + if (!afile->L) return luaL_error(L, "Invalid lua coroutine."); - eio_close(lua_tointeger(L, 2), EIO_PRI_DEFAULT, AIO_RESPONSE, (void*)t); + luaL_Stream *p = luaL_checkudata(L, 2, LUA_FILEHANDLE); + if (!p || !p->closef) + luaL_error(L, "attempt to use a closed file"); + + afile->f = p->f; + + eio_custom(AIO_FFLUSH, 0, AIO_RESPONSE_FFLUSH, afile); return 1; } -/* aio.fileno 文件指针转换为fd */ -static int laio_fileno(lua_State* L) { - luaL_Stream* f = (luaL_Stream*)luaL_checkudata(L, 1, LUA_FILEHANDLE); - if (!f) - return luaL_error(L, "Invalide luaL_Stream."); +/* aio.close 关闭文件描述符 */ +static int laio_close(lua_State* L) { + lua_State *t = lua_tothread(L, 1); + if (!t) + return luaL_error(L, "Invalid lua coroutine."); - lua_Integer fd = fileno((FILE *)f); - if (fd < 0) - return 0; + eio_close(lua_tointeger(L, 2), EIO_PRI_DEFAULT, AIO_RESPONSE, (void*)t); - lua_pushinteger(L, fd); return 1; } @@ -584,13 +616,13 @@ LUAMOD_API int luaopen_laio(lua_State* L){ { "readdir", laio_readdir }, { "readpath", laio_readpath }, { "truncate", laio_truncate }, - { "open", laio_open}, + { "open", laio_open }, { "read", laio_read }, { "write", laio_write }, { "flush", laio_flush }, { "close", laio_close }, { "create", laio_create }, - { "fileno", laio_fileno }, + { "fflush", laio_fflush }, {NULL, NULL}, }; luaL_newlib(L, aio_libs); From 94747dcf5e6c5f2364c4e56d8bd6f6d774f729b5 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Fri, 27 Mar 2020 11:55:04 +0800 Subject: [PATCH 547/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9httpd=E5=BA=93?= =?UTF-8?q?=E7=9A=84=E6=97=A5=E5=BF=97=E5=9C=A8=E8=B0=83=E7=94=A8run?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E5=90=8E=E6=89=8D=E5=BC=80=E5=A7=8B=E8=BE=93?= =?UTF-8?q?=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/init.lua | 47 ++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua index 4bf7450a..7c0cd46f 100644 --- a/lualib/httpd/init.lua +++ b/lualib/httpd/init.lua @@ -143,17 +143,24 @@ function httpd:log(path) end end --- CLOSE_LOG指定为true后将不会产生任何请求日志, 这样能提升更高的性能. -local CLOSE_LOG = false +-- 关闭所有日志 +function httpd:nolog( disable ) + -- disable指定为true后本机将不会生成任何请求日志, 这样能有利于框架提升更高的性能. + self.CLOSE_LOG = disable +end + -- LOG_FMT用于构建日志格式 local LOG_FMT = "[%s] - %s - %s - %s - %s - %d - req_time: %0.6f/Sec\n" function httpd:tolog(code, path, ip, ip_list, method, speed) + if self.CLOSE_LOG then + return + end local now = os_date("%Y/%m/%d %H:%M:%S") - if self.logging and not CLOSE_LOG then + if self.logging then self.logging:dump(fmt(LOG_FMT, now, ip, ip_list, path, method, code, speed)) end - if self.output and not CLOSE_LOG then + if io.type(io.output()) == 'file' then io_write(fmt(LOG_FMT, now, ip, ip_list, path, method, code, speed)) end end @@ -161,12 +168,8 @@ end -- 监听请求 function httpd:listen(ip, port, backlog) assert(type(ip) == 'string' and toint(port), "httpd error: invalid ip or port") - if io.type(io.output()) == 'file' then - io_write(fmt('\27[32m[%s] [INFO]\27[0m httpd listen: %s:%s \n', os_date("%Y/%m/%d %H:%M:%S"), "0.0.0.0", port)) - end - if self.logging then - self.logging:dump(fmt('[%s] [INFO] httpd listen: %s:%s\n', os_date("%Y/%m/%d %H:%M:%S"), "0.0.0.0", port)) - end + self.ip = ip + self.port = port self.sock:set_backlog(toint(backlog)) return assert(self.sock:listen(ip or "0.0.0.0", toint(port), function (fd, ipaddr) return EVENT_DISPATCH(fd, match(ipaddr, '^::[f]+:(.+)') or ipaddr, self) @@ -176,12 +179,7 @@ end -- 监听unixsock function httpd:listenx(unix_domain_path, backlog) assert(type(unix_domain_path) == 'string' and unix_domain_path ~= '', "httpd error: invalid unix domain path") - if io.type(io.output()) == 'file' then - io_write(fmt('\27[32m[%s] [INFO]\27[0m httpd listen: %s \n', os_date("%Y/%m/%d %H:%M:%S"), unix_domain_path)) - end - if self.logging then - self.logging:dump(fmt('[%s] [INFO] httpd listen: %s\n', os_date("%Y/%m/%d %H:%M:%S"), unix_domain_path)) - end + self.unix_domain_path = unix_domain_path self.sock:set_backlog(toint(backlog)) return assert(self.sock:listen_ex(unix_domain_path, true, function (fd, ipaddr) return EVENT_DISPATCH(fd, match(ipaddr, '^::[f]+:(.+)') or ipaddr, self) @@ -190,10 +188,21 @@ end -- 正确的运行方式 function httpd:run() - if io.type(io.output()) == 'file' then - self.output = true - io_write(fmt('\27[32m[%s] [INFO]\27[0m httpd Web Server Running...\n', os_date("%Y/%m/%d %H:%M:%S"))) + if self.ip and self.port then + if self.logging then + self.logging:dump(fmt('[%s] [INFO] httpd listen: %s:%s \n', os_date("%Y/%m/%d %H:%M:%S"), "0.0.0.0", self.port)) + end + io_write(fmt('\27[32m[%s] [INFO]\27[0m httpd listen: %s:%s \n', os_date("%Y/%m/%d %H:%M:%S"), "0.0.0.0", self.port)) end + + if self.unix_domain_path then + if self.logging then + self.logging:dump(fmt('[%s] [INFO] httpd listen: %s\n', os_date("%Y/%m/%d %H:%M:%S"), self.unix_domain_path)) + end + io_write(fmt('[%s] [INFO] httpd listen: %s\n', os_date("%Y/%m/%d %H:%M:%S"), self.unix_domain_path)) + end + + io_write(fmt('\27[32m[%s] [INFO]\27[0m httpd Web Server Running...\n', os_date("%Y/%m/%d %H:%M:%S"))) if self.logging then self.logging:dump(fmt('[%s] [INFO] httpd Web Server Running...\n', os_date("%Y/%m/%d %H:%M:%S"))) end From 742b1a48e5b4c4ea4e816424be1c649ad478a8bd Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Fri, 27 Mar 2020 11:56:18 +0800 Subject: [PATCH 548/956] =?UTF-8?q?aio=E5=BA=93=E6=96=B0=E5=A2=9Ecreate/ff?= =?UTF-8?q?lush=E6=96=B9=E6=B3=95=E7=94=A8=E4=BA=8E=E7=89=B9=E6=AE=8A?= =?UTF-8?q?=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/aio/init.lua | 75 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 9 deletions(-) diff --git a/lualib/aio/init.lua b/lualib/aio/init.lua index d2e2eecc..f6005115 100644 --- a/lualib/aio/init.lua +++ b/lualib/aio/init.lua @@ -9,7 +9,9 @@ local co_wakeup = cf.wakeup local laio = require "laio" local aio_open = laio.open local aio_stat = laio.stat +local aio_create = laio.create local aio_flush = laio.flush +local aio_fflush = laio.fflush local aio_read = laio.read local aio_write = laio.write local aio_close = laio.close @@ -131,15 +133,10 @@ function File:flush() if self.status == "closed" then return nil, "File already Closed." end - assert(not self.__FLUSH__, "File:flush方法不可以在多个协程中并发调用.") - self.__FLUSH__ = { current_co = co_self() } - self.__FLUSH__.event_co = co_new(function ( ok, err ) - local current_co = self.__FLUSH__.current_co - self.__FLUSH__ = nil - return co_wakeup(current_co, ok, err) - end) - aio_flush(self.__FLUSH__.event_co, self.fd) - return co_wait() + self.__FLUSH__ = assert(not self.__FLUSH__, "File:flush方法不可以在多个协程中并发调用.") + local ok, err = aio.flush(self.fd) + self.__FLUSH__ = nil + return ok, err end -- 清空文件 @@ -212,6 +209,40 @@ function aio._open(filename) return co_wait() end +-- 打开文件, 如果 +function aio.create(filename) + local fd, err = aio._create(filename) + if not fd then + return nil, err + end + local stat, err = aio.stat(filename) + if not stat then + local t = {} + t.current_co = co_self() + t.event_co = co_new(function ( ok, err ) + aio[t] = nil + return co_wakeup(t.current_co, ok, err) + end) + aio[t] = true + aio_close(t.event_co, fd) + return co_wait() + end + return File:new { fd = fd, path = filename, stat = stat } +end + +function aio._create(filename) + filename = assert(type(filename) == 'string' and filename ~= '' and filename ~= '.' and filename ~= '..' and filename, "Invalid filename.") + local t = {} + t.current_co = co_self() + t.event_co = co_new(function ( fd, err) + aio[t] = nil + return co_wakeup(t.current_co, fd, err) + end) + aio[t] = true + aio_create(t.event_co, filename) + return co_wait() +end + -- 仅关闭fd function aio._close(fd) fd = assert(toint(fd) and toint(fd) >= 0 and toint(fd), "Invalid fd.") @@ -336,4 +367,30 @@ function aio.truncate(filename, length) return co_wait() end +-- 刷新fd缓存 +function aio.flush(fd) + fd = assert(toint(fd) and toint(fd) >= 0 and toint(fd), "Invalid fd.") + local t = {} + t.current_co = co_self() + t.event_co = co_new(function ( ok, err ) + aio[t] = nil + return co_wakeup(t.current_co, ok, err) + end) + aio[t] = true + aio_flush(t.event_co, fd) + return co_wait() +end + +-- 刷新文件指针缓存 +function aio.fflush(file) + local t = {} + t.current_co = co_self() + t.event_co = co_new(function ( ok, err ) + aio[t] = nil + return co_wakeup(t.current_co, ok, err) + end) + aio[t] = aio_fflush(t.event_co, file) + return co_wait() +end + return aio \ No newline at end of file From 3eae0758173546d6e5e9bf43036cda1b7d743a96 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Fri, 27 Mar 2020 13:19:32 +0800 Subject: [PATCH 549/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=BC=82=E6=AD=A5?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E7=9A=84=E5=86=99=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/logging/init.lua | 73 ++++++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 20 deletions(-) diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua index 2624db8b..25a055ee 100644 --- a/lualib/logging/init.lua +++ b/lualib/logging/init.lua @@ -1,10 +1,15 @@ -- logging 核心配置 local cf = require "cf" +local cf_at = cf.at + +local aio = require "aio" +local aio_fflush = aio.fflush local class = require "class" -local new_tab = require "sys".new_tab -local now = require "sys".now +local sys = require "sys" +local now = sys.now +local new_tab = sys.new_tab local os_date = os.date @@ -12,6 +17,7 @@ local type = type local select = select local assert = assert local pairs = pairs +local ipairs = ipairs local tostring = tostring local getmetatable = getmetatable @@ -26,14 +32,15 @@ local concat = table.concat -- 可以在这里手动设置是否使用异步日志 local ASYNC = true - -if ASYNC then - if io_type(io.output()) == 'file' then - io.output():setvbuf("full", 2 ^ 20) - cf.at(0.5, function () - return io_flush() -- 定期刷新缓冲, 减少日志缓冲频繁导致的性能问题 - end) - end +-- 这里可以设置异步所使用的buffer. +local ASYNC_BUFFER_SIZE = 1 << 20 + +if ASYNC and io_type(io.output()) == 'file' then + local output = io.output() + output:setvbuf("full", ASYNC_BUFFER_SIZE) + local at = cf_at(0.5, function () + aio_fflush(output) + end) end -- 格式化时间: [年-月-日 时:分:秒,毫秒] @@ -118,6 +125,7 @@ local Log = class("Log") function Log:ctor (opt) if type(opt) == 'table' then + self.sync = opt.sync self.dumped = opt.dump self.path = opt.path self.today = Y_m_d() @@ -167,30 +175,55 @@ end -- 可以在这里手动设置日志路径 local LOG_FOLDER = 'logs/' +-- 异步写入(写缓存, 刷新工作交由工作线程) +function Log:async_write(log) + if not self.timer then + self.timer = cf_at(0.5, function ( ... ) + if self.oldfile then + self.oldfile:close() + self.oldfile = nil + end + if self.file then + aio_fflush(self.file) -- 使用单独的进程刷写数据到磁盘, 以此减少线程阻塞的可能性. + end + end) + end + return self.file:write(log) +end + +-- 同步写入(直接刷写到磁盘) +function Log:sync_write(log) + return self.file:write(log) +end + + -- dump日志到磁盘 function Log:dump(log) local today = Y_m_d() if today ~= self.today then if self.file then - self.file:close() + self.oldfile = self.file self.file = nil end - local file, err = io_open(LOG_FOLDER..self.path..'_'..today..'.log', 'a') + end + if not self.file then + local file, err = io_open(LOG_FOLDER..self.path..'_'..today..'.log', 'a+') if not file then return io_type(io.output()) == 'file' and io_write('打开文件失败: '..(('['..err..']') or '')..'\n') end self.file, self.today = file, today - file:setvbuf("line") + if self.async and ASYNC then + file:setvbuf("full", ASYNC_BUFFER_SIZE) + else + file:setvbuf("line") + end end - if not self.file then - local file, err = io_open(LOG_FOLDER..self.path..'_'..today..'.log', 'a') - if not file then - return io_type(io.output()) == 'file' and io_write('打开文件失败: '..(('['..err..']') or '')..'\n') + if not self.sync then + if not ASYNC then + return self:async_write(log) end - file:setvbuf("line") - self.file = file end - return self.file:write(log) + return self:sync_write(log) end return Log From ff49b7a1fb5bf9532d5221039d3c284bfa238857 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 28 Mar 2020 01:23:18 +0800 Subject: [PATCH 550/956] =?UTF-8?q?=E4=BC=98=E5=8C=96aio=E5=BA=93=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=B9=B6=E5=A2=9E=E5=8A=A0=E4=BA=86aio.remove?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E7=94=A8=E6=9D=A5=E5=88=A0=E9=99=A4=E6=96=87?= =?UTF-8?q?=E4=BB=B6/=E6=96=87=E4=BB=B6=E5=A4=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/laio.c | 84 +++++++++++++++----- lualib/aio/init.lua | 181 ++++++++++++++++++++++++++------------------ 2 files changed, 172 insertions(+), 93 deletions(-) diff --git a/luaclib/src/laio.c b/luaclib/src/laio.c index 3de373c9..c4b7edcb 100644 --- a/luaclib/src/laio.c +++ b/luaclib/src/laio.c @@ -3,15 +3,15 @@ /* 工作线程最大使用堆栈 */ #define EIO_STACKSIZE (1 << 16) +/* 最小线程数量(数量多少与性能并无太大相关性, 只是为了配合事件驱动完成异步IO改造) */ +#define AIO_MAX_NTHREADS (8) + #include #include /* 初始化 */ static int INITIALIZATION = 0; -/* 最小线程数量 */ -#define AIO_MAX_NTHREADS (8) - #define req_data_to_coroutine(req) (req->data) #define luaL_push_string_string(L, k, v) ({ lua_pushliteral(L, k); lua_pushstring(L, (v)); lua_rawset(L, -3); }) @@ -274,9 +274,36 @@ int AIO_RESPONSE_PATH(eio_req* req) { return 0; } +typedef struct aio_object{ + lua_State *L; + char *path; +}aio_object; + +static int AIO_RESPONSE_REMOVE(eio_req* req) { + aio_object* obj = (aio_object*)req_data_to_coroutine(req); + if (EIO_RESULT (req) == -1){ + lua_pushboolean(obj->L, 0); + lua_pushstring(obj->L, strerror(req->errorno)); + }else { + lua_pushboolean(obj->L, 1); + } + if (LUA_OK != CO_RESUME(obj->L, NULL, lua_gettop(obj->L) - 1)) { + LOG("ERROR", lua_tostring(obj->L, -1)); + } + return 0; +} + +static void AIO_REMOVE(eio_req *req) { + aio_object* obj = (aio_object*)req_data_to_coroutine(req); + if ((req->result = remove(obj->path)) == -1) { + req->errorno = errno; + } +} + + typedef struct aio_file{ - FILE *f; lua_State *L; + FILE *f; }aio_file; static int AIO_RESPONSE_FFLUSH(eio_req* req) { @@ -294,7 +321,7 @@ static int AIO_RESPONSE_FFLUSH(eio_req* req) { } static void AIO_FFLUSH(eio_req *req) { - aio_file* afile = (aio_file*)req->data; + aio_file* afile = (aio_file*)req_data_to_coroutine(req); if ((req->result = fflush(afile->f)) == -1) { req->errorno = errno; } @@ -368,8 +395,6 @@ static int aio_init() { } - - /* aio.open 打开一个文件(不存在则创建) */ static int laio_open(lua_State* L) { lua_State *t = lua_tothread(L, 1); @@ -410,8 +435,8 @@ static int laio_read(lua_State* L) { if (!t) return luaL_error(L, "Invalid lua coroutine."); - /* 当offset大于0使用pread, 否则使用read */ - eio_read(lua_tointeger(L, 2), 0, lua_tointeger(L, 3),lua_tointeger(L, 4), EIO_PRI_DEFAULT, AIO_RESPONSE_READ, (void*)t); + /* 适用pread来完成offset控制读取. */ + eio_read(lua_tointeger(L, 2), 0, lua_tointeger(L, 3), lua_tointeger(L, 4), EIO_PRI_DEFAULT, AIO_RESPONSE_READ, (void*)t); return 1; } @@ -429,8 +454,8 @@ static int laio_write(lua_State* L) { return luaL_error(L, "Invalid aio write [buffer]."); } - /* 当offset大于等于0使用pwrite, 否则使用write */ - eio_write(fd, (void*)buffer, buffer_size, lua_tointeger(L, 4), EIO_PRI_DEFAULT, AIO_RESPONSE_WRITE, (void*)t); + /* 适用write来完成追加操作, 同时也不允许单线程覆盖写入. */ + eio_write(fd, (void*)buffer, buffer_size, -1, EIO_PRI_DEFAULT, AIO_RESPONSE_WRITE, (void*)t); return 1; } @@ -460,7 +485,27 @@ static int laio_fflush(lua_State* L) { afile->f = p->f; - eio_custom(AIO_FFLUSH, 0, AIO_RESPONSE_FFLUSH, afile); + eio_custom(AIO_FFLUSH, EIO_PRI_DEFAULT, AIO_RESPONSE_FFLUSH, (void*)afile); + + return 1; +} + +/* aio.remove 删除一个文件或者文件夹 */ +static int laio_remove(lua_State* L) { + + aio_object* obj = lua_newuserdata(L, sizeof(aio_object)); + + obj->L = lua_tothread(L, 1); + if (!obj->L) + return luaL_error(L, "Invalid lua coroutine."); + + size_t path_size = 0; + obj->path = (char*)luaL_checklstring(L, 2, &path_size); + if (!obj->path || path_size < 1){ + return luaL_error(L, "Invalid aio truncate [path]."); + } + + eio_custom(AIO_REMOVE, EIO_PRI_DEFAULT, AIO_RESPONSE_REMOVE, (void*)obj); return 1; } @@ -602,12 +647,14 @@ static int laio_rmdir(lua_State* L) { LUAMOD_API int luaopen_laio(lua_State* L){ // printf("主线程ID为: %d\n", pthread_self()); luaL_checkversion(L); - if (!INITIALIZATION){ - if (aio_init()){ - return luaL_error(L, "aio init error."); - } - INITIALIZATION = 1; - } + if (INITIALIZATION) + return luaL_error(L, "aio error: Repeated initialization."); + + if (aio_init()) + return luaL_error(L, "aio init error."); + + INITIALIZATION = 1; + luaL_Reg aio_libs[] = { { "mkdir", laio_mkdir }, { "rmdir", laio_rmdir }, @@ -623,6 +670,7 @@ LUAMOD_API int luaopen_laio(lua_State* L){ { "close", laio_close }, { "create", laio_create }, { "fflush", laio_fflush }, + { "remove", laio_remove }, {NULL, NULL}, }; luaL_newlib(L, aio_libs); diff --git a/lualib/aio/init.lua b/lualib/aio/init.lua index f6005115..3b30acb0 100644 --- a/lualib/aio/init.lua +++ b/lualib/aio/init.lua @@ -1,5 +1,8 @@ local class = require "class" +local sys = require "sys" +local new_tab = sys.new_tab + local cf = require "cf" local co_new = coroutine.create local co_self = cf.self @@ -12,6 +15,7 @@ local aio_stat = laio.stat local aio_create = laio.create local aio_flush = laio.flush local aio_fflush = laio.fflush +local aio_remove = laio.remove local aio_read = laio.read local aio_write = laio.write local aio_close = laio.close @@ -48,64 +52,58 @@ function File:__gc() return true end --- 读取文件指定大小内容 +-- 重置read_offset; 这个方法一般情况下不会用到, 除非你非常明白自己在做什么. +function File:read_lseek(read_offset) + if toint(read_offset) and toint(read_offset) >= 0 then + self.read_offset = read_offset + end +end + +-- 读取文件指定大小内容; 除非调用read_lseek重置位置或有新内容写入, 否则超出文件长度后将会返回空字符串. function File:read( bytes ) if self.status == "closed" then return nil, "File already Closed." end bytes = toint(bytes) if not bytes or bytes <= 0 then + if bytes == 0 then + return "", 0 + end return nil, "Invalid file read bytes." end - assert(not self.__READ__, "File:read方法不可以在多个协程中并发调用.") - if not self.read_offset then - self.read_offset = 0 - end - self.__READ__ = { current_co = co_self() } - self.__READ__.event_co = co_new(function ( data, size ) - local current_co = self.__READ__.current_co - if type(data) == 'string' then - self.read_offset = self.read_offset + size - end + self.__READ__ = assert(not self.__READ__, "File:read/readall方法不可以在多个协程中并发调用.") + local stat, err = aio.stat(self.path) + if not stat then self.__READ__ = nil - return co_wakeup(current_co, data, size) - end) - aio_read(self.__READ__.event_co, self.fd, bytes, self.read_offset) - return co_wait() + return nil, err + end + self.stat = stat + -- 这一段的意思是: 当存在offset则取offset, 否则将offset置0; 无特殊情况不需要改动此地方 + self.read_offset = stat.size - (stat.size - (toint(self.read_offset) and toint(self.read_offset) > 0 and toint(self.read_offset) or 0)) + local data, err = aio._read(self.fd, bytes, self.read_offset) + self.read_offset = self.read_offset + #data + self.__READ__ = nil + return data, err end --- 读取文件所有内容 +-- 读取文件所有内容; 除非调用read_lseek重置位置或有新内容写入, 否则超出文件长度后将会返回空字符串. function File:readall() if self.status == "closed" then return nil, "File already Closed." end - local bytes = toint(self.stat.size) - if not bytes or bytes < 1 then - return "" - end - assert(not self.__READ__, "File:readall方法不可以在多个协程中并发调用.") - if not self.read_offset then - self.read_offset = 0 -- 如果没有读取过, 则一次性全部读取完毕. - else - -- 如果调用这个之前有调用过read, 那么将使用read_offset将之后的字节全部读取出来 - -- 如果已经读到末尾, 则直接返回空字符串并且调整read_offset确保一致性. - bytes = bytes > self.read_offset and bytes - self.read_offset or 0 - if bytes == 0 then - self.read_offset = self.stat.size - return "" - end - end - self.__READ__ = { current_co = co_self() } - self.__READ__.event_co = co_new(function ( data, size ) - local current_co = self.__READ__.current_co - if type(data) == 'string' then - self.read_offset = self.read_offset + size - end + self.__READ__ = assert(not self.__READ__, "File:read/readall方法不可以在多个协程中并发调用.") + local stat, err = aio.stat(self.path) + if not stat then self.__READ__ = nil - return co_wakeup(current_co, data, size) - end) - aio_read(self.__READ__.event_co, self.fd, bytes, self.read_offset) - return co_wait() + return nil, err + end + self.stat = stat + -- 这一段的意思是: 当存在offset则取offset, 否则将offset置0; 无特殊情况不需要改动此地方 + self.read_offset = stat.size - (stat.size - (toint(self.read_offset) and toint(self.read_offset) > 0 and toint(self.read_offset) or 0)) + local data, err = aio._read(self.fd, toint(self.stat.size), self.read_offset) + self.read_offset = self.read_offset + #data + self.__READ__ = nil + return data, err end -- 写入文件 @@ -113,19 +111,13 @@ function File:write( data ) if self.status == "closed" then return nil, "File already Closed." end - assert(not self.__WRITE__, "File:write方法不可以在多个协程中并发调用.") if type(data) ~= 'string' or data == "" then return nil, "Invalid file write data." end - self.__WRITE__ = { current_co = co_self() } - self.__WRITE__.event_co = co_new(function ( data, err ) - local current_co = self.__WRITE__.current_co - self.__WRITE__ = nil - return co_wakeup(current_co, data, err) - end) - aio_write(self.__WRITE__.event_co, self.fd, data, self.stat.size) - self.stat.size = self.stat.size + #data - return co_wait() + self.__WRITE__ = assert(not self.__WRITE__, "File:write方法不可以在多个协程中并发调用.") + local size, err = aio._write(self.fd, data) + self.__WRITE__ = nil + return size, err end -- 刷新缓存 @@ -166,7 +158,7 @@ end -- 关闭文件描述符 function File:close( ... ) if self.status == "closed" then - return nil, "File already Closed." + return nil, "File already closed." end local fd = self.fd self.fd = nil @@ -182,7 +174,7 @@ function aio.open(filename) end local stat, err = aio.stat(filename) if not stat then - local t = {} + local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( ok, err ) aio[t] = nil @@ -192,13 +184,13 @@ function aio.open(filename) aio_close(t.event_co, fd) return co_wait() end - return File:new { fd = fd, path = filename, stat = stat } + return File:new { fd = fd, path = filename } end -- 仅返回fd function aio._open(filename) filename = assert(type(filename) == 'string' and filename ~= '' and filename ~= '.' and filename ~= '..' and filename, "Invalid filename.") - local t = {} + local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( fd, err) aio[t] = nil @@ -217,7 +209,7 @@ function aio.create(filename) end local stat, err = aio.stat(filename) if not stat then - local t = {} + local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( ok, err ) aio[t] = nil @@ -232,7 +224,7 @@ end function aio._create(filename) filename = assert(type(filename) == 'string' and filename ~= '' and filename ~= '.' and filename ~= '..' and filename, "Invalid filename.") - local t = {} + local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( fd, err) aio[t] = nil @@ -243,10 +235,38 @@ function aio._create(filename) return co_wait() end +-- 读取指定字节 +function aio._read(fd, bytes, offset) + fd = assert(toint(fd) and toint(fd) >= 0 and toint(fd), "Invalid fd.") + local t = new_tab(0, 3) + t.current_co = co_self() + t.event_co = co_new(function ( data, size ) + aio[t] = nil + return co_wakeup(t.current_co, data, size) + end) + aio[t] = true + aio_read(t.event_co, fd, bytes, offset) + return co_wait() +end + +-- 写入(追加)指定大小数据 +function aio._write(fd, data) + fd = assert(toint(fd) and toint(fd) >= 0 and toint(fd), "Invalid fd.") + local t = new_tab(0, 3) + t.current_co = co_self() + t.event_co = co_new(function ( size, err ) + aio[t] = nil + return co_wakeup(t.current_co, size, err) + end) + aio[t] = true + aio_write(t.event_co, fd, data) + return co_wait() +end + -- 仅关闭fd function aio._close(fd) fd = assert(toint(fd) and toint(fd) >= 0 and toint(fd), "Invalid fd.") - local t = {} + local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( ok, err ) aio[t] = nil @@ -257,10 +277,10 @@ function aio._close(fd) return co_wait() end --- 创建指定目录 +-- 创建指定文件夹 function aio.mkdir(dir) dir = assert(type(dir) == 'string' and dir ~= '' and dir, "Invalid folder.") - local t = {} + local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( ok, err ) aio[t] = nil @@ -271,9 +291,9 @@ function aio.mkdir(dir) return co_wait() end --- 删除指定目录 +-- 删除指定文件夹 function aio.rmdir(dir) - local t = {} + local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( ok, err ) aio[t] = nil @@ -284,14 +304,14 @@ function aio.rmdir(dir) return co_wait() end --- 获取文件/目录状态 +-- 获取文件/文件夹状态 function aio.attributes(path) return aio.stat(path) end --- 获取文件/目录状态 +-- 获取文件/文件夹状态 function aio.stat(path) - local t = {} + local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( list, err ) aio[t] = nil @@ -302,10 +322,10 @@ function aio.stat(path) return co_wait() end --- 获取目录下所有文件 +-- 获取文件夹下所有文件(文件夹) function aio.dir(path) path = assert(type(path) == 'string' and path ~= '' and path, "Invalid path.") - local t = {} + local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( dirs ) aio[t] = nil @@ -320,7 +340,7 @@ end function aio.rename(old_name, new_name) old_name = assert(type(old_name) == 'string' and old_name ~= '' and old_name, "Invalid old_name.") new_name = assert(type(new_name) == 'string' and new_name ~= '' and new_name, "Invalid new_name.") - local t = {} + local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( ok ) aio[t] = nil @@ -338,7 +358,7 @@ end -- 获取指定目录完整路径 function aio.readpath(path) - local t = {} + local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( path ) aio[t] = nil @@ -352,11 +372,11 @@ function aio.readpath(path) return co_wait() end --- 清空文件或者缩减文件大小. 当length为0或者nil的时候将会清空文件. +-- 清空文件或者缩减文件内容到指定大小. 当length为0或者nil的时候将会清空文件. -- 注意: 这个操作是非常危险的, 您需要非常清楚自己的做什么. function aio.truncate(filename, length) filename = assert(type(filename) == 'string' and filename ~= '' and filename, "Invalid filename.") - local t = {} + local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( ok, err ) aio[t] = nil @@ -370,7 +390,7 @@ end -- 刷新fd缓存 function aio.flush(fd) fd = assert(toint(fd) and toint(fd) >= 0 and toint(fd), "Invalid fd.") - local t = {} + local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( ok, err ) aio[t] = nil @@ -381,9 +401,9 @@ function aio.flush(fd) return co_wait() end --- 刷新文件指针缓存 +-- 刷新FILE指针缓存 function aio.fflush(file) - local t = {} + local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( ok, err ) aio[t] = nil @@ -393,4 +413,15 @@ function aio.fflush(file) return co_wait() end +function aio.remove(filename) + local t = new_tab(0, 3) + t.current_co = co_self() + t.event_co = co_new(function ( ok, err ) + aio[t] = nil + return co_wakeup(t.current_co, ok, err) + end) + aio[t] = aio_remove(t.event_co, filename) + return co_wait() +end + return aio \ No newline at end of file From 50c283594a5e96e7d884f6d711f9389f1374f0b0 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 28 Mar 2020 02:39:06 +0800 Subject: [PATCH 551/956] =?UTF-8?q?=E5=AE=8C=E5=96=84test=5Faio.lua?= =?UTF-8?q?=E7=9A=84=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_aio.lua | 114 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/script/test_aio.lua b/script/test_aio.lua index 929051b6..3fd554fa 100644 --- a/script/test_aio.lua +++ b/script/test_aio.lua @@ -2,6 +2,8 @@ local LOG = require "logging" local aio = require "aio" local cf = require "cf" +require "utils" + local function run(func_name, ...) local ret, err = aio[func_name](...) return ret, err @@ -100,5 +102,117 @@ end -- end) -- end +--[[ + + 所以如果您需要外部使用, 请知悉这些内容. 这能让您明白是否真正的需要用到它. 由aio.open创建(一般情况下不可能失败)的对象并不是io库的file对象(元表是__AIO__). + + __AIO__相关方法的行为根据pread/pwrite行为而定, 且read_seek方法并不是真正调用的底层函数. 根据不同平台实现可能也并不是线程安全的, 而且__AIO__会在必要的时候做重入检查(抛出异常). + + 同时需要知道__AIO__的读取与写入性能并不会比原生io库高. 所以性能并不是__AIO__对象的优势, 它们的优势在于不会因为同步文件I/O操作长时间阻塞内置事件循环. + + 最后, 它与io库的file对象一样需要显示关闭文件描述符(fd). 虽然__AIO__对象在触发gc的时候可以检查并且关闭可能造成的泄露, 但是请不要过于依赖它(因为open files too many错误可能会比gc先到来). + +]] +local function test_aio_file_operations() + + local f = assert(aio.open("message.txt")) + + print("写入字节: [hello world!], 长度为: " .. f:write("hello world!")) + + print("读取1个字节为 : " .. f:read(1)) + + print("读取2个字节为 : " .. f:read(2)) + + print("读取剩余内容为 : " .. f:readall()) + + print("重置读取起始位置(offset)", f:read_lseek(0)) + + print("读取剩余内容为 : " .. f:readall()) + + print("清空文件: ", f:clean() and "成功") + + print("关闭文件: ", f:close() and "成功") + + print("删除message.txt文件: " , assert(aio.remove("message.txt")) and "成功") + +end + +-- aio.create方法在文件存在的时候会因为创建文件失败而返回nil与错误信息 +-- aio.remove方法在文件不存在的时候会因为删除失败而返回nil与错误信息 +local function test_aio_create_file_and_delete() + + print("创建message文件: ", assert(aio.create("message")) and "成功") + + print("删除message文件或文件夹: " , assert(aio.remove("message")) and "成功") + +end + +-- aio.mkdir与aio.rmdir方法用于创建于删除文件夹(aio.remove也可完成删除文件夹) +local function test_aio_create_dir_and_delete() + print("创建message文件夹: ", assert(aio.mkdir("message")) and "成功") + + print("删除message文件或文件夹: " , assert(aio.rmdir("message")) and "成功") +end + +-- aio.truncate方法会截断文件内容, 当它的第二个参数为0/nil/不传递第二个参数的时候您就如同做了清空文件的操作(效果等同于调用f:clean()) +-- 这在某些情况下是非常危险的操作, 因为这会立即影响到文件内容. 所以除非您知道自己在做什么, 否则请谨慎使用此方法. 一般情况下不会用到它. +local function test_aio_truncate_file() + -- aio.truncate("filename", "filesize") +end + +-- aio.currentdir方法会返回表示当前路径的字符串 +local function test_aio_display_current_dir() + print("当前目录为: " .. (aio.currentdir() or "")) +end + +-- aio.dir方法返回指定目录下的所有文件/文件夹名称(数组) +local function test_aio_display_dir() + var_dump(aio.dir(".")) +end + +-- aio.stat / aio.attributes 方法返回指定名称的文件/文件夹的属性 +local function test_aio_stat() + var_dump(aio.stat(".")) + -- var_dump(aio.attributes(".")) +end + +-- aio.rename 方法将会将会对指定的文件/文件夹重命名, 例如将3rd命名为4rd后, 最后再将其改回3rd +local function test_aio_rename() + print("将3rd文件夹改为4rd: ", assert(aio.rename("3rd", "4rd")) and "成功") + print("将4rd文件夹改为3rd: ", assert(aio.rename("4rd", "3rd")) and "成功") +end + +-- aio.fflush 方法将会刷新io.file对象的缓冲区(前提是您设置了), 这是一个同步非阻塞的操作. +-- 真正的刷新操作将会在其它系统线程内执行, 所以您并不用担心主线程会被影响到. 但一般情况下您并不会用用到它 +local function test_aio_fflush( ... ) + local f = assert(io.open("test.txt", "a")) + -- 设置完全缓冲区, 这在缓冲区未被写满或者f:close之前是不会刷写到磁盘上的. + f:setvbuf("full", "1024") + f:write("hello world!") + -- 如果您将以此之后的代码删除或者注释, 您会发现test.txt内并无任何内容. + -- 但如果您未注释下面的代码, test.txt会正常写入到磁盘上. + aio.fflush(f) + f:close() + -- 如果您不注释下面的这段代码! 文件操作完毕将会被删除, 您将不会知晓任何操作细节. + aio.remove("test.txt") +end + +-- test_aio_create_file_and_delete() + +-- test_aio_create_dir_and_delete() + +-- test_aio_file_operations() + +-- test_aio_display_current_dir() + +-- test_aio_display_dir() + +-- test_aio_stat() + +-- test_aio_rename() + +-- test_aio_fflush() + + cf.wait() \ No newline at end of file From 6071390586bdb59325860096ff35de6a4944a78a Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 28 Mar 2020 08:09:36 +0800 Subject: [PATCH 552/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BA=86aio=E5=BA=93?= =?UTF-8?q?=E7=9A=84=E6=9F=90=E4=BA=9B=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/aio/init.lua | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lualib/aio/init.lua b/lualib/aio/init.lua index 3b30acb0..441cdd63 100644 --- a/lualib/aio/init.lua +++ b/lualib/aio/init.lua @@ -238,6 +238,8 @@ end -- 读取指定字节 function aio._read(fd, bytes, offset) fd = assert(toint(fd) and toint(fd) >= 0 and toint(fd), "Invalid fd.") + bytes = assert(toint(bytes) and toint(bytes) >= 0 and toint(bytes), "Invalid read bytes.") + offset = assert(toint(offset) and toint(offset) >= 0 and toint(offset), "Invalid read offset.") local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( data, size ) @@ -252,6 +254,7 @@ end -- 写入(追加)指定大小数据 function aio._write(fd, data) fd = assert(toint(fd) and toint(fd) >= 0 and toint(fd), "Invalid fd.") + data = assert(type(data) == 'string' and data ~= '' and data, "Invalid write data.") local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( size, err ) @@ -293,6 +296,7 @@ end -- 删除指定文件夹 function aio.rmdir(dir) + dir = assert(type(dir) == 'string' and dir ~= '' and dir, "Invalid folder.") local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( ok, err ) @@ -300,7 +304,7 @@ function aio.rmdir(dir) return co_wakeup(t.current_co, ok, err) end) aio[t] = true - aio_rmdir(t.event_co, assert(type(dir) == 'string' and dir ~= '' and dir, "Invalid folder.")) + aio_rmdir(t.event_co, dir) return co_wait() end @@ -358,6 +362,7 @@ end -- 获取指定目录完整路径 function aio.readpath(path) + path = assert(type(path) == "string" and path ~= "" and path, "Invalid read path.") local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( path ) @@ -365,9 +370,6 @@ function aio.readpath(path) return co_wakeup(t.current_co, path ) end) aio[t] = true - if type(path) ~= 'string' or path == "" then - return nil, "Invalid path" - end aio_readpath(t.event_co, path) return co_wait() end @@ -413,6 +415,7 @@ function aio.fflush(file) return co_wait() end +-- 移除文件或文件夹 function aio.remove(filename) local t = new_tab(0, 3) t.current_co = co_self() From 5f65142e11313a2a1d0b7c4dcb35c24de896de69 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 29 Mar 2020 15:29:48 +0800 Subject: [PATCH 553/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0admin=E5=BA=93?= =?UTF-8?q?=E4=B8=8Esrc=E5=86=85=E9=83=A8=E7=9A=84=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=86=8D=E4=B8=8D=E5=90=8C=E6=83=85=E5=86=B5=E4=B8=8B=E7=9A=84?= =?UTF-8?q?=E5=85=BC=E5=AE=B9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/html/login/action.html | 2 +- lualib/admin/html/profile/base.html | 101 +++--- .../admin/html/system/header/header-add.html | 5 +- .../admin/html/system/header/header-edit.html | 50 +-- lualib/admin/html/system/header/header.html | 5 +- lualib/admin/html/system/menu/menu-add.html | 58 ++-- lualib/admin/html/system/menu/menu-edit.html | 65 ++-- lualib/admin/html/system/menu/menu.html | 39 +-- lualib/admin/html/system/role/role-add.html | 9 +- lualib/admin/html/system/role/role-edit.html | 8 +- lualib/admin/html/system/role/role.html | 4 +- lualib/admin/html/system/user/user-add.html | 60 ++-- lualib/admin/html/system/user/user-edit.html | 61 ++-- lualib/admin/html/system/user/user.html | 9 +- src/core.c | 21 +- static/welcome.html | 307 +++++++++--------- 16 files changed, 412 insertions(+), 392 deletions(-) diff --git a/lualib/admin/html/login/action.html b/lualib/admin/html/login/action.html index d71156e5..9827ba58 100644 --- a/lualib/admin/html/login/action.html +++ b/lualib/admin/html/login/action.html @@ -18,7 +18,7 @@ return layer.msg(res.msg, {time: 1000}, function(){ return window.location.href = res.url + '?token=' + res.token; }); }, error: function(res) { // 网络请求失败 - return layer.msg("请求失败", {time: 2000}, () => $('#sub').show()); + return layer.msg("请求失败", {time: 2000}, function(){ return $('#sub').show(); }); }, }) } diff --git a/lualib/admin/html/profile/base.html b/lualib/admin/html/profile/base.html index 1b431248..8e26c752 100644 --- a/lualib/admin/html/profile/base.html +++ b/lualib/admin/html/profile/base.html @@ -115,60 +115,59 @@
                  + }); + diff --git a/lualib/admin/html/system/header/header-add.html b/lualib/admin/html/system/header/header-add.html index 5a0dce66..0a226a57 100644 --- a/lualib/admin/html/system/header/header-add.html +++ b/lualib/admin/html/system/header/header-add.html @@ -62,8 +62,9 @@
                  diff --git a/lualib/admin/html/system/header/header.html b/lualib/admin/html/system/header/header.html index 28712876..5928c417 100644 --- a/lualib/admin/html/system/header/header.html +++ b/lualib/admin/html/system/header/header.html @@ -51,8 +51,9 @@
                  diff --git a/lualib/admin/html/system/menu/menu-edit.html b/lualib/admin/html/system/menu/menu-edit.html index 225f59bc..81f804bb 100644 --- a/lualib/admin/html/system/menu/menu-edit.html +++ b/lualib/admin/html/system/menu/menu-edit.html @@ -79,41 +79,42 @@ + }); + // 直接渲染value会导致被转义; + document.getElementById('name').value = '{*menu.name*}'; + document.getElementById('url').value = "{*menu.url ~= null and menu.url or '' *}"; + document.getElementById('icon').value = '{*menu.icon*}'; + diff --git a/lualib/admin/html/system/menu/menu.html b/lualib/admin/html/system/menu/menu.html index ec72ad0a..44f38bc1 100644 --- a/lualib/admin/html/system/menu/menu.html +++ b/lualib/admin/html/system/menu/menu.html @@ -103,33 +103,34 @@ diff --git a/lualib/admin/html/system/user/user-edit.html b/lualib/admin/html/system/user/user-edit.html index c8ce8ec9..8f749606 100644 --- a/lualib/admin/html/system/user/user-edit.html +++ b/lualib/admin/html/system/user/user-edit.html @@ -111,38 +111,37 @@ diff --git a/lualib/admin/html/system/user/user.html b/lualib/admin/html/system/user/user.html index d64ff873..a83f3fe6 100644 --- a/lualib/admin/html/system/user/user.html +++ b/lualib/admin/html/system/user/user.html @@ -68,13 +68,12 @@ {-raw-} From 940fa0bd485aebf7bd9f26fa02342fd6e84e4c64 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 29 Mar 2020 15:42:47 +0800 Subject: [PATCH 554/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dhttpd=E5=90=AF?= =?UTF-8?q?=E5=8A=A8=E5=8F=AF=E8=83=BD=E6=9C=AA=E6=98=8E=E8=89=B2=E6=A0=87?= =?UTF-8?q?=E5=87=BA=E7=9A=84=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua index 7c0cd46f..26c11809 100644 --- a/lualib/httpd/init.lua +++ b/lualib/httpd/init.lua @@ -199,13 +199,13 @@ function httpd:run() if self.logging then self.logging:dump(fmt('[%s] [INFO] httpd listen: %s\n', os_date("%Y/%m/%d %H:%M:%S"), self.unix_domain_path)) end - io_write(fmt('[%s] [INFO] httpd listen: %s\n', os_date("%Y/%m/%d %H:%M:%S"), self.unix_domain_path)) + io_write(fmt('\27[32m[%s] [INFO]\27[0m httpd listen: %s\n', os_date("%Y/%m/%d %H:%M:%S"), self.unix_domain_path)) end - io_write(fmt('\27[32m[%s] [INFO]\27[0m httpd Web Server Running...\n', os_date("%Y/%m/%d %H:%M:%S"))) if self.logging then self.logging:dump(fmt('[%s] [INFO] httpd Web Server Running...\n', os_date("%Y/%m/%d %H:%M:%S"))) end + io_write(fmt('\27[32m[%s] [INFO]\27[0m httpd Web Server Running...\n', os_date("%Y/%m/%d %H:%M:%S"))) return cf.wait() end From fa8e466f8c9da4810f57816832a8911cc9938ba1 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 29 Mar 2020 23:15:44 +0800 Subject: [PATCH 555/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0mysql=E5=AF=86?= =?UTF-8?q?=E7=A0=81=E7=9A=84=E7=94=9F=E6=88=90=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/mysql.lua | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua index e5c0b1ef..31ccde63 100644 --- a/lualib/protocol/mysql.lua +++ b/lualib/protocol/mysql.lua @@ -1,5 +1,7 @@ local tcp = require "internal.TCP" local crypt = require "crypt" +local sha1 = crypt.sha1 +local xor_str = crypt.xor_str local sub = string.sub local strgsub = string.gsub @@ -9,7 +11,6 @@ local strchar = string.char local strrep = string.rep local strunpack = string.unpack local strpack = string.pack -local sha1= crypt.sha1 local setmetatable = setmetatable local error = error local tonumber = tonumber @@ -127,15 +128,10 @@ local function _dumphex(bytes) return strgsub(bytes, ".", function(x) return strformat("%02x ", strbyte(x)) end) end -local function _compute_token(password, scramble) +local function mysq_native_password(password, scramble) local stage1 = sha1(password) - local stage2 = sha1(stage1) - local stage3 = sha1(scramble .. stage2) - local i = 0 - return strgsub(stage3,".", function(x) - i = i + 1 - return strchar(strbyte(x) ~ strbyte(stage1, i)) - end) + local stage2 = sha1(scramble .. sha1(stage1)) + return xor_str(stage2, stage1) end local function _send_packet(self, req, size) @@ -524,7 +520,7 @@ function MySQL.connect(self, opts) local password = opts.password or "" - local token = _compute_token(password, scramble) + local token = mysq_native_password(password, scramble) --print("token: ", _dump(token)) From ba5829d86904c43f015b4cc3b41590ddb282942c Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 29 Mar 2020 23:32:53 +0800 Subject: [PATCH 556/956] =?UTF-8?q?=E4=BC=98=E5=8C=96websocket=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E7=BB=93=E6=9E=84=E4=B8=8E=E5=8F=AF=E8=AF=BB=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/websocket/protocol.lua | 129 +++++++++++-------------- 1 file changed, 57 insertions(+), 72 deletions(-) diff --git a/lualib/protocol/websocket/protocol.lua b/lualib/protocol/websocket/protocol.lua index 8833ab97..fabc996e 100644 --- a/lualib/protocol/websocket/protocol.lua +++ b/lualib/protocol/websocket/protocol.lua @@ -72,8 +72,11 @@ end -- 压缩数据 local function compress_data(data) - data = compress2(data) - return gsub(data, ".", char(byte(data) - 1), 1) + local comp = compress2(data) + if not comp then + return nil + end + return gsub(comp, ".", char(byte(comp) - 1), 1) end -- 解压数据 @@ -84,7 +87,7 @@ end function _M.recv_frame(sock, max_payload_len, force_masking) local data, err = sock_recv(sock, 2) if not data then - return nil, nil, err + return nil, nil, err end local fst, snd = byte(data, 1, 2) @@ -114,63 +117,54 @@ function _M.recv_frame(sock, max_payload_len, force_masking) local payload_len = snd & 0x7f if payload_len == 126 then - local data, err = sock_recv(sock, 2) - if not data then - return nil, nil, "failed to receive the 2 byte payload length: " - .. (err or "unknown") - end - payload_len = (byte(data, 1) >> 8) | byte(data, 2) - + local data, err = sock_recv(sock, 2) + if not data then + return nil, nil, "failed to receive the 2 byte payload length: " .. (err or "unknown") + end + payload_len = (byte(data, 1) >> 8) | byte(data, 2) elseif payload_len == 127 then - local data, err = sock_recv(sock, 8) - if not data then - return nil, nil, "failed to receive the 8 byte payload length: " - .. (err or "unknown") - end + local data, err = sock_recv(sock, 8) + if not data then + return nil, nil, "failed to receive the 8 byte payload length: " .. (err or "unknown") + end - if byte(data, 1) ~= 0 or byte(data, 2) ~= 0 or byte(data, 3) ~= 0 or byte(data, 4) ~= 0 then - return nil, nil, "payload len too large" - end + if byte(data, 1) ~= 0 or byte(data, 2) ~= 0 or byte(data, 3) ~= 0 or byte(data, 4) ~= 0 then + return nil, nil, "payload len too large" + end - local fifth = byte(data, 5) - if fifth & 0x80 ~= 0 then - return nil, nil, "payload len too large" - end - payload = fifth << 24 | byte(data, 6) << 16 | byte(data, 7) | byte(data, 8) + local fifth = byte(data, 5) + if fifth & 0x80 ~= 0 then + return nil, nil, "payload len too large" + end + payload = fifth << 24 | byte(data, 6) << 16 | byte(data, 7) | byte(data, 8) end if opcode & 0x8 ~= 0 then - -- being a control frame - if payload_len > 125 then - return nil, nil, "too long payload for control frame" - end + -- being a control frame + if payload_len > 125 then + return nil, nil, "too long payload for control frame" + end - if not fin then - return nil, nil, "fragmented control frame" - end + if not fin then + return nil, nil, "fragmented control frame" + end end if payload_len > max_payload_len then return nil, nil, "exceeding max payload len" end - local rest + local rest = payload_len if mask then - rest = payload_len + 4 - - else - rest = payload_len + rest = payload_len + 4 end - local data + local data = "" if rest > 0 then - data, err = sock_recv(sock, rest) - if not data then - return nil, nil, "failed to read masking-len and payload: " - .. (err or "unknown") - end - else - data = "" + data, err = sock_recv(sock, rest) + if not data then + return nil, nil, "failed to read masking-len and payload: " .. (err or "unknown") + end end if opcode == 0x8 then @@ -224,26 +218,23 @@ function _M.recv_frame(sock, max_payload_len, force_masking) return "", "close", nil end - local msg + local msg = data if mask then - -- TODO string.buffer optimizations - local bytes = new_tab(payload_len, 0) - for i = 1, payload_len do - bytes[i] = str_char(byte(data, 4 + i) ~ byte(data, (i - 1) % 4 + 1)) - end - msg = concat(bytes) - - else - msg = data + -- TODO string.buffer optimizations + local bytes = new_tab(payload_len, 0) + for i = 1, payload_len do + bytes[i] = str_char(byte(data, 4 + i) ~ byte(data, (i - 1) % 4 + 1)) + end + msg = concat(bytes) end if fst & 0x40 == 0x40 and #msg > 0 then - -- print("压缩后的数据长度为:" .. #msg) + -- print("解压前的数据长度为:" .. #msg) local data = uncompress_data(msg) if not data then return msg, types[opcode], "invalide deflate data." end msg = data - -- print("压缩前的数据长度为:" .. #msg) + -- print("解压后的数据长度为:" .. #msg) end return msg, types[opcode], not fin and "again" or nil end @@ -251,11 +242,9 @@ end local function build_frame(fin, opcode, payload_len, payload, masking, ext) - local fst + local fst = opcode if fin then - fst = 0x80 | (ext and 0x40 or 0) | opcode - else - fst = opcode + fst = 0x80 | (ext == 'deflate' and 0x40 or 0) | opcode end local snd, extra_len_bytes @@ -277,7 +266,7 @@ local function build_frame(fin, opcode, payload_len, payload, masking, ext) extra_len_bytes = char(0, 0, 0, 0, (payload_len >> 24) & 0xff, (payload_len >> 16) & 0xff, (payload_len >> 8) & 0xff, payload_len & 0xff) end - local masking_key + local masking_key = "" if masking then -- set the mask bit snd = snd | 0x80 @@ -290,11 +279,7 @@ local function build_frame(fin, opcode, payload_len, payload, masking, ext) bytes[i] = str_char(byte(payload, i) ~ byte(masking_key, (i - 1) % 4 + 1)) end payload = concat(bytes) - - else - masking_key = "" end - return char(fst, snd) .. extra_len_bytes .. masking_key .. payload end _M.build_frame = build_frame @@ -306,6 +291,14 @@ function _M.send_frame(sock, fin, opcode, payload, max_payload_len, masking, ext local payload_len = #payload + -- 支持permessage-deflate压缩 + if ext == 'deflate' and payload_len > 0 then + -- print("压缩前的数据长度为:" .. #payload) + payload = assert(compress_data(payload), "deflate compress data error.") + payload_len = #payload + -- print("压缩后的数据长度为:" .. #payload) + end + if opcode & 0x8 ~= 0 then if payload_len > 125 then return error("The payload length of the control frame is too long.") @@ -315,14 +308,6 @@ function _M.send_frame(sock, fin, opcode, payload, max_payload_len, masking, ext end end - -- 支持permessage-deflate压缩 - if ext == 'deflate' and payload_len > 0 then - -- print("压缩前的数据长度为:" .. #payload) - payload = assert(compress_data(payload), "deflate compress data error.") - payload_len = #payload - -- print("压缩后的数据长度为:" .. #payload) - end - local frame, err = build_frame(fin, opcode, payload_len, payload, masking, ext) if not frame then return error("Invalid data frame: " .. err) From 9dfae469a2cf3536264bd5467a21466872d68cd1 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 29 Mar 2020 23:37:03 +0800 Subject: [PATCH 557/956] =?UTF-8?q?=E5=AE=8C=E5=96=84uncompress2,=E5=87=8F?= =?UTF-8?q?=E5=B0=91buffer=E4=B8=8D=E5=A4=9F=E7=9A=84=E5=8F=AF=E8=83=BD?= =?UTF-8?q?=E6=80=A7.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lz/lzlib.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/luaclib/src/lz/lzlib.c b/luaclib/src/lz/lzlib.c index 93792d3a..65367eb6 100644 --- a/luaclib/src/lz/lzlib.c +++ b/luaclib/src/lz/lzlib.c @@ -3,6 +3,8 @@ #include "../../../src/core.h" #include +#define MAX_COMPRESS_BUF_SIZE_TIMES (1 << 10) + static inline void stream_init(z_stream *z) { memset(z, 0x0, sizeof(*z)); z->zalloc = Z_NULL; @@ -137,7 +139,7 @@ static int lgzip_uncompress(lua_State *L) { return 0; } /* 防止内存溢出 */ - if (out_size > in_size * 256){ + if (out_size > in_size * MAX_COMPRESS_BUF_SIZE_TIMES){ luaL_pushresultsize(&B, 0); inflateEnd(&z); return 0; @@ -224,7 +226,7 @@ static int luncompress2(lua_State *L) { return 0; } /* 防止内存溢出 */ - if (out_size > in_size * 256){ + if (out_size > in_size * MAX_COMPRESS_BUF_SIZE_TIMES){ luaL_pushresultsize(&B, 0); inflateEnd(&z); return 0; From c6bc471e6048b264aa270ac36439607c20716d6e Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 30 Mar 2020 10:42:24 +0800 Subject: [PATCH 558/956] =?UTF-8?q?=E6=9B=B4=E6=94=B9httpd=E5=8E=8B?= =?UTF-8?q?=E7=BC=A9=E4=BC=98=E5=85=88=E4=BD=BF=E7=94=A8gzip,=20=E5=85=B6?= =?UTF-8?q?=E6=AC=A1=E4=BD=BF=E7=94=A8deflate.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index ec5c8433..65fa0b18 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -493,24 +493,19 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) return sock:close() end local accept_encoding = HEADER['Accept-Encoding'] - if enable_gzip and type(accept_encoding) == 'string' and #body >= 50 then - local YES = false - -- 如果支持deflate则使用compress压缩 - if find(lower(accept_encoding), "deflate") then - local compress_body = decompress(body) + if type(accept_encoding) == 'string' and enable_gzip and #body >= 50 then + -- body压缩选择优先使用gzip, 否则使用deflate, 如果都没有则使用原始字符串. + if find(lower(accept_encoding), "gzip") then + local compress_body = gzcompress(body) if compress_body then - header[#header+1] = 'Content-Encoding: deflate' + header[#header+1] = 'Content-Encoding: gzip' body = compress_body - YES = true end - end - -- 如果支持gzip就使用gzip压缩后返回 - if not YES and find(lower(accept_encoding), "gzip") then - local compress_body = gzcompress(body) + else find(lower(accept_encoding), "deflate") then + local compress_body = decompress(body) if compress_body then - header[#header+1] = 'Content-Encoding: gzip' + header[#header+1] = 'Content-Encoding: deflate' body = compress_body - YES = true end end end From f2c37827bcb9944a9ee8019823362bcca6982a2d Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 30 Mar 2020 10:46:52 +0800 Subject: [PATCH 559/956] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E8=AF=AD=E6=B3=95=E9=94=99=E8=AF=AF=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index 65fa0b18..63a85896 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -501,7 +501,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) header[#header+1] = 'Content-Encoding: gzip' body = compress_body end - else find(lower(accept_encoding), "deflate") then + elseif find(lower(accept_encoding), "deflate") then local compress_body = decompress(body) if compress_body then header[#header+1] = 'Content-Encoding: deflate' From 66e1107e4fbfb348c2f1a70812a305c91e2e37f2 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 31 Mar 2020 20:29:46 +0800 Subject: [PATCH 560/956] =?UTF-8?q?sys=E5=BA=93=E5=A2=9E=E5=8A=A0hostname?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E8=8E=B7=E5=8F=96=E5=BD=93=E5=89=8D=E4=B8=BB?= =?UTF-8?q?=E6=9C=BA=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lsys.c | 90 +++++++++++++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 32 deletions(-) diff --git a/luaclib/src/lsys.c b/luaclib/src/lsys.c index e4da253a..01d85f27 100644 --- a/luaclib/src/lsys.c +++ b/luaclib/src/lsys.c @@ -10,30 +10,43 @@ static int lnow(lua_State *L){ /* 此方法可用于检查是否为有效ipv4地址*/ static int lipv4(lua_State *L){ - const char *IP = lua_tostring(L, 1); - if (!IP) return luaL_error(L, "ipv4 error: 请至少传递一个string类型参数\n"); - lua_pushboolean(L, ipv4(IP)); - return 1; + size_t str_len = 0; + const char *IP = luaL_checklstring(L, 1, &str_len); + if (!IP || str_len == 0) + return luaL_error(L, "ipv4 error: 请至少传递一个string类型参数\n"); + lua_pushboolean(L, ipv4(IP)); + return 1; } /* 此方法可用于检查是否为有效ipv6地址*/ static int lipv6(lua_State *L){ - const char *IP = lua_tostring(L, 1); - if (!IP) return luaL_error(L, "ipv6 error: 请至少传递一个string类型参数\n"); - lua_pushboolean(L, ipv6(IP)); - return 1; + size_t str_len = 0; + const char *IP = luaL_checklstring(L, 1, &str_len); + if (!IP || str_len == 0) + return luaL_error(L, "ipv6 error: 请至少传递一个string类型参数\n"); + lua_pushboolean(L, ipv6(IP)); + return 1; } -/* 返回时间 */ +/* 返回格式化后的时间 */ static int ldate(lua_State *L){ - const char *fmt = lua_tostring(L, 1); - if (!fmt) return luaL_error(L, "Date: 错误的格式化方法"); - time_t timestamp = lua_tointeger(L, 2); - if (0 >= timestamp) timestamp = time(NULL); - char fmttime[256]; - strftime(fmttime, 256, fmt, localtime(×tamp)); - lua_pushstring(L, fmttime); - return 1; + size_t str_len = 0; + const char *fmt = luaL_checklstring(L, 1, &str_len); + if (!fmt || str_len == 0) + return luaL_error(L, "Date: 错误的格式化方法"); + + time_t timestamp = lua_tointeger(L, 2); + if (0 >= timestamp) + timestamp = time(NULL); + + size_t len = 128 + str_len; + char fmttime[len]; + memset(fmttime, 0x0, len); + int result = strftime(fmttime, len, fmt, localtime(×tamp)); + if (result < 0) + return 0; + lua_pushlstring(L, fmttime, result); + return 1; } /* 返回当前操作系统类型 */ @@ -42,26 +55,39 @@ static int los(lua_State *L){ return 1; } +/* 返回主机名 */ +static int lhostname(lua_State *L){ + size_t max_hostaname = 4096; + char *hostname = lua_newuserdata(L, max_hostaname); + memset(hostname, 0x0, max_hostaname); + int len = gethostname(hostname, max_hostaname); + if (0 > len) + return 0; + lua_pushlstring(L, hostname, strlen(hostname)); + return 1; +} + /* 创建表 */ static int lnew_tab(lua_State *L){ - lua_Integer array_size = luaL_checkinteger(L, 1); - lua_Integer hash_size = luaL_checkinteger(L, 2); - lua_createtable(L, array_size, hash_size); - return 1; + // lua_Integer array_size = luaL_checkinteger(L, 1); // array 部分大小 + // lua_Integer hash_size = luaL_checkinteger(L, 2); // hash 部分大小 + lua_createtable(L, luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)); + return 1; } LUAMOD_API int luaopen_sys(lua_State *L){ - luaL_checkversion(L); - luaL_Reg sys_libs[] = { - {"now", lnow}, - {"ipv4", lipv4}, - {"ipv6", lipv6}, - {"date", ldate}, + luaL_checkversion(L); + luaL_Reg sys_libs[] = { + {"now", lnow}, + {"ipv4", lipv4}, + {"ipv6", lipv6}, + {"date", ldate}, {"os", los}, - {"new_tab", lnew_tab}, - {NULL, NULL} - }; - luaL_newlib(L, sys_libs); - return 1; + {"hostname", lhostname}, + {"new_tab", lnew_tab}, + {NULL, NULL} + }; + luaL_newlib(L, sys_libs); + return 1; } From 72824b2e893957b5720398759ab6f1237b25370f Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 31 Mar 2020 20:35:59 +0800 Subject: [PATCH 561/956] =?UTF-8?q?=E8=B0=83=E6=95=B4uuid.c=E7=9A=84?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/uuid.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/luaclib/src/lcrypt/uuid.c b/luaclib/src/lcrypt/uuid.c index e9aac8d2..b9cd1b68 100644 --- a/luaclib/src/lcrypt/uuid.c +++ b/luaclib/src/lcrypt/uuid.c @@ -2,7 +2,7 @@ #define UUID_V4_LENGTH 36 -static inline int uuid_v4_gen(char *buffer) +static inline const char* uuid_v4_gen(char *buffer) { union { struct { @@ -16,7 +16,7 @@ static inline int uuid_v4_gen(char *buffer) uint8_t __rnd[16]; } uuid; - int rc = RAND_bytes(uuid.__rnd, sizeof(uuid)); + RAND_bytes(uuid.__rnd, sizeof(uuid)); uuid.clk_seq_hi_res = (uint8_t) ((uuid.clk_seq_hi_res & 0x3F) | 0x80); uuid.time_hi_and_version = (uint16_t) ((uuid.time_hi_and_version & 0x0FFF) | 0x4000); @@ -27,13 +27,11 @@ static inline int uuid_v4_gen(char *buffer) uuid.node[0], uuid.node[1], uuid.node[2], uuid.node[3], uuid.node[4], uuid.node[5] ); - - return rc; + buffer[UUID_V4_LENGTH] = '\0'; + return (const char*) buffer; } int luuid(lua_State *L) { - char* UUID = lua_newuserdata(L, UUID_V4_LENGTH + 1); - uuid_v4_gen(UUID); - lua_pushlstring(L, UUID, UUID_V4_LENGTH); + lua_pushlstring(L, uuid_v4_gen(lua_newuserdata(L, UUID_V4_LENGTH + 1)), UUID_V4_LENGTH); return 1; } \ No newline at end of file From 51575fabd3e1b56438fd711c5220a43e2baf8207 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 31 Mar 2020 21:37:50 +0800 Subject: [PATCH 562/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9randomkey=E7=9A=84?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0,=20=E5=A2=9E=E5=8A=A0randomkey=5Fex(?= =?UTF-8?q?=E6=94=AF=E6=8C=818-64=E4=B8=AA=E5=AD=97=E7=AC=A6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/lcrypt.c | 48 ++++++++++++++++++------------------- lualib/crypt/init.lua | 10 +++++++- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/luaclib/src/lcrypt/lcrypt.c b/luaclib/src/lcrypt/lcrypt.c index 9ecf24d1..4da18f34 100644 --- a/luaclib/src/lcrypt/lcrypt.c +++ b/luaclib/src/lcrypt/lcrypt.c @@ -11,7 +11,7 @@ static int lxor_str(lua_State *L) { luaL_Buffer b; char * buffer = luaL_buffinitsize(L, &b, len1); int i; - for (i=0;i 64) + return luaL_error(L, "randomkey error: 8 <= byte <= 64."); + + uint8_t random_buf[len]; RAND_bytes(random_buf, len); + uint8_t random_key[len]; RAND_bytes(random_key, len); + uint8_t random_tmp[len]; RAND_bytes(random_tmp, len); int i; - char x = 0; - for (i=0;i<8;i++) { - tmp[i] = random() & 0xff; - x ^= tmp[i]; - } - if (x==0) { - tmp[0] |= 1; // avoid 0 - } - lua_pushlstring(L, tmp, 8); + for (i = 0; i < len; i++) + random_buf[i] = ((random_key[i] ^ random_tmp[i]) ^ random_buf[i]) & 0xff; + lua_pushlstring(L, (const char *)random_buf, len); return 1; } /* -- xor_str -- */ @@ -40,7 +40,7 @@ LUAMOD_API int luaopen_lcrypt(lua_State *L) { luaL_checkversion(L); luaL_Reg lcrypt[] = { - {"uuid", luuid}, + { "uuid", luuid }, { "hashkey", lhashkey }, { "randomkey", lrandomkey }, { "desencode", ldesencode }, @@ -76,25 +76,25 @@ luaopen_lcrypt(lua_State *L) { { "hmac_hash", lhmac_hash }, { "xor_str", lxor_str }, // 公钥加密 -> 私钥解密 - {"rsa_public_key_encode", lrsa_public_key_encode}, - {"rsa_private_key_decode", lrsa_private_key_decode}, + { "rsa_public_key_encode", lrsa_public_key_encode }, + { "rsa_private_key_decode", lrsa_private_key_decode }, // 私钥加密 -> 公钥解密 - {"rsa_private_key_encode", lrsa_private_key_encode}, - {"rsa_public_key_decode", lrsa_public_key_decode}, + { "rsa_private_key_encode", lrsa_private_key_encode }, + { "rsa_public_key_decode", lrsa_public_key_decode }, //shawithrsa - {"sha128WithRsa_sign", lSha128WithRsa_sign}, - {"sha128WithRsa_verify", lSha128WithRsa_verify}, - {"sha256WithRsa_sign", lSha256WithRsa_sign}, - {"sha256WithRsa_verify", lSha256WithRsa_verify}, + { "sha128WithRsa_sign", lSha128WithRsa_sign }, + { "sha128WithRsa_verify", lSha128WithRsa_verify }, + { "sha256WithRsa_sign", lSha256WithRsa_sign }, + { "sha256WithRsa_verify", lSha256WithRsa_verify }, // aes 加密 - {"aes_ecb_encrypt", laes_ecb_encrypt}, - {"aes_cbc_encrypt", laes_cbc_encrypt}, + { "aes_ecb_encrypt", laes_ecb_encrypt }, + { "aes_cbc_encrypt", laes_cbc_encrypt }, // {"aes_cfb_encrypt", laes_cfb_encrypt}, // {"aes_ofb_encrypt", laes_ofb_encrypt}, // {"aes_ctr_encrypt", laes_ctr_encrypt}, // aes 解密 - {"aes_ecb_decrypt", laes_ecb_decrypt}, - {"aes_cbc_decrypt", laes_cbc_decrypt}, + { "aes_ecb_decrypt", laes_ecb_decrypt }, + { "aes_cbc_decrypt", laes_cbc_decrypt }, // {"aes_cfb_decrypt", laes_cfb_decrypt}, // {"aes_ofb_decrypt", laes_ofb_decrypt}, // {"aes_ctr_decrypt", laes_ctr_decrypt}, diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index a3a99a16..be18a0d5 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -155,7 +155,15 @@ function crypt.xor_str (text, key, hex) end function crypt.randomkey(hex) - local hash = randomkey() + local hash = randomkey(8) + if hash and hex then + return hexencode(hash) + end + return hash +end + +function crypt.randomkey_ex(byte, hex) + local hash = randomkey(byte) if hash and hex then return hexencode(hash) end From 40fa9ae3d3ccab5ce24a64dd0a6810abc53be5d5 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 31 Mar 2020 22:29:45 +0800 Subject: [PATCH 563/956] =?UTF-8?q?=E4=BC=98=E5=8C=96crypt=E5=BA=93?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/lcrypt.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/luaclib/src/lcrypt/lcrypt.c b/luaclib/src/lcrypt/lcrypt.c index 4da18f34..0b0217d3 100644 --- a/luaclib/src/lcrypt/lcrypt.c +++ b/luaclib/src/lcrypt/lcrypt.c @@ -2,18 +2,23 @@ /* -- xor_str -- */ static int lxor_str(lua_State *L) { - size_t len1,len2; + size_t len1 = 0; const char *s1 = luaL_checklstring(L, 1, &len1); + if (!s1 || len1 == 0) + return luaL_error(L, "Can't xor empty string 1."); + + size_t len2 = 0; const char *s2 = luaL_checklstring(L, 2, &len2); - if (len2 == 0) { - return luaL_error(L, "Can't xor empty string"); - } + if (!s2 || len2 == 0) + return luaL_error(L, "Can't xor empty string 2."); + luaL_Buffer b; char * buffer = luaL_buffinitsize(L, &b, len1); + int i; - for (i = 0; i < len1; i++) { + for (i = 0; i < len1; i ++) buffer[i] = s1[i] ^ s2[i % len2]; - } + luaL_addsize(&b, len1); luaL_pushresult(&b); return 1; @@ -22,14 +27,16 @@ static int lxor_str(lua_State *L) { static int lrandomkey(lua_State *L) { lua_Integer len = lua_tointeger(L, 1); if (len < 8 || len > 64) - return luaL_error(L, "randomkey error: 8 <= byte <= 64."); + return luaL_error(L, "randomkey error: 8 <= len <= 64."); uint8_t random_buf[len]; RAND_bytes(random_buf, len); uint8_t random_key[len]; RAND_bytes(random_key, len); uint8_t random_tmp[len]; RAND_bytes(random_tmp, len); + int i; for (i = 0; i < len; i++) random_buf[i] = ((random_key[i] ^ random_tmp[i]) ^ random_buf[i]) & 0xff; + lua_pushlstring(L, (const char *)random_buf, len); return 1; } From 9c36310f162ef398bc0257fea047d4cf0d6f5de5 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 1 Apr 2020 10:23:24 +0800 Subject: [PATCH 564/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dadmin=E5=BA=93?= =?UTF-8?q?=E7=9A=84=E5=BC=95=E7=94=A8=E9=94=99=E8=AF=AF=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/html/system/role/role-edit.html | 2 +- lualib/admin/html/system/user/user-add.html | 2 +- lualib/admin/html/system/user/user-edit.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lualib/admin/html/system/role/role-edit.html b/lualib/admin/html/system/role/role-edit.html index 0c958daa..a596cbac 100644 --- a/lualib/admin/html/system/role/role-edit.html +++ b/lualib/admin/html/system/role/role-edit.html @@ -62,7 +62,7 @@ + @@ -103,7 +103,7 @@ + + \ No newline at end of file diff --git a/lualib/admin/html/login/content.html b/lualib/admin/html/login/content.html index cb412608..2dbae1ae 100644 --- a/lualib/admin/html/login/content.html +++ b/lualib/admin/html/login/content.html @@ -9,6 +9,9 @@

                  + +
                  +
                  diff --git a/lualib/admin/html/login/head.html b/lualib/admin/html/login/head.html index 46871eb1..5d93bd2c 100644 --- a/lualib/admin/html/login/head.html +++ b/lualib/admin/html/login/head.html @@ -1,17 +1,16 @@ - {{ locale['login.form.title'] }} - - - - - - - - - - + {{ locale['login.form.title'] }} + + + + + + + + + diff --git a/lualib/admin/http/login.lua b/lualib/admin/http/login.lua index 64e4f0b1..4b88e760 100644 --- a/lualib/admin/http/login.lua +++ b/lualib/admin/http/login.lua @@ -12,10 +12,21 @@ local json_decode = json.decode local json_encode = json.encode local type = type +local byte = string.byte local get_locale = utils.get_locale local template_path = 'lualib/admin/html/login/base.html' +local function random_sign(randomkey, value) + local sign = {} + randomkey = randomkey:reverse() + for i = 1, #value do + sign[i] = (byte(value, i) ~ byte(randomkey, i <= #randomkey and i or (i % randomkey) + 1)) & 0xff + sign[i] = string.format("%02x", sign[i]) + end + return table.concat(sign) +end + local login = {} -- 登录页面逻辑 @@ -27,6 +38,7 @@ function login.render(content) return template.compile(template_path){ cdn = config.cdn, login_api = config.login_api, + randomkey = crypt.randomkey_ex(16, true):reverse(), locale = get_locale(Cookie.getCookie("CFLANG")) } end @@ -34,19 +46,23 @@ end -- 登录接口逻辑 function login.response (content) local db = config.db - local args = content.args - if type(args) ~= 'table' then - return json_encode({code = 401, msg = "1. 非法的参数"}) + local args = content.args or (content.json and json_decode(content.body)) + -- 验证参数是否存在 + if type(args) ~= 'table' or type(args.username) ~= 'string' or type(args.password) ~= 'string' or type(args.randomkey) ~= 'string' then + return json_encode({code = 401, msg = "1. 无效的请求参数"}) + end + local username, password, randomkey, verify_code = args.username, args.password, args.randomkey, args.verify_code + if username == '' or password == '' or randomkey == '' or type(verify_code) ~= 'string' then + return json_encode({code = 401, msg = "2. 无效的请求参数"}) end - -- 验证参数 - local username, password = args.username, args.password - if not username or not password then - return json_encode({code = 401, msg = "2. 错误的参数"}) + -- 验证随机数行为 + if random_sign(randomkey, username .. password) ~= verify_code then + return json_encode({code = 401, msg = "3. 用户密码验证失败"}) end -- 获取登录信息 local user_info = user.user_exists(db, username) if not user_info or username ~= user_info.username or crypt.sha1(password, true) ~= user_info.password then - return json_encode({code = 403, msg = "3. 用户不存在或者密码错误"}) + return json_encode({code = 403, msg = "4. 用户不存在或者密码错误"}) end local uid, name = user_info.id, user_info.name -- 生成token diff --git a/static/layui_ext/dist/sliderVerify.js b/static/layui_ext/dist/sliderVerify.js new file mode 100644 index 00000000..c845f4a5 --- /dev/null +++ b/static/layui_ext/dist/sliderVerify.js @@ -0,0 +1 @@ +layui.define(["jquery","layer","form"],function(s){var d=layui.jquery,b=layui.form,r=layui.layer,k=layui.device(),h={read:(function(){var u=".slider-item{height:38px;line-height:38px;background-color:#d0d0d0;position:relative;border: 1px solid white;}.slider-bg{position:absolute;width:40px;height:100%;z-index:100}.slider-btn{width:40px;height:96%;position:absolute;border:1px solid #ccc;cursor:move;text-align:center;background-color:#fff;user-select:none;color:#666;z-index:120}.slider-btn-success{font-size:26px}.slider-text{position:absolute;text-align:center;width:100%;height:100%;user-select:none;font-size:14px;color:#fff;z-index:120}.slider-error{animation:glow 800ms ease-out infinite alternate;}@keyframes glow{0%{border-color:#e6e6e6}100%{border-color:#ff5722}}",v=document.createElement("style");v.innerHTML=u;v.type="text/css";(d("head link:last")[0]&&d("head link:last").after(v))||d("head").append(v)})()},m=function(u){return u[0]},o=function(){var u=this;return{isOk:function(){return u.isOk.call(u)},reset:function(){return u.reset.call(u)},version:"1.7.1"}},g="sliderVerify",i="slider-btn",j="slider-bg",q="slider-text",a="layui-icon-next",t="layui-icon-ok-circle",f="slider-btn-success",n="layui-bg-green",c="slider-error",p="请拖动滑块验证",e="验证通过",l=function(u){var v=this;v.config=d.extend({},v.config,u);v.render()};l.prototype.config={elem:"",onOk:null,isOk:false,isAutoVerify:true,timer:null,bg:n,text:p};l.prototype.render=function(){var w=this,u=w.config,v=d(u.elem);if(!v[0]){return}if(u.domid){u.domid.remove()}u.domid=w.createIdNum();var x=d(['
                  ','
                  ','
                  '+u.text+"
                  ",'
                  '].join(""));v.hide().after(x);u.domid=d("#"+u.domid);w.events();if(u.isAutoVerify){b.verify({sliderVerify:function(y,z){if(!y){z.classList.add(c);return u.text}}})}};l.prototype.isMobile=function(){return(k.os=="ios"||k.os=="android"||k.android||k.ios)||(k.weixin&&typeof k.weixin===Boolean)};l.prototype.createIdNum=function(){return(g+(+new Date()).toString()+Math.random().toString().substr(2,7))};l.prototype.isOk=function(){return this.config.isOk};l.prototype.error=function(u){return r.msg(u,{icon:5})};l.prototype.distance=function(){var u=this.config.container;return u.box.offsetWidth-u.btn.offsetWidth};l.prototype.reset=function(){this.config.isOk=false;return this.render()};l.prototype.resize=function(w){var v=this,u=v.config.container;var w=w||v.distance();u.btn.style.left=w+"px";u.bg.style.width=w+"px"};l.prototype.cancelTransition=function(){var u=this.config.container;this.config.domid[0].classList.remove(c);u.btn.style.transition="";u.bg.style.transition=""};l.prototype.down=function(y){var x=this,w=x.config,v=w.container,y=y||window.event,z=y.clientX||y.touches[0].clientX;x.cancelTransition();var u=function(A){x.move(z,A)};x.events.move=u;if(x.isMobile()){document.addEventListener("touchmove",x.events.move)}else{document.onmousemove=u}if(navigator.userAgent.indexOf("UCBrowser")>-1){y.preventDefault()}};l.prototype.move=function(C,B){var A=this,y=A.config,u=y.container;var B=B||window.event;var w=B.clientX||B.touches[0].clientX;var z=w-C;if(z>u.distance){z=u.distance}else{if(z<0){z=0}}u.btn.style.left=z+"px";u.bg.style.width=z+"px";if(z==u.distance){u.text.innerHTML=e;var v=window.getComputedStyle?window.getComputedStyle(u.bg,null):u.bg.currentStyle;u.btn.style.border="1px solid "+v.backgroundColor;u.btn.style.color=v.backgroundColor;u.btn.classList.remove(a);u.btn.classList.add(t,f);y.isOk=true;u.box.value=true;if(A.isMobile()){u.btn.removeEventListener("touchstart",A.events.down,false);document.removeEventListener("touchmove",A.events.move,false)}else{u.btn.onmousedown=null;document.onmousemove=null}y.onOk&&typeof y.onOk=="function"&&y.onOk();return}var x=function(D){A.stop(D)};A.events.seup=x;if(A.isMobile()){document.addEventListener("touchend",x)}else{document.onmouseup=x}};l.prototype.stop=function(x){var w=this,v=w.config,u=v.container;if(w.isOk()){return}else{u.btn.style.left=0;u.bg.style.width=0;u.btn.style.transition="left 1s";u.bg.style.transition="width 1s"}document.onmousemove=null;document.onmouseup=null;if(w.isMobile()){document.removeEventListener("touchmove",w.events.move,false);document.removeEventListener("touchend",w.events.seup,false)}};l.prototype.events=function(){var z=this,y=z.config;if(!y.domid){return z.error("创建滑块验证失败")}var x=y.domid.find("."+i),w=y.domid.find("."+j),A=y.domid.find("."+q),v={box:m(y.domid),btn:m(x),bg:m(w),text:m(A)};y.container=v;v.distance=z.distance();var B=function(C){z.down(C)};z.events.down=B;if(z.isMobile()){v.btn.addEventListener("touchstart",z.events.down)}else{v.btn.onmousedown=B}var u=d(window);u.on("resize",y.domid,function(){if(z.config.isOk){z.resize()}else{clearTimeout(y.timer);y.timer=setTimeout(function(){z.render()},400)}})};h.render=function(u){var v=new l(u);return o.call(v)};s(g,h)}); \ No newline at end of file From b70d3765e9f4d08d02a41579d46967d12cfe6b88 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 18 Jun 2020 14:20:31 +0800 Subject: [PATCH 619/956] =?UTF-8?q?admin=E5=BA=93=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=BC=BA=E5=88=B6=E9=87=8D=E6=96=B0=E7=99=BB=E5=BD=95=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/db/token.lua | 5 +++++ lualib/admin/html/system/user/user.html | 20 ++++++++++++++++++++ lualib/admin/http/system/user.lua | 10 +++++++++- lualib/admin/locales/EN-US.lua | 1 + lualib/admin/locales/ZH-CN.lua | 1 + 5 files changed, 36 insertions(+), 1 deletion(-) diff --git a/lualib/admin/db/token.lua b/lualib/admin/db/token.lua index b2ba6d7d..a632d636 100644 --- a/lualib/admin/db/token.lua +++ b/lualib/admin/db/token.lua @@ -60,4 +60,9 @@ function token.token_to_userinfo (db, token) return token[1] end +-- 删除其他用户token信息 +function token.flush_all(db, uid) + return db:query(fmt([[DELETE FROM cfadmin_tokens WHERE uid != %u]], uid)) +end + return token diff --git a/lualib/admin/html/system/user/user.html b/lualib/admin/html/system/user/user.html index a83f3fe6..8d3781cf 100644 --- a/lualib/admin/html/system/user/user.html +++ b/lualib/admin/html/system/user/user.html @@ -42,6 +42,8 @@ diff --git a/lualib/admin/http/system/user.lua b/lualib/admin/http/system/user.lua index 4aea1c99..38957aab 100644 --- a/lualib/admin/http/system/user.lua +++ b/lualib/admin/http/system/user.lua @@ -128,7 +128,7 @@ function system.user_response (content) return json_encode({code = 400, data = null, msg = '3. token不存在或权限不足'}) end local user_info = user.user_info(db, exists.uid) - if not user_info or user_info.is_admin == 0 then + if not user_info or user_info.is_admin ~= 1 then return json_encode({code = 400, data = null, msg = '4. 用户权限不足'}) end local action = args.action @@ -143,6 +143,14 @@ function system.user_response (content) end return json_encode({code = 0, data = users, count = count}) end + -- 清除所有用户登录状态 + if action == "clear_login" then + local ok = user_token.flush_all(db, exists.uid) + if not ok then + return json_encode({code = 500, msg = "清除登录状态失败"}) + end + return json_encode({code = 0, msg = "清除登录状态成功"}) + end -- 添加用户 if action == 'add' then if not args.name or not args.username or not args.password then diff --git a/lualib/admin/locales/EN-US.lua b/lualib/admin/locales/EN-US.lua index e0b10d86..e09190c9 100644 --- a/lualib/admin/locales/EN-US.lua +++ b/lualib/admin/locales/EN-US.lua @@ -58,6 +58,7 @@ return { ['dashboard.menu.user_manage.table.options'] = 'Options', ['dashboard.menu.user_manage.table.reflush'] = 'Reflush', + ['dashboard.menu.user_manage.table.clear_login'] = "Clear login status", ['dashboard.menu.user_manage.table.adduser'] = 'Add User', ['dashboard.menu.user_manage.table.edituser'] = 'Edit User', diff --git a/lualib/admin/locales/ZH-CN.lua b/lualib/admin/locales/ZH-CN.lua index a15b80b6..8916589c 100644 --- a/lualib/admin/locales/ZH-CN.lua +++ b/lualib/admin/locales/ZH-CN.lua @@ -58,6 +58,7 @@ return { ['dashboard.menu.user_manage.table.options'] = '操作', ['dashboard.menu.user_manage.table.reflush'] = '刷新', + ['dashboard.menu.user_manage.table.clear_login'] = "清除登录状态", ['dashboard.menu.user_manage.table.adduser'] = '添加用户', ['dashboard.menu.user_manage.table.edituser'] = '编辑用户', ['dashboard.menu.user_manage.table.true'] = '是', From 581769e35a2cc098d9f2a698768a8616dc8e87c1 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 22 Jun 2020 17:08:47 +0800 Subject: [PATCH 620/956] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E5=BC=95=E7=94=A8=E4=BE=9D=E8=B5=96=E9=A1=BA=E5=BA=8F=E5=AF=BC?= =?UTF-8?q?=E8=87=B4=E7=9A=84C=20stack=20overflow=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/DB/init.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index 8e1633a4..1dc03c46 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -1,6 +1,7 @@ local class = require "class" -local mysql = require "protocol.mysql" + local timer = require "internal.Timer" +local mysql = require "protocol.mysql" local log = require "logging" local Log = log:new({ dump = true, path = 'DB'}) From 0c30e8a748c2f17944cf871e30e641a64211c2e1 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 16 Jul 2020 10:07:49 +0800 Subject: [PATCH 621/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Makefile | 6 +++--- src/core_ev.h | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Makefile b/src/Makefile index 782dce4b..49fc8f9e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -14,17 +14,17 @@ INCLUDES += -I. -L../ -I/usr/local/include # 使用jemalloc内存分配器请启用这段 # CFLAGS += -Wall -O3 -fPIC --shared -DJEMALLOC -fno-strict-aliasing -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib -# DLL += -ljemalloc -lev -llua -ldl +# DLL += -ljemalloc -lev -llua -ldl -lm # MACRO += -w -O3 -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib/ -DJEMALLOC # 使用tcmalloc内存分配器请启用这段 # CFLAGS += -Wall -O3 -fPIC --shared -DTCMALLOC -fno-strict-aliasing -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib -# DLL += -ltcmalloc -lev -llua -ldl +# DLL += -ltcmalloc -lev -llua -ldl -lm # MACRO += -w -O3 -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib -DTCMALLOC # 默认情况下使用系统内存分配器 CFLAGS += -Wall -O3 -fPIC --shared -fno-strict-aliasing -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib -DLL += -lev -llua -ldl +DLL += -lev -llua -ldl -lm MACRO += -w -O3 -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib build : diff --git a/src/core_ev.h b/src/core_ev.h index 774fb3f0..1b746381 100644 --- a/src/core_ev.h +++ b/src/core_ev.h @@ -10,9 +10,7 @@ #define EV_VERIFY 0 -#ifndef NO_USE_FLOOR - #define EV_USE_FLOOR 1 // 如果没有math.h库, 可以定义NO_USE_FLOOR -#endif +#define EV_USE_FLOOR 1 #define EV_FORK_ENABLE 0 #define EV_CHILD_ENABLE 0 From 94cc41336467ffc6737c2a1e6904dbdbd540cd2a Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 16 Jul 2020 10:24:48 +0800 Subject: [PATCH 622/956] =?UTF-8?q?=E6=B6=88=E9=99=A4aio=E5=9C=A8=E6=9F=90?= =?UTF-8?q?=E4=BA=9B=E7=89=88=E6=9C=AC=E4=B8=8B=E7=9A=84=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/laio.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/luaclib/src/laio.c b/luaclib/src/laio.c index c4b7edcb..3c7c5e9c 100644 --- a/luaclib/src/laio.c +++ b/luaclib/src/laio.c @@ -332,13 +332,15 @@ static int sp[2]; static void AIO_WANT_POLL(void) { // printf("AIO_WANT_POLL Called. 工作线程ID为: %d\n", pthread_self()); char event = '1'; - write(sp[1], &event, 1); + int wsize = write(sp[1], &event, 1); + (void)wsize; } static void AIO_DONE_POLL(void) { // printf("AIO_DONE_POLL Called. 主线程ID为: %d\n", pthread_self()); char event = '2'; - read(sp[0], &event, 1); + int rsize = read(sp[0], &event, 1); + (void)rsize; } static void AIO_EVENT(CORE_P_ core_io *io, int revents) { From 0cb9298521acc8a72b3f203fef1465ec9a9399d4 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 16 Jul 2020 10:28:12 +0800 Subject: [PATCH 623/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0sys=E5=BA=93=E7=9A=84?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E6=8F=90=E7=A4=BA=E4=B8=BA=E8=8B=B1=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lsys.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/luaclib/src/lsys.c b/luaclib/src/lsys.c index fa56953c..0b894e26 100644 --- a/luaclib/src/lsys.c +++ b/luaclib/src/lsys.c @@ -15,7 +15,7 @@ static int lipv4(lua_State *L){ size_t str_len = 0; const char *IP = luaL_checklstring(L, 1, &str_len); if (!IP || str_len == 0) - return luaL_error(L, "ipv4 error: 请至少传递一个string类型参数\n"); + return luaL_error(L, "ipv4 error: A parameter of type string is required\n"); lua_pushboolean(L, ipv4(IP)); return 1; } @@ -25,7 +25,7 @@ static int lipv6(lua_State *L){ size_t str_len = 0; const char *IP = luaL_checklstring(L, 1, &str_len); if (!IP || str_len == 0) - return luaL_error(L, "ipv6 error: 请至少传递一个string类型参数\n"); + return luaL_error(L, "ipv6 error: A parameter of type string is required\n"); lua_pushboolean(L, ipv6(IP)); return 1; } @@ -61,7 +61,7 @@ static int ldate(lua_State *L){ size_t str_len = 0; const char *fmt = luaL_checklstring(L, 1, &str_len); if (!fmt || str_len == 0) - return luaL_error(L, "Date: 错误的格式化方法"); + return luaL_error(L, "Date: Invalid format."); time_t timestamp = lua_tointeger(L, 2); if (0 >= timestamp) From b2a894f6fd713f0f41f5000972defaaaff6bdcad Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 20 Jul 2020 10:15:07 +0800 Subject: [PATCH 624/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0DB=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=E6=B1=A0=E5=B0=9D=E8=AF=95=E5=A4=B1=E8=B4=A5=E5=90=8E=E6=9C=AA?= =?UTF-8?q?=E4=BC=91=E7=9C=A0=E4=B8=8Ehttpd=E7=9A=84=E5=8D=8F=E8=AE=AE?= =?UTF-8?q?=E4=B8=BA=E8=83=BD=E6=AD=A3=E7=A1=AE=E5=BA=8F=E5=88=97=E5=8C=96?= =?UTF-8?q?cookie=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/DB/init.lua | 1 + lualib/protocol/http/init.lua | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index 1dc03c46..47d2e63a 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -50,6 +50,7 @@ local function DB_CREATE (opt) break end Log:WARN("The connection failed. The reasons are: [" .. err .. "], Try to reconnect after 3 seconds") + timer.sleep(3) db:close() end return db diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index f67951d2..7dba7309 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -427,7 +427,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) if typ == HTTP_PROTOCOL.API or typ == HTTP_PROTOCOL.USE then -- 如果httpd开启了记录Cookie字段, 则每次尝试是否deCookie if cookie and typ == HTTP_PROTOCOL.USE then - deCookie(content['headers']["Cookie"]) + deCookie(content['headers']["Cookie"] or content['headers']["cookie"]) end if type(cls) == "table" then local method = cls[lower(METHOD)] From 593920e52197ef9e23d7784a97a61c5975dd577d Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 22 Jul 2020 18:02:31 +0800 Subject: [PATCH 625/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0httpd=E7=9A=84?= =?UTF-8?q?=E5=AF=B9=E5=8F=91=E8=B5=B7args/form=E4=BA=8C=E7=BB=B4=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E7=BB=93=E6=9E=84(table/array)=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=E6=94=AF=E6=8C=81.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Form.lua | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lualib/httpd/Form.lua b/lualib/httpd/Form.lua index 949a7138..d2eaacf5 100644 --- a/lualib/httpd/Form.lua +++ b/lualib/httpd/Form.lua @@ -2,11 +2,16 @@ local url = require "url" local urlencode = url.encode local urldecode = url.decode +local sys = require "sys" +local new_tab = sys.new_tab + local type = type +local tonumber = tonumber local string = string local sub = string.sub local find = string.find +local match = string.match local splite = string.gmatch local table = table @@ -36,7 +41,17 @@ function form.urlencode(body) end local ARGS = {} for key, value in splite(body, "([^&]-)=([^&]+)") do - ARGS[urldecode(key)] = urldecode(value) + local tname, keyname = match(urldecode(key), "(.+)%[(.+)%]$") + if tname and keyname then + local t = ARGS[tname] + if not t then + t = new_tab(8, 8) + ARGS[tname] = t + end + t[tonumber(keyname) or keyname] = urldecode(value) + else + ARGS[urldecode(key)] = urldecode(value) + end end return ARGS end From 248c38391f5bd0479676b2cbeac9da199f5343a1 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 13 Aug 2020 21:51:03 +0800 Subject: [PATCH 626/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0rest=E8=AF=AD?= =?UTF-8?q?=E6=B3=95=E6=94=AF=E6=8C=81=E4=B8=8Eerror=20page=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E5=BC=80=E5=90=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Router.lua | 398 ++++++++++------- lualib/httpd/init.lua | 57 ++- lualib/protocol/http/code.lua | 2 +- lualib/protocol/http/init.lua | 138 +++--- lualib/protocol/http/pages.lua | 788 +++++++++++++++++++++++++++++++++ 5 files changed, 1146 insertions(+), 237 deletions(-) create mode 100644 lualib/protocol/http/pages.lua diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index 7a63aca2..cd4706b5 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -1,161 +1,237 @@ -local log = require "logging" -local Log = log:new({dump = true, path = 'httpd-Router'}) - -local aio = require "aio" -local aio_stat = aio.stat - -local url = require "url" -local url_decode = url.decode --- local url_encode = url.encode - -local new_tab = require("sys").new_tab - -local string = string -local byte = string.byte -local split = string.sub -local find = string.find -local match = string.match -local splite = string.gmatch -local spliter = string.gsub - -local type = type -local next = next -local ipairs = ipairs -local tonumber = tonumber -local tostring = tostring --- local io_open = io.open - -local slash = '\x2f' -- '/' -local slash2 = '\x2f\x2f' -- '//' -local point = '\x2e' -- '.' -local point2 = '\x2e\x2e' -- '..' - -local Router = { - API = 1, - USE = 2, - STATIC = 3, - WS = 4, -} - -local routes = {} -- 存储路由 - -local static = {} -- 静态文件路由 - --- 主要用作分割路径判断. -local function hex_route(route) - local tab = new_tab(32, 0) - for r in splite(route, '/([^ /%?]+)') do - if r ~= '' then - tab[#tab + 1] = r - end - end - return tab -end - --- 主要用作分割hash路由查找 -local function to_route(route) - local r = spliter(route, "([/]+)", '/') - if byte(r, #r) ~= byte(slash) then - return r - end - return split(r, 1, -2) -end - --- 检查是路径回退是否超出静态文件根目录 -local function check_path_deep (paths) - -- 检查是否合法路径. - local head, tail = paths[1], paths[#paths] - if head == point2 or tail == point or tail == point2 then - return true - end - local deep = 1 - for _, path in ipairs(paths) do - if path ~= point then - if path == point2 then - deep = deep - 1 - else - deep = deep + 1 - end - end - if deep <= 0 then - return true - end - end - return false -end - -local function registery_static (prefix, route_type) - if next(static) then - return - end - static.prefix = prefix - static.type = route_type -end - -local load_file - -local function registery_router (route, class, route_type) - routes[to_route(route)] = {class = class, type = route_type} -end - -local function find_route (method, path) - path = url_decode(split(path, 1, (find(path, '?') or 0) - 1)) - local t = routes[to_route(path)] - if t then - return t.class, t.type - end - local prefix, typ = static.prefix, static.type - if not prefix and not typ then - return - end - -- 非GET/HEAD方法不查找静态文件 - if method ~= 'GET' and method ~= 'HEAD' then - return - end - local tab = hex_route(path) - -- 凡是找到'../'并且检查路径回退已经超出静态文件根目录返回404 - if check_path_deep(tab) then - return - end - if not load_file then - load_file = function ( path ) - local filepath = prefix .. url_decode(path) - local stat = aio_stat(filepath) - if type(stat) ~= 'table' or stat.mode ~= 'file' then - return - end - return stat.size, filepath, match(path, '.+%.([%a]+)') - end - end - return load_file, typ -end - --- 查找路由 -function Router.find(method, path) - -- 凡是不以'/'开头的path都返回404 - if byte(path) ~= byte(slash) then - return - end - return find_route(method, path) -end - --- 注册静态文件查找路径 -function Router.static (...) - return registery_static(...) -end - --- 注册路由 -function Router.registery(route, class, route_type) - if type(route) ~= 'string' or route == '' then -- 过滤错误的路由输入 - return Log:WARN('Please Do not add empty string in route registery method :)') - end - if find(route, slash2) then -- 不允许出现路由出现[//] - return Log:WARN('Please Do not add [//] in route registery method :)') - end - if find(route, '^/%[%w+:.+%]$') then -- 不允许路由注册rest模式. - return Log:WARN('Please Do not add [/[type:key] in root route :)]') - end - return registery_router(route, class, route_type) -end - -return Router +local LOG = require "logging":new({dump = true, path = 'httpd-Router'}) + +local aio = require "aio" +local aio_stat = aio.stat + +local url = require "url" +local url_decode = url.decode +-- local url_encode = url.encode + +local new_tab = require("sys").new_tab + +local string = string +local byte = string.byte +local split = string.sub +local find = string.find +local match = string.match +local splite = string.gmatch +local spliter = string.gsub + +local type = type +local next = next +local pairs = pairs +local error = error +local ipairs = ipairs +local tonumber = tonumber +local tostring = tostring + +local slash = '\x2f' -- '/' +local slash2 = '\x2f\x2f' -- '//' +local point = '\x2e' -- '.' +local point2 = '\x2e\x2e' -- '..' + +local class = require "class" + +local Router = class("httpd-route") + +Router.API, Router.USE, Router.STATIC, Router.WS = 1, 2, 3, 4 + +function Router:ctor (opt) + self.rests = {} -- rest路由 + self.routes = {} -- 普通路由 + self.statics = {} -- 静态文件路由 + self.enable_rest = false -- 默认关闭rest路由支持 +end + +function Router:enable_rest_route () + self.enable_rest = true +end + +function Router:tonumber (v) + return tonumber(v) +end + +function Router:toarray (v, t) + if type(v) ~= 'string' or #v < 3 then + return v + end + local array = new_tab(32, 0) + for str in v:gmatch("[^,%[%]%{%}]+") do + if not t or t == 'string[]' then + array[#array+1] = str + else + array[#array+1] = tonumber(str) or tonumber(str:gsub("^0x", ""), 16) + end + end + return array +end + +-- 将rest路由转换匹配模式语法 +function Router:to_regex (r) + if type(r) ~= 'string' or not find(r, "[{}]") then + return + end + local regex = r + :gsub("%+", "%%+") + :gsub("%-", "%%-") + :gsub("%.", "%%.") + :gsub("%*", "%%*") + :gsub("[/]+", "/") + :gsub("/", "[/]-") + local args = {} + for p in splite(r, "%{([^/]-)%}") do + local t, v = match(p, "([^:{}]+):([^:{}]+)") + if t ~= 'string' and t ~= 'number' and t ~= 'string[]' and t ~= 'number[]' then + local name = match(p, "([^}{]+)") + if not name then + error("Invalid rest router syntex in [" .. r .. "], type is not support.") + end + t, v = name, "string" + end + local reg = "([^/]+)" + if t == 'number' then + reg = "([%%d]+[%%.]?[%%d]-)" + end + regex = spliter(regex, "{" .. spliter(t, "%[%]", "%%[%%]") .. ":" .. v .. "}", reg) + regex = spliter(regex, "{" .. v .. "}", reg) + args[#args+1] = { k = v, t = t } + end + if byte(r, #r) == byte("/") then + regex = regex .. "$" + else + regex = regex .. "[/]-$" + end + args["route"] = r + args["regex"] = "^" .. regex + -- LOG:DEBUG(args) + return regex, args +end + +function Router:match_regex (route, regex) + return match(route, regex) +end + +function Router:to_route (route) + local r = spliter(route, "([/]+)", '/') + if byte(r, #r) ~= byte(slash) then + return r + end + return split(r, 1, -2) +end + +function Router:hex_route (route) + local tab = new_tab(32, 0) + for r in splite(route, '/([^ /%?]+)') do + if r ~= '' then + tab[#tab + 1] = r + end + end + return tab +end + +-- 检查是路径回退是否超出静态文件根目录(是否合法路径.) +function Router:check_path_deep (paths) + local head, tail = paths[1], paths[#paths] + if head == point2 or tail == point or tail == point2 then + return true + end + local deep = 1 + for _, path in ipairs(paths) do + if path ~= point then + if path == point2 then + deep = deep - 1 + else + deep = deep + 1 + end + end + if deep <= 0 then + return true + end + end + return false +end + +-- 路由查找 +function Router:find (method, path) + -- 检查是否能O(1)定位普通路由 + path = url_decode(split(path, 1, (find(path, '?') or 0) - 1)) + local t = self.routes[self:to_route(path)] + if t then + return t.class, t.type + end + -- 检查是否需要查找rest路由 + if self.enable_rest then + for regex, cls in pairs(self.rests) do + local args_list = table.pack(self:match_regex(path, regex)) + if args_list and #args_list == #cls then + local args = new_tab(8, 0) + for index, arg in ipairs(args_list) do + local item = cls[index] + local t = item.t + local k = item.k + if t == 'number' then + args[k] = self:tonumber(arg) + elseif t == 'number[]' or t == 'string[]' then + args[k] = self:toarray(arg, t) + else + args[k] = arg + end + end + return cls.class, cls.type, args + end + end + end + -- 查找静态文件路由(文件) + local prefix, typ = self.statics.prefix, self.statics.type + -- LOG:DEBUG(prefix, typ) + if not prefix and not typ then + return + end + -- 非GET/HEAD方法不查找静态文件 + if method ~= 'GET' and method ~= 'HEAD' then + return + end + local tab = self:hex_route(path) + -- 凡是找到'../'并且检查路径回退已经超出静态文件根目录返回404 + if self:check_path_deep(tab) then + return + end + + if not self.load_file then + self.load_file = function ( path ) + local filepath = prefix .. url_decode(path) + local stat = aio_stat(filepath) + if type(stat) ~= 'table' or stat.mode ~= 'file' then + return + end + return stat.size, filepath, match(path, '.+%.([%a]+)') + end + end + return self.load_file, typ +end + +-- 注册rest语法路由与普通路由 +function Router:registery (route, class, route_type) + -- 过滤错误的路由输入 + if type(route) ~= 'string' or route == '' then + return LOG:WARN('Please Do not add empty string in route registery method :)') + end + if find(route, slash2) then -- 不允许出现路由出现[//] + return LOG:WARN('Please Do not add [//] in route registery method :)') + end + local regex, args = self:to_regex(route) + if regex and args then + self.rests[regex], args["class"], args["type"] = args, class, route_type + end + self.routes[self:to_route(route)] = {class = class, type = route_type} +end + +-- 注册静态文件路由 +function Router:static (route_prefix, route_type) + if next(self.statics) then + return + end + self.statics.prefix, self.statics.type = route_prefix, route_type +end + +return Router diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua index 01b4047d..24f69976 100644 --- a/lualib/httpd/init.lua +++ b/lualib/httpd/init.lua @@ -1,5 +1,5 @@ -local HTTP = require "protocol.http" -local HTTPD = require "httpd.Router" +local http = require "protocol.http" +local router = require "httpd.Router" local tcp = require "internal.TCP" local class = require "class" local log = require "logging" @@ -17,39 +17,46 @@ local io_write = io.write local toint = math.tointeger -- 请求解析 -local EVENT_DISPATCH = HTTP.EVENT_DISPATCH - --- 注册HTTP路由与静态文件 -local HTTP_STATIC_REGISTERY = HTTPD.static -local HTTP_ROUTE_REGISTERY = HTTPD.registery - +local EVENT_DISPATCH = http.EVENT_DISPATCH local httpd = class("httpd") function httpd:ctor(opt) - self.API = HTTPD.API - self.USE = HTTPD.USE - self.sock = tcp:new() + self.sock = tcp:new() + self.router = router:new() + self.WS = self.router.WS + self.API = self.router.API + self.USE = self.router.USE + self.STATIC = self.router.STATIC + self.__timeout = nil + self.__server = nil + self.__before_func = nil + self.__max_path_size = 1024 + self.__max_header_size = 65535 + self.__max_body_size = 1 * 1024 * 1024 + self.__enable_gzip = false + self.__enable_cookie = false + self.__enable_cros_timeout = nil end -- 用来注册WebSocket对象 function httpd:ws(route, class) if route and type(class) == "table" then - HTTP_ROUTE_REGISTERY(route, class, HTTPD.WS) + self.router:registery(route, class, self.WS) end end --- 用来注册接口 +-- 用来注册api路由 function httpd:api(route, class) if route and (type(class) == "table" or type(class) == "function")then - HTTP_ROUTE_REGISTERY(route, class, HTTPD.API) + self.router:registery(route, class, self.API) end end --- 用来注册普通路由 +-- 用来注册use路由 function httpd:use(route, class) if route and (type(class) == "table" or type(class) == "function") then - HTTP_ROUTE_REGISTERY(route, class, HTTPD.USE) + self.router:registery(route, class, self.USE) end end @@ -59,9 +66,9 @@ function httpd:group(target, prefix, array) for _, route in ipairs(array) do local r, c = route['route'], route['class'] if target == self.USE then - self:use(prefix..r, c) + self:use(prefix .. r, c) else - self:api(prefix..r, c) + self:api(prefix .. r, c) end end end @@ -122,9 +129,19 @@ function httpd:enable_gzip() self.__enable_gzip = true end +-- 开启rest路由注册支持 +function httpd:enable_rest () + self.router:enable_rest_route() +end + -- 是否记录解析cookie function httpd:enable_cookie () - self.__cookie = true + self.__enable_cookie = true +end + +-- 开启错误页面显示 +function httpd:enable_error_pages () + self.__enable_error_pages = true end -- 设置Cookie加密Key @@ -141,7 +158,7 @@ function httpd:static(foldor, ttl) if ttl and ttl > 0 then self.ttl = ttl end - return HTTP_STATIC_REGISTERY(self.foldor, HTTPD.STATIC) + return self.router:static(self.foldor, self.STATIC) end end diff --git a/lualib/protocol/http/code.lua b/lualib/protocol/http/code.lua index f7f614b4..16ed911c 100644 --- a/lualib/protocol/http/code.lua +++ b/lualib/protocol/http/code.lua @@ -68,4 +68,4 @@ return { [510] = "HTTP/1.1 510 Not Extended", [503] = "HTTP/1.1 511 Network Authentication Required", -} \ No newline at end of file +} diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index 7dba7309..36d8f368 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -30,14 +30,11 @@ local secCookie = Cookie.setSecure -- 设置Cookie加密字段 local seCookie = Cookie.serialization -- 序列化 local deCookie = Cookie.deserialization -- 反序列化 -local Router = require "httpd.Router" -local ROUTE_FIND = Router.find -local ROUTE_REGISTERY = Router.registery - local type = type local tostring = tostring local next = next -local pcall = pcall +local xpcall = xpcall +local pairs = pairs local ipairs = ipairs local time = os.time local lower = string.lower @@ -61,7 +58,7 @@ local COMMA = '\x2c' local SERVER = 'cf web/0.1' local HTTP_CODE = require "protocol.http.code" - +local PAGES = require "protocol.http.pages" local MIME = require "protocol.http.mime" local HTTP_PARSER = require "protocol.http.parser" @@ -96,12 +93,6 @@ function HTTP_PROTOCOL.FILEMIME(mime) return MIME[mime] end --- -- 路由注册 -HTTP_PROTOCOL.ROUTE_REGISTERY = ROUTE_REGISTERY - --- -- 路由查找 -HTTP_PROTOCOL.ROUTE_FIND = ROUTE_FIND - local function HTTP_DATE() return DATE("Date: %a, %d %b %Y %X GMT") -- return os.date("Date: %a, %d %b %Y %X GMT") @@ -116,6 +107,26 @@ local function req_time(ts) return now() - (ts or now()) end +local tab_copy +tab_copy = function (src) + local dst = new_tab(0, 32) + for k, v in pairs(src) do + dst[k] = type(v) == 'table' and tab_copy(v) or v + end + return dst +end + +-- 追踪调用栈信息 +local function trace (msg) + return debug.traceback(coroutine.running(), msg, 2) +end + +-- 安全运行回调函数 +local function safe_call (f, ...) + local ok, r1, r2, r3, r4, t5 = xpcall(f, trace, ...) + return ok, r1, r2, r3, r4, t5 +end + local function PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEADER) local content = new_tab(0, 8) if METHOD == "GET" then @@ -208,16 +219,24 @@ end -- 一些错误返回 local function ERROR_RESPONSE(http, code, path, ip, forword, method, speed) http:tolog(code, path, ip, X_Forwarded_FORMAT(forword) or ip, method, speed) - return concat({concat({ - REQUEST_STATUCODE_RESPONSE(code), - HTTP_DATE(), - 'Accept-Ranges: none', - 'Origin: *', - 'Allow: GET, POST, PUT, HEAD, OPTIONS', - 'Connection: close', - 'Content-length: 0', - 'Server: ' .. (http.__server or SERVER), - }, CRLF), CRLF2}) + local response = { + REQUEST_STATUCODE_RESPONSE(code), + HTTP_DATE(), + 'Accept-Ranges: none', + 'Origin: *', + 'Allow: GET, POST, PUT, HEAD, OPTIONS', + 'Connection: keep-alive', + 'Server: ' .. (http.__server or SERVER), + } + local error_page = PAGES[code] + if error_page and http.__enable_error_pages then + response[#response+1] = 'Content-length: ' .. #error_page + response[#response+1] = 'Content-length: ' .. #error_page + return concat({concat(response, CRLF), error_page}, CRLF2) + else + response[#response+1] = 'Content-length: 0' + return concat({concat(response, CRLF), CRLF2}) + end end -- WebSocket @@ -288,14 +307,17 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) local ttl = http.ttl local server = http.__server local timeout = http.__timeout or 0 - local cookie = http.__cookie + local enable_cookie = http.__enable_cookie local enable_gzip = http.__enable_gzip local enable_cros_timeout = http.__enable_cros_timeout local cookie_secure = http.__cookie_secure - local before_func = http._before_func + local before_func = http.__before_func local max_path_size = http.__max_path_size local max_header_size = http.__max_header_size local max_body_size = http.__max_body_size + local http_router = http.router + local route_find = http_router.find + local tolog = http.tolog secCookie(cookie_secure) -- 如果需要 local sock = tcp:new():set_fd(fd):timeout(timeout) while 1 do @@ -330,25 +352,26 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) return sock:close() end -- 这里根据PATH先查找路由, 如果没有直接返回404. - local cls, typ = ROUTE_FIND(METHOD, PATH) + -- local cls, typ, rest_args = http_router:find(METHOD, PATH) + local cls, typ, rest_args = route_find(http_router, METHOD, PATH) if not cls or not typ then sock:send(ERROR_RESPONSE(http, 404, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, req_time(start))) - return sock:close() + goto CONTINUE end -- 根据请求方法进行解析, 解析失败返回501 local ok, content = PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEADER) if not ok then if content == 413 then sock:send(ERROR_RESPONSE(http, 413, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, req_time(start))) - return sock:close() + goto CONTINUE end sock:send(ERROR_RESPONSE(http, 501, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, req_time(start))) - return sock:close() + goto CONTINUE end -- 如果请求使用了 HEAD 与 OPTIONS 方法, 这里会根据配置检查是否需要返回跨域标识. (除非您手动设置请求头部, 否则一般不会遇到此处逻辑.) -- 值得一提的是: 由于框架不支持范围请求(Accept-Ranges), 所以目前的处理方式将HEAD与OPTIONS都将返回0. 这样可以有助于快速完成检查请求. if not content then - http:tolog(200, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, req_time(start)) + tolog(http, 200, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, req_time(start)) local res = new_tab(16, 0) res[#res+1] = REQUEST_STATUCODE_RESPONSE(200) res[#res+1] = HTTP_DATE() @@ -362,62 +385,62 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) res[#res+1] = 'Access-Control-Max-Age: ' .. enable_cros_timeout end res[#res+1] = 'Server: ' .. (server or SERVER) - res[#res+1] = 'Connection: close' + res[#res+1] = 'Connection: keep-alive' res[#res+1] = 'Content-Length : 0' sock:send(concat(res, CRLF)..CRLF2) - return sock:close() + goto CONTINUE end content['ROUTE'] = HTTP_PROTOCOL[typ] content['method'], content['path'], content['headers'], content['client_ip'] = METHOD, PATH, HEADER, ipaddr -- before 函数只影响接口与view if before_func and (typ == HTTP_PROTOCOL.API or typ == HTTP_PROTOCOL.USE) then - local ok, code, data = pcall(before_func, content) + local ok, code, data = safe_call(before_func, tab_copy(content)) if not ok then -- before 函数执行出错 Log:ERROR(code) sock:send(ERROR_RESPONSE(http, 500, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, req_time(start))) - return sock:close() + goto CONTINUE end if code then if type(code) == "number" then if code < 200 or code > 500 then Log:ERROR("before function: Illegal return value") sock:send(ERROR_RESPONSE(http, 500, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, req_time(start))) - return sock:close() + goto CONTINUE elseif code == 301 or code == 302 then - http:tolog(code, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, req_time(start)) + tolog(http, code, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, req_time(start)) sock:send(concat({ REQUEST_STATUCODE_RESPONSE(code), HTTP_DATE(), - 'Connection: close', + 'Connection: keep-alive', 'Server: ' .. (server or SERVER), 'Location: ' .. (data or "https://github.com/CandyMi/core_framework"), }, CRLF)..CRLF2) - return sock:close() + goto CONTINUE elseif code ~= 200 then if data then if type(data) == 'string' and data ~= '' then - http:tolog(code, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, req_time(start)) + tolog(http, code, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, req_time(start)) sock:send(concat({concat({ REQUEST_STATUCODE_RESPONSE(code), HTTP_DATE(), 'Origin: *', 'Allow: GET, POST, PUT, HEAD, OPTIONS', 'Server: ' .. (server or SERVER), - 'Connection: close', + 'Connection: keep-alive', 'Content-Type: ' .. REQUEST_MIME_RESPONSE('html'), 'Content-Length: ' .. tostring(#data), }, CRLF), CRLF2, data})) - return sock:close() + goto CONTINUE end end sock:send(ERROR_RESPONSE(http, code, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, req_time(start))) - return sock:close() + goto CONTINUE end else sock:send(ERROR_RESPONSE(http, 401, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, req_time(start))) - return sock:close() + goto CONTINUE end else sock:send(ERROR_RESPONSE(http, 401, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, req_time(start))) - return sock:close() + goto CONTINUE end end @@ -426,22 +449,25 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) if typ == HTTP_PROTOCOL.API or typ == HTTP_PROTOCOL.USE then -- 如果httpd开启了记录Cookie字段, 则每次尝试是否deCookie - if cookie and typ == HTTP_PROTOCOL.USE then - deCookie(content['headers']["Cookie"] or content['headers']["cookie"]) + if enable_cookie and typ == HTTP_PROTOCOL.USE then + deCookie(HEADER["Cookie"] or HEADER["cookie"]) + end + if http_router.enable_rest then + content['query'] = rest_args end if type(cls) == "table" then local method = cls[lower(METHOD)] if not method or type(method) ~= 'function' then -- 注册的路由未实现这个方法 sock:send(ERROR_RESPONSE(http, 405, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, req_time(start))) - return sock:close() + goto CONTINUE end - local c = cls:new(content) - ok, body = pcall(method, c) + local c = cls:new(tab_copy(content)) + ok, body = safe_call(method, c) else - ok, body = pcall(cls, content) + ok, body = safe_call(cls, tab_copy(content)) end -- 如果httpd开启了记录Cookie字段, 则每次尝试是否需要seCookie - if cookie and typ == HTTP_PROTOCOL.USE then + if enable_cookie and typ == HTTP_PROTOCOL.USE then local Cookies = seCookie() for _, Cookie in ipairs(Cookies) do header[#header+1] = Cookie @@ -451,12 +477,12 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) if not ok then Log:ERROR(body or "empty response.") sock:send(ERROR_RESPONSE(http, 500, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, req_time(start))) - return sock:close() + goto CONTINUE end statucode = 200 insert(header, 1, REQUEST_STATUCODE_RESPONSE(statucode)) elseif typ == HTTP_PROTOCOL.WS then - local ok, msg = pcall(Switch_Protocol, http, cls, sock, HEADER, METHOD, VERSION, PATH, HEADER['X-Real-IP'] or ipaddr, start) + local ok, msg = safe_call(Switch_Protocol, http, cls, sock, HEADER, METHOD, VERSION, PATH, HEADER['X-Real-IP'] or ipaddr, start) if not ok then Log:ERROR(msg) end @@ -472,7 +498,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) if not body_len then statucode = 404 sock:send(ERROR_RESPONSE(http, statucode, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, req_time(start))) - return sock:close() + goto CONTINUE end statucode = 200 header[#header+1] = REQUEST_STATUCODE_RESPONSE(statucode) @@ -503,7 +529,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) if type(body) ~= 'string' or body == '' then Log:ERROR("Response Error ["..(split(PATH , 1, (find(PATH, '?') or 0 ) - 1)).."]: response must be a string and not empty.") sock:send(ERROR_RESPONSE(http, 500, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, req_time(start))) - return sock:close() + goto CONTINUE end local accept_encoding = HEADER['Accept-Encoding'] if type(accept_encoding) == 'string' and enable_gzip and #body >= 50 then @@ -542,7 +568,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) header[#header+1] = 'Access-Control-Max-Age: ' .. enable_cros_timeout end -- 不计算数据传输时间, 仅计算实际回调处理所用时间. - http:tolog(statucode, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, req_time(start)) + tolog(http, statucode, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, req_time(start)) -- 根据实际情况分块发送 local ok = send_header(sock, header) and send_body(sock, body, filepath) or false if not ok then @@ -557,6 +583,8 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) sock:send(ERROR_RESPONSE(http, 431, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, req_time(start))) return sock:close() end + -- 大部分情况下不需要主动关闭TCP连接, 这样有利于减少负载均衡器对连接池频繁销毁与建立. + :: CONTINUE :: end end diff --git a/lualib/protocol/http/pages.lua b/lualib/protocol/http/pages.lua new file mode 100644 index 00000000..74fe1383 --- /dev/null +++ b/lualib/protocol/http/pages.lua @@ -0,0 +1,788 @@ +local pages = {} + +pages[400] = [[ + + + + + 400 Bad Request + + + +
                  +

                  + 4 + 0 + 0 +

                  +

                  Bad Request

                  +
                  + + +]] + +pages[401] = [[ + + + + + 401 Unauthorized + + + +
                  +

                  + 4 + 0 + 1 +

                  +

                  Unauthorized

                  +
                  + + +]] + +pages[403] = [[ + + + + + 403 Forbidden + + + +
                  +

                  + 4 + 0 + 3 +

                  +

                  Forbidden

                  +
                  + + +]] + +pages[404] = [[ + + + + + 404 Page Not Found + + + +
                  +

                  + 4 + 0 + 4 +

                  +

                  Page Not Found

                  +
                  + + +]] + +pages[413] = [[ + + + + + 413 Payload Too Large + + + +
                  +

                  + 4 + 1 + 3 +

                  +

                  Payload Too Large

                  +
                  + + +]] + +pages[431] = [[ + + + + + 431 Request Header Fields Too Large + + + +
                  +

                  + 4 + 3 + 1 +

                  +

                  Request Header Fields Too Large

                  +
                  + + +]] + +pages[500] = [[ + + + + + 500 Internal Server Error + + + +
                  +

                  + 5 + 0 + 0 +

                  +

                  Internal Server Error

                  +
                  + + +]] + +pages[501] = [[ + + + + + 501 Not Implemented + + + +
                  +

                  + 5 + 0 + 1 +

                  +

                  Not Implemented

                  +
                  + + +]] + +pages[505] = [[ + + + + + 505 HTTP Version Not Supported + + + +
                  +

                  + 5 + 0 + 5 +

                  +

                  HTTP Version Not Supported

                  +
                  + + +]] + + + +return pages From cb9d3ecd01b008aa6edb7a1b3c5d8d9568e9595c Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 23 Aug 2020 18:26:14 +0800 Subject: [PATCH 627/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Ddns=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E4=B8=A2=E5=8C=85=E7=9A=84=E9=A2=91=E7=B9=81=E7=A8=8B=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/dns.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lualib/protocol/dns.lua b/lualib/protocol/dns.lua index c2a819a2..7397a179 100644 --- a/lualib/protocol/dns.lua +++ b/lualib/protocol/dns.lua @@ -237,7 +237,8 @@ local function dns_query(domain, ip_version) local req = pack_header()..pack_question(domain, msg) local times = 1 while 1 do - dns_client:send(req) + -- 每轮发送三次请求, 减少丢包几率 + local ok1, ok2, ok3 = dns_client:send(req), dns_client:send(req), dns_client:send(req) cf_sleep(1) if readable then return From 8ef679d2a9dcae14f32abf286ccd40faaa26a3cf Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 23 Aug 2020 18:27:04 +0800 Subject: [PATCH 628/956] =?UTF-8?q?=E5=8E=BB=E9=99=A4aio=E7=9A=84=E4=B8=80?= =?UTF-8?q?=E4=BA=9B=E6=97=A0=E7=94=A8=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/aio/init.lua | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/lualib/aio/init.lua b/lualib/aio/init.lua index 441cdd63..a309d3a8 100644 --- a/lualib/aio/init.lua +++ b/lualib/aio/init.lua @@ -26,13 +26,10 @@ local aio_readdir = laio.readdir local aio_readpath = laio.readpath local aio_truncate = laio.truncate -local new_tab = require "sys".new_tab - local type = type local assert = assert local toint = math.tointeger - local aio = new_tab(0, 1 << 16) local File = class("__AIO__") @@ -166,7 +163,7 @@ function File:close( ... ) return aio._close(fd) end --- 打开文件(始终以rw模式打开, 没有则会创建) +-- 打开文件,并返回File(始终以rw模式打开, 没有则会创建) function aio.open(filename) local fd, err = aio._open(filename) if not fd then @@ -177,7 +174,7 @@ function aio.open(filename) local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( ok, err ) - aio[t] = nil + aio[t] = nil return co_wakeup(t.current_co, ok, err) end) aio[t] = true @@ -187,7 +184,7 @@ function aio.open(filename) return File:new { fd = fd, path = filename } end --- 仅返回fd +-- 打开文件,并返回fd(始终以rw模式打开, 没有则会创建) function aio._open(filename) filename = assert(type(filename) == 'string' and filename ~= '' and filename ~= '.' and filename ~= '..' and filename, "Invalid filename.") local t = new_tab(0, 3) @@ -201,7 +198,7 @@ function aio._open(filename) return co_wait() end --- 打开文件, 如果 +-- 打开文件, 如果不存在则创建(返回File) function aio.create(filename) local fd, err = aio._create(filename) if not fd then @@ -222,6 +219,7 @@ function aio.create(filename) return File:new { fd = fd, path = filename, stat = stat } end +-- 打开文件, 如果存在则失败(返回fd) function aio._create(filename) filename = assert(type(filename) == 'string' and filename ~= '' and filename ~= '.' and filename ~= '..' and filename, "Invalid filename.") local t = new_tab(0, 3) @@ -286,7 +284,7 @@ function aio.mkdir(dir) local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( ok, err ) - aio[t] = nil + aio[t] = nil return co_wakeup(t.current_co, ok, err) end) aio[t] = true @@ -300,7 +298,7 @@ function aio.rmdir(dir) local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( ok, err ) - aio[t] = nil + aio[t] = nil return co_wakeup(t.current_co, ok, err) end) aio[t] = true @@ -318,7 +316,7 @@ function aio.stat(path) local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( list, err ) - aio[t] = nil + aio[t] = nil return co_wakeup(t.current_co, list, err) end) aio[t] = true @@ -332,7 +330,7 @@ function aio.dir(path) local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( dirs ) - aio[t] = nil + aio[t] = nil return co_wakeup(t.current_co, dirs ) end) aio[t] = true @@ -347,7 +345,7 @@ function aio.rename(old_name, new_name) local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( ok ) - aio[t] = nil + aio[t] = nil return co_wakeup(t.current_co, ok) end) aio[t] = true @@ -366,7 +364,7 @@ function aio.readpath(path) local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( path ) - aio[t] = nil + aio[t] = nil return co_wakeup(t.current_co, path ) end) aio[t] = true @@ -381,7 +379,7 @@ function aio.truncate(filename, length) local t = new_tab(0, 3) t.current_co = co_self() t.event_co = co_new(function ( ok, err ) - aio[t] = nil + aio[t] = nil return co_wakeup(t.current_co, ok, err) end) aio[t] = true @@ -427,4 +425,4 @@ function aio.remove(filename) return co_wait() end -return aio \ No newline at end of file +return aio From 7b77936c7104157bee5329b0db4ec23c2130256b Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 23 Aug 2020 18:28:37 +0800 Subject: [PATCH 629/956] =?UTF-8?q?=E8=B7=9F=E8=BF=9Bhttp=5Fparse=E7=9A=84?= =?UTF-8?q?bug=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lhttpparser/httpparser.c | 53 +++++++++++++++++----------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/luaclib/src/lhttpparser/httpparser.c b/luaclib/src/lhttpparser/httpparser.c index 6d2fb285..b216356e 100644 --- a/luaclib/src/lhttpparser/httpparser.c +++ b/luaclib/src/lhttpparser/httpparser.c @@ -36,8 +36,6 @@ #endif #include "httpparser.h" -/* $Id$ */ - #if __GNUC__ >= 3 #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) @@ -73,9 +71,9 @@ #define ADVANCE_TOKEN(tok, toklen) \ do { \ const char *tok_start = buf; \ - static const char ALIGNED(16) ranges2[] = "\000\040\177\177"; \ + static const char ALIGNED(16) ranges2[16] = "\000\040\177\177"; \ int found2; \ - buf = findchar_fast(buf, buf_end, ranges2, sizeof(ranges2) - 1, &found2); \ + buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \ if (!found2) { \ CHECK_EOF(); \ } \ @@ -138,15 +136,11 @@ static const char *get_token_to_eol(const char *buf, const char *buf_end, const const char *token_start = buf; #ifdef __SSE4_2__ - static const char ranges1[] = "\0\010" - /* allow HT */ - "\012\037" - /* allow SP and up to but not including DEL */ - "\177\177" - /* allow chars w. MSB set */ - ; + static const char ALIGNED(16) ranges1[16] = "\0\010" /* allow HT */ + "\012\037" /* allow SP and up to but not including DEL */ + "\177\177"; /* allow chars w. MSB set */ int found; - buf = findchar_fast(buf, buf_end, ranges1, sizeof(ranges1) - 1, &found); + buf = findchar_fast(buf, buf_end, ranges1, 6, &found); if (found) goto FOUND_CTL; #else @@ -359,9 +353,15 @@ static const char *parse_request(const char *buf, const char *buf_end, const cha /* parse request line */ ADVANCE_TOKEN(*method, *method_len); - ++buf; + do { + ++buf; + CHECK_EOF(); + } while (*buf == ' '); ADVANCE_TOKEN(*path, *path_len); - ++buf; + do { + ++buf; + CHECK_EOF(); + } while (*buf == ' '); if (*method_len == 0 || *path_len == 0) { *ret = -1; return NULL; @@ -418,10 +418,14 @@ static const char *parse_response(const char *buf, const char *buf_end, int *min return NULL; } /* skip space */ - if (*buf++ != ' ') { + if (*buf != ' ') { *ret = -1; return NULL; } + do { + ++buf; + CHECK_EOF(); + } while (*buf == ' '); /* parse status code, we want at least [:digit:][:digit:][:digit:] to try to parse */ if (buf_end - buf < 4) { *ret = -2; @@ -429,13 +433,22 @@ static const char *parse_response(const char *buf, const char *buf_end, int *min } PARSE_INT_3(status); - /* skip space */ - if (*buf++ != ' ') { - *ret = -1; + /* get message includig preceding space */ + if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) { return NULL; } - /* get message */ - if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) { + if (*msg_len == 0) { + /* ok */ + } else if (**msg == ' ') { + /* Remove preceding space. Successful return from `get_token_to_eol` guarantees that we would hit something other than SP + * before running past the end of the given buffer. */ + do { + ++*msg; + --*msg_len; + } while (**msg == ' '); + } else { + /* garbage found after status code */ + *ret = -1; return NULL; } From e7e3489a2cdefe6dd0b0071227941f4c04978b10 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 23 Aug 2020 18:29:28 +0800 Subject: [PATCH 630/956] =?UTF-8?q?http=20get=E4=B8=8Epost=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E5=BC=80=E5=A7=8B=E6=94=AF=E6=8C=81=E4=BA=8C=E7=BB=B4?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E7=BB=93=E6=9E=84=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Form.lua | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lualib/httpd/Form.lua b/lualib/httpd/Form.lua index 949a7138..d2eaacf5 100644 --- a/lualib/httpd/Form.lua +++ b/lualib/httpd/Form.lua @@ -2,11 +2,16 @@ local url = require "url" local urlencode = url.encode local urldecode = url.decode +local sys = require "sys" +local new_tab = sys.new_tab + local type = type +local tonumber = tonumber local string = string local sub = string.sub local find = string.find +local match = string.match local splite = string.gmatch local table = table @@ -36,7 +41,17 @@ function form.urlencode(body) end local ARGS = {} for key, value in splite(body, "([^&]-)=([^&]+)") do - ARGS[urldecode(key)] = urldecode(value) + local tname, keyname = match(urldecode(key), "(.+)%[(.+)%]$") + if tname and keyname then + local t = ARGS[tname] + if not t then + t = new_tab(8, 8) + ARGS[tname] = t + end + t[tonumber(keyname) or keyname] = urldecode(value) + else + ARGS[urldecode(key)] = urldecode(value) + end end return ARGS end From 837f617fb5c59c340afd256eba313aaf55598823 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 23 Aug 2020 19:40:45 +0800 Subject: [PATCH 631/956] =?UTF-8?q?=E4=B8=BAhttpd=E5=A2=9E=E5=8A=A0ssl?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 83 ++++++++-- lualib/httpd/init.lua | 25 ++- lualib/internal/TCP.lua | 292 ++++++++++++++++++++++++++-------- lualib/protocol/http/init.lua | 14 +- 4 files changed, 330 insertions(+), 84 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 6e13ac0b..aba1dd42 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -38,8 +38,9 @@ static inline void SETSOCKETOPT(int sockfd, int mode){ /* 地址重用 */ #ifdef SO_REUSEADDR ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &Enable, sizeof(Enable)); - if (ret) { + if (ret < 0) { LOG("ERROR", "Setting SO_REUSEADDR failed."); + LOG("ERROR", strerror(errno)); return _exit(-1); } #endif @@ -48,8 +49,9 @@ static inline void SETSOCKETOPT(int sockfd, int mode){ #ifdef SO_REUSEPORT if (mode == SERVER) { ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &Enable, sizeof(Enable)); - if (ret) { + if (ret < 0) { LOG("ERROR", "Setting SO_REUSEPORT failed."); + LOG("ERROR", strerror(errno)); return _exit(-1); } } @@ -58,8 +60,9 @@ static inline void SETSOCKETOPT(int sockfd, int mode){ /* 关闭小包延迟合并算法 */ #ifdef TCP_NODELAY ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &Enable, sizeof(Enable)); - if (ret){ + if (ret < 0){ LOG("ERROR", "Setting TCP_NODELAY failed."); + LOG("ERROR", strerror(errno)); return _exit(-1); } #endif @@ -69,8 +72,9 @@ static inline void SETSOCKETOPT(int sockfd, int mode){ #ifndef __MSYS__ /* 在仿真环境中会操作始终会失败 */ if (mode != None){ ret = setsockopt(sockfd, IPPROTO_TCP, SO_KEEPALIVE, &Enable , sizeof(Enable)); - if (ret){ + if (ret < 0){ LOG("ERROR", "Setting SO_KEEPALIVE failed."); + LOG("ERROR", strerror(errno)); return _exit(-1); } } @@ -81,8 +85,22 @@ static inline void SETSOCKETOPT(int sockfd, int mode){ #ifdef TCP_DEFER_ACCEPT if (mode == SERVER) { ret = setsockopt(sockfd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &Enable, sizeof(Enable)); - if (ret){ + if (ret < 0){ LOG("ERROR", "Setting TCP_DEFER_ACCEPT failed."); + LOG("ERROR", strerror(errno)); + return _exit(-1); + } + } +#endif + +/* 开启快速连接复用 */ +#if defined(TCP_FASTOPEN) && defined(MSG_FASTOPEN) + if (mode == SERVER) { + int len = 5; + ret = setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN, &len, sizeof(len)); + if (ret < 0){ + LOG("ERROR", "Setting TCP_FASTOPEN failed."); + LOG("ERROR", strerror(errno)); return _exit(-1); } } @@ -92,8 +110,9 @@ static inline void SETSOCKETOPT(int sockfd, int mode){ #ifdef TCP_KEEPIDLE int keepidle = 30; ret = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle , sizeof(keepidle)); - if (ret){ + if (ret < 0){ LOG("ERROR", "Setting TCP_KEEPIDLE failed."); + LOG("ERROR", strerror(errno)); return _exit(-1); } #endif @@ -102,8 +121,9 @@ static inline void SETSOCKETOPT(int sockfd, int mode){ #ifdef TCP_KEEPCNT int keepcount = 3; ret = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, &keepcount , sizeof(keepcount)); - if (ret){ + if (ret < 0){ LOG("ERROR", "Setting TCP_KEEPCNT failed."); + LOG("ERROR", strerror(errno)); return _exit(-1); } #endif @@ -112,8 +132,9 @@ static inline void SETSOCKETOPT(int sockfd, int mode){ #ifdef TCP_KEEPINTVL int keepinterval = 5; ret = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, &keepinterval , sizeof(keepinterval)); - if (ret){ + if (ret < 0){ LOG("ERROR", "Setting TCP_KEEPINTVL failed."); + LOG("ERROR", strerror(errno)); return _exit(-1); } #endif @@ -123,8 +144,9 @@ static inline void SETSOCKETOPT(int sockfd, int mode){ if (mode != None) { int No = 0; ret = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&No, sizeof(No)); - if (ret){ + if (ret < 0){ LOG("ERROR", "Setting IPV6_V6ONLY failed."); + LOG("ERROR", strerror(errno)); return _exit(-1); } } @@ -791,18 +813,19 @@ static int tcp_sslconnect(lua_State *L){ SSL *ssl = (SSL*) lua_touserdata(L, 1); if (!ssl) return 0; - int status = SSL_do_handshake(ssl); - if (status >= 1) { + int reason = SSL_get_error(ssl, SSL_do_handshake(ssl)); + /* 握手结束 -> 握手成功 */ + if (reason == SSL_ERROR_NONE){ lua_pushboolean(L, 1); return 1; } - int ERR = SSL_get_error(ssl, status); - // printf("status = %d, ERR = %d, SSL_ERROR_WANT_READ == %d, SSL_ERROR_WANT_WRITE == %d\n", status, ERR, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE); - if (SSL_ERROR_WANT_READ == ERR || SSL_ERROR_WANT_WRITE == ERR) { + /* 需要再进一步交互 */ + if (SSL_ERROR_WANT_READ == reason || SSL_ERROR_WANT_WRITE == reason){ lua_pushnil(L); - lua_pushinteger(L, ERR - 1); + lua_pushinteger(L, reason - 1); return 2; } + /* 握手失败 */ return 0; } @@ -834,6 +857,34 @@ static int tcp_start(lua_State *L){ } +static int ssl_set_connect_mode(lua_State *L) { + SSL *ssl = (SSL*) lua_touserdata(L, 1); + if (!ssl) + return luaL_error(L, "Invalid SSL ssl."); + + SSL_CTX *ctx = (SSL_CTX*) lua_touserdata(L, 2); + if (!ctx) + return luaL_error(L, "Invalid SSL_CTX ctx."); + + SSL_set_connect_state(ssl); + + return 1; +} + +static int ssl_set_accept_mode(lua_State *L) { + SSL *ssl = (SSL*) lua_touserdata(L, 1); + if (!ssl) + return luaL_error(L, "Invalid SSL ssl."); + + SSL_CTX *ctx = (SSL_CTX*) lua_touserdata(L, 2); + if (!ctx) + return luaL_error(L, "Invalid SSL_CTX ctx."); + + SSL_set_accept_state(ssl); + + return 1; +} + // 加载证书 static int ssl_set_certificate(lua_State *L) { @@ -1066,6 +1117,8 @@ luaopen_tcp(lua_State *L){ {"new_unixsock_fd", new_unixsock_fd}, {"sendfile", tcp_sendfile}, {"ssl_verify", ssl_verify}, + {"ssl_set_accept_mode", ssl_set_accept_mode}, + {"ssl_set_connect_mode", ssl_set_connect_mode}, {"ssl_set_privatekey", ssl_set_privatekey}, {"ssl_set_certificate", ssl_set_certificate}, {"ssl_set_userdata_key", ssl_set_userdata_key}, diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua index 24f69976..ac4f4a5f 100644 --- a/lualib/httpd/init.lua +++ b/lualib/httpd/init.lua @@ -17,7 +17,7 @@ local io_write = io.write local toint = math.tointeger -- 请求解析 -local EVENT_DISPATCH = http.EVENT_DISPATCH +local RAW_DISPATCH = http.RAW_DISPATCH local httpd = class("httpd") @@ -198,17 +198,29 @@ function httpd:listen(ip, port, backlog) self.port = port self.sock:set_backlog(toint(backlog)) return assert(self.sock:listen(ip or "0.0.0.0", toint(port), function (fd, ipaddr) - return EVENT_DISPATCH(fd, match(ipaddr, '^::[f]+:(.+)') or ipaddr, self) + return RAW_DISPATCH(fd, match(ipaddr, '^::[f]+:(.+)') or ipaddr, self) end)) end +function httpd:listen_ssl(ip, port, backlog, key, cert, pw) + assert(type(ip) == 'string' and toint(port), "httpd error: invalid ip or port") + self.ip, self.ssl_port = ip, toint(port) or 443 + self.ssl_key, self.ssl_cert, self.ssl_pw = key, cert, pw + self.sock:set_backlog(toint(backlog)) + return assert(self.sock:listen_ssl(ip or "0.0.0.0", self.ssl_port, { cert = self.ssl_cert, key = self.ssl_key, pw = self.ssl_pw }, + function (sock, ipaddr) + return RAW_DISPATCH(sock, match(ipaddr, '^::[f]+:(.+)') or ipaddr, self) + end) + ) +end + -- 监听unixsock function httpd:listenx(unix_domain_path, backlog) assert(type(unix_domain_path) == 'string' and unix_domain_path ~= '', "httpd error: invalid unix domain path") self.unix_domain_path = unix_domain_path self.sock:set_backlog(toint(backlog)) return assert(self.sock:listen_ex(unix_domain_path, true, function (fd, ipaddr) - return EVENT_DISPATCH(fd, match(ipaddr, '^::[f]+:(.+)') or ipaddr, self) + return RAW_DISPATCH(fd, match(ipaddr, '^::[f]+:(.+)') or ipaddr, self) end)) end @@ -228,6 +240,13 @@ function httpd:run() io_write(fmt('\27[32m[%s] [INFO]\27[0m httpd listen: %s\n', os_date("%Y/%m/%d %H:%M:%S"), self.unix_domain_path)) end + if self.ssl_key and self.ssl_cert then + if self.logging then + self.logging:dump(fmt('[%s] [INFO] httpd ssl listen: %s:%s\n', os_date("%Y/%m/%d %H:%M:%S"), "0.0.0.0", self.ssl_port)) + end + io_write(fmt('\27[32m[%s] [INFO]\27[0m httpd ssl listen: %s:%s\n', os_date("%Y/%m/%d %H:%M:%S"), "0.0.0.0", self.ssl_port)) + end + if self.logging then self.logging:dump(fmt('[%s] [INFO] httpd Web Server Running...\n', os_date("%Y/%m/%d %H:%M:%S"))) end diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index 5a3c7414..c57cbba3 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -36,7 +36,7 @@ local tcp_stop = tcp.stop local tcp_free_ssl = tcp.free_ssl local tcp_close = tcp.close local tcp_connect = tcp.connect -local tcp_ssl_do_handshak = tcp.ssl_connect +local tcp_ssl_do_handshake = tcp.ssl_connect local tcp_read = tcp.read local tcp_sslread = tcp.ssl_read local tcp_write = tcp.write @@ -54,6 +54,8 @@ local tcp_new_unixsock_fd = tcp.new_unixsock_fd local tcp_ssl_verify = tcp.ssl_verify local tcp_ssl_set_fd = tcp.ssl_set_fd +local tcp_ssl_set_accept_mode = tcp.ssl_set_accept_mode +local tcp_ssl_set_connect_mode = tcp.ssl_set_connect_mode local tcp_ssl_set_privatekey = tcp.ssl_set_privatekey local tcp_ssl_set_certificate = tcp.ssl_set_certificate local tcp_ssl_set_userdata_key = tcp.ssl_set_userdata_key @@ -76,31 +78,50 @@ end local TCP = class("TCP") function TCP:ctor(...) - +--[[ + -- 当前socke运行模式 + self.mode = nil + -- 默认关闭定时器 + self._timeout = nil + -- 默认backlog + self._backlog = 128 + -- connect 或 accept 得到的文件描述符 + self.fd = nil + -- listen unix domain socket 文件描述符 + self.ufd = nil + -- ssl 对象 + self.ssl = nil + self.ssl_ctx = nil + -- 密钥与证书路径 + self.privatekey_path = nil + self.certificate_path = nil + -- 配套密码 + self.ssl_password = nil +--]] end -- 超时时间 function TCP:timeout(Interval) - if Interval and Interval > 0 then - self._timeout = Interval - end - return self + if Interval and Interval > 0 then + self._timeout = Interval + end + return self end -- 设置fd function TCP:set_fd(fd) - if not self.fd then - self.fd = fd - end - return self + if not self.fd then + self.fd = fd + end + return self end -- 设置backlog function TCP:set_backlog(backlog) - if type(backlog) == 'number' and backlog > 0 then - self._backlog = backlog - end - return self + if type(backlog) == 'number' and backlog > 0 then + self._backlog = backlog + end + return self end -- 开启验证 @@ -137,12 +158,15 @@ function TCP:ssl_set_password(password) self.ssl, self.ssl_ctx = tcp_ssl_new() end assert(type(password) == 'string', "not have ssl or ssl_ctx.") - self.password = password - return tcp_ssl_set_userdata_key(self.ssl, self.ssl_ctx, self.password) + self.ssl_password = password + return tcp_ssl_set_userdata_key(self.ssl, self.ssl_ctx, self.ssl_password) end -- sendfile的文件fd使用aio库来打开与关闭可以减少阻塞. function TCP:sendfile (filename, offset) + if self.ssl or self.ssl_ctx then + return self:ssl_sendfile(filename, offset) + end if type(filename) == 'string' and filename ~= '' then local fd, err = aio_open(filename) if not fd then @@ -166,10 +190,25 @@ function TCP:sendfile (filename, offset) end end +-- 假装自己是sendfile +function TCP:ssl_sendfile(filename, offset) + if type(filename) == 'string' and filename ~= '' then + local f, err = aio.open(filename) + if not f then + return nil, err + end + local data, err = f:readall() + f:close() + if not data then + return nil, err + end + return self:ssl_send(data) + end +end + function TCP:send(buf) if self.ssl then - Log:ERROR("Please use ssl_send method :)") - return nil, "Please use ssl_send method" + return self:ssl_send(buf) end if not self.fd or type(buf) ~= 'string' or buf == '' then return @@ -258,9 +297,21 @@ function TCP:readline(sp, no_sp) end tcp_push(self.READ_IO) tcp_stop(self.READ_IO) + self.READ_IO = nil + self.read_co = nil + self.read_current_co = nil return co_wakeup(co, buf, buf_size) end if not buf and not buf_size then + if self.timer then + self.timer:stop() + self.timer = nil + end + tcp_push(self.READ_IO) + tcp_stop(self.READ_IO) + self.READ_IO = nil + self.read_co = nil + self.read_current_co = nil return co_wakeup(co, nil, "server close") end co_wait() @@ -304,9 +355,21 @@ function TCP:ssl_readline(sp, no_sp) end tcp_push(self.READ_IO) tcp_stop(self.READ_IO) + self.READ_IO = nil + self.read_co = nil + self.read_current_co = nil return co_wakeup(co, buf, buf_size) end if not buf and not buf_size then + if self.timer then + self.timer:stop() + self.timer = nil + end + tcp_push(self.READ_IO) + tcp_stop(self.READ_IO) + self.READ_IO = nil + self.read_co = nil + self.read_current_co = nil return co_wakeup(co, nil, "server close") end co_wait() @@ -327,8 +390,7 @@ end function TCP:recv(bytes) if self.ssl then - Log:ERROR("Please use ssl_recv method :)") - return nil, "Please use ssl_recv method :)" + return self:ssl_recv(bytes) end if not self.fd then return @@ -350,7 +412,7 @@ function TCP:recv(bytes) tcp_stop(self.READ_IO) self.READ_IO = nil self.read_co = nil - self.read_current_co =nil + self.read_current_co = nil if not buf then return co_wakeup(co) end @@ -428,13 +490,14 @@ function TCP:ssl_recv(bytes) end function TCP:listen(ip, port, cb) + self.mode = "server" self.LISTEN_IO = tcp_pop() self.fd = tcp_new_server_fd(ip, port, self._backlog or 128) if not self.fd then return nil, "Listen port failed. Please check if the port is already occupied." end if type(cb) ~= 'function' then - return nil, "Listen_ex function was invalid." + return nil, "Listen function was invalid." end self.listen_co = co_new(function (fd, ipaddr) while 1 do @@ -447,7 +510,59 @@ function TCP:listen(ip, port, cb) return true, tcp_listen(self.LISTEN_IO, self.fd, self.listen_co) end +local function ssl_accept(callback, opt, fd, ipaddr) + local sock = TCP:new() + sock.mode = "server" + sock:set_fd(fd) + -- :timeout(0.1) + sock.ssl, sock.ssl_ctx = tcp_ssl_new_fd(fd) + if type(opt.pw) == 'string' and opt.pw ~= '' then + sock:ssl_set_password(opt.pw) + end + sock:ssl_set_certificate(opt.cert) + sock:ssl_set_privatekey(opt.key) + tcp_ssl_set_accept_mode(sock.ssl, sock.ssl_ctx) + if not sock:ssl_handshake() then + return sock:close() + end + return callback(sock, ipaddr) +end + +function TCP:listen_ssl(ip, port, opt, cb) + self.mode = "server" + self.LISTEN_SSL_IO = tcp_pop() + self.sfd = tcp_new_server_fd(ip, port, self._backlog or 128) + if not self.sfd then + return nil, "Listen port failed. Please check if the port is already occupied." + end + if type(opt) ~= 'table' then + return nil, "ssl listen must have key/cert/pw(optional)." + end + if type(opt.pw) == 'string' and opt.pw ~= '' then + self:ssl_set_password(opt.pw) + end + self:ssl_set_certificate(opt.cert) + self:ssl_set_privatekey(opt.key) + -- 验证证书与私钥有效性 + if not self:ssl_set_verify() then + return nil, "The certificate does not match the private key." + end + if type(cb) ~= 'function' then + return nil, "Listen function was invalid." + end + self.listen_ssl_co = co_new(function (fd, ipaddr) + while 1 do + if fd and ipaddr then + co_spwan(ssl_accept, cb, opt, fd, ipaddr) + fd, ipaddr = co_wait() + end + end + end) + return true, tcp_listen(self.LISTEN_SSL_IO, self.sfd, self.listen_ssl_co) +end + function TCP:listen_ex(unix_domain_path, removed, cb) + self.mode = "server" self.LISTEN_EX_IO = tcp_pop() self.ufd = tcp_new_unixsock_fd(unix_domain_path, removed or true, self._backlog or 128) if not self.ufd then @@ -468,6 +583,7 @@ function TCP:listen_ex(unix_domain_path, removed, cb) end function TCP:connect(domain, port) + self.mode = "client" local ok, IP = dns_resolve(domain) if not ok then return nil, "Can't resolve this domain or ip:"..(domain or IP or "") @@ -490,7 +606,7 @@ function TCP:connect(domain, port) self.CONNECT_IO = nil self.connect_co = nil if connected then - return co_wakeup(co, true) + return co_wakeup(co, true) end return co_wakeup(co, false, 'connect failed') end) @@ -510,57 +626,83 @@ end function TCP:ssl_connect(domain, port) local ok, err = self:connect(domain, port) if not ok then - return nil, "domain connect error." + return nil, err end return self:ssl_handshake() end -function TCP:ssl_handshake() - if not self.ssl_ctx and not self.ssl then - self.ssl, self.ssl_ctx = tcp_ssl_new_fd(self.fd) - else - tcp_ssl_set_fd(self.ssl, self.fd) - end +local function event_wait(self, event) + -- 当前协程对象 local co = co_self() + self.connect_current_co = co + -- 从对象池之中取出一个观察者对象 self.CONNECT_IO = tcp_pop() - self.connect_current_co = co_self() - self.connect_co = co_new(function () - local EVENTS = EVENT_WRITE - while 1 do - local ok, EVENT = tcp_ssl_do_handshak(self.ssl) - if ok or not EVENT then - if self.timer then - self.timer:stop() - self.timer = nil - end - tcp_push(self.CONNECT_IO) - tcp_stop(self.CONNECT_IO) - self.CONNECT_IO = nil - self.connect_co = nil - self.connect_current_co = nil - return co_wakeup(co, ok) - end - if EVENTS ~= EVENT then - EVENTS = EVENT - tcp_stop(self.CONNECT_IO) - tcp_start(self.CONNECT_IO, self.fd, EVENTS, self.connect_co) - end - co_wait() + -- 读/写回调 + self.connect_co = co_new(function ( ... ) + -- 如果事件在超时之前到来需要停止定时器 + if self.timer then + self.timer:stop() + self.timer = nil end + -- 停止当前IO事件观察者并且将其放入对象池之中 + tcp_stop(self.CONNECT_IO) + tcp_push(self.CONNECT_IO) + self.CONNECT_IO = nil + self.connect_co = nil + self.connect_current_co = nil + -- 唤醒协程 + return co_wakeup(co, true) end) + -- 定时器回调 self.timer = ti_timeout(self._timeout, function ( ... ) - tcp_push(self.CONNECT_IO) - tcp_stop(self.CONNECT_IO) - self.timer = nil - self.CONNECT_IO = nil - self.connect_co = nil - self.connect_current_co = nil - return co_wakeup(co, nil, 'ssl_connect timeout.') + -- 停止当前IO事件观察者并且将其放入对象池之中 + tcp_push(self.CONNECT_IO) + tcp_stop(self.CONNECT_IO) + self.timer = nil + self.CONNECT_IO = nil + self.connect_co = nil + self.connect_current_co = nil + -- 唤醒协程 + return co_wakeup(co, nil, 'connect timeout.') end) - tcp_start(self.CONNECT_IO, self.fd, EVENT_WRITE, self.connect_co) + -- print(self.CONNECT_IO, self.fd, event, self.connect_co) + -- 注册I/O事件 + tcp_start(self.CONNECT_IO, self.fd, event, self.connect_co) + -- 让出执行权 return co_wait() end +function TCP:ssl_handshake() + -- 如果是服务端模式, 需要等待客户端先返送hello信息. + -- 如果是客户端, 则需要先发送hello信息. + if self.mode == "server" then + local ok, err = event_wait(self, EVENT_READ) + if not ok then + return nil, err + end + else + if not self.ssl_ctx and not self.ssl then + self.ssl, self.ssl_ctx = tcp_ssl_new_fd(self.fd) + else + tcp_ssl_set_fd(self.ssl, self.fd) + end + end + -- 开始握手 + while 1 do + local ok, event = tcp_ssl_do_handshake(self.ssl) + if ok then + return true + end + if not event then + return nil, "ssl handshake failed." + end + local ok, err = event_wait(self, event) + if not ok then -- 超时 + return nil, err + end + end +end + function TCP:count() return #POOL end @@ -594,6 +736,27 @@ function TCP:close() self.connect_co = nil end + if self.LISTEN_IO then + tcp_stop(self.LISTEN_IO) + tcp_push(self.LISTEN_IO) + self.LISTEN_IO = nil + self.listen_co = nil + end + + if self.LISTEN_EX_IO then + tcp_stop(self.LISTEN_EX_IO) + tcp_push(self.LISTEN_EX_IO) + self.LISTEN_EX_IO = nil + self.listen_ex_co = nil + end + + if self.LISTEN_SSL_IO then + tcp_stop(self.LISTEN_SSL_IO) + tcp_push(self.LISTEN_SSL_IO) + self.LISTEN_SSL_IO = nil + self.listen_ssl_co = nil + end + if self.connect_current_co then co_wakeup(self.connect_current_co) self.connect_current_co = nil @@ -631,7 +794,12 @@ function TCP:close() if self.ufd then tcp_close(self.ufd) - self.fd = nil + self.ufd = nil + end + + if self.sfd then + tcp_close(self.sfd) + self.sfd = nil end end diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index 36d8f368..dde8bb87 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -302,11 +302,10 @@ local function send_body (sock, body, filepath) return sock:send(body) end -function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) +function HTTP_PROTOCOL.DISPATCH(sock, ipaddr, http) local buffers = {} local ttl = http.ttl local server = http.__server - local timeout = http.__timeout or 0 local enable_cookie = http.__enable_cookie local enable_gzip = http.__enable_gzip local enable_cros_timeout = http.__enable_cros_timeout @@ -319,7 +318,6 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) local route_find = http_router.find local tolog = http.tolog secCookie(cookie_secure) -- 如果需要 - local sock = tcp:new():set_fd(fd):timeout(timeout) while 1 do local buf = sock:recv(8192) if not buf then @@ -386,7 +384,7 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) end res[#res+1] = 'Server: ' .. (server or SERVER) res[#res+1] = 'Connection: keep-alive' - res[#res+1] = 'Content-Length : 0' + res[#res+1] = 'Content-Length: 0' sock:send(concat(res, CRLF)..CRLF2) goto CONTINUE end @@ -588,4 +586,12 @@ function HTTP_PROTOCOL.EVENT_DISPATCH(fd, ipaddr, http) end end + +function HTTP_PROTOCOL.RAW_DISPATCH(s, ipaddr, http) + if type(s) == 'table' then + return HTTP_PROTOCOL.DISPATCH(s, ipaddr, http) + end + return HTTP_PROTOCOL.DISPATCH(tcp:new():set_fd(s):timeout(http.__timeout), ipaddr, http) +end + return HTTP_PROTOCOL From 787edc3fdc5a77ce590df09bf93c027b32a0d891 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 23 Aug 2020 20:49:06 +0800 Subject: [PATCH 632/956] =?UTF-8?q?build.sh=E5=A2=9E=E5=8A=A0=E5=9B=BD?= =?UTF-8?q?=E5=86=85=E5=8A=A0=E9=80=9F=E6=BA=90,=20=E7=94=A8=E4=BA=8E?= =?UTF-8?q?=E5=BF=AB=E9=80=9F=E4=BE=9D=E8=B5=96=E4=B8=8B=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/build.sh b/build.sh index 2f5792fc..20149cb0 100755 --- a/build.sh +++ b/build.sh @@ -16,22 +16,42 @@ current=`pwd` rm -rf build && mkdir build && cd build +# git clone https://gitee.com/CandyMi/lua -b v5.3.5 git clone https://github.com/CandyMi/lua -b v5.3.5 -git clone https://github.com/CandyMi/libeio - +# git clone https://gitee.com/CandyMi/libev -b v4.25 git clone https://github.com/CandyMi/libev -b v4.25 -echo "========== build lua ==========" && - cd ${current}/build/lua && make posix MYCFLAGS="-fPIC -DLUA_USE_DLOPEN -DLUA_USE_READLINE" MYLIBS="-ldl -lreadline" && - cp lua.h luaconf.h lualib.h lauxlib.h ${current}/src && cp liblua.* ${current}/ +# git clone https://gitee.com/CandyMi/libeio +git clone https://github.com/CandyMi/libeio echo "========== build libev ==========" && cd ${current}/build/libev && sh autogen.sh && ./configure --prefix=/usr/local && + + ## 1. 将头文件与库文件放到cf框架目录下(Put the header files and library files in the cf framework directory) make && cp e*.h ${current}/src && cd .libs && cp $(printf "%s" "`ls | grep libev | grep -v la`") ${current}/ + ## 2. 将 libev 安装到 /usr/local 区域, 对其进行全局共享库链接. (Install `libev` into the `/usr/local` zone and link it with global shared libraries.) + # make && make install + echo "========== build libeio ==========" && cd ${current}/build/libeio && sh autogen.sh && ./configure --prefix=/usr/local && + + ## 1. 将头文件与库文件放到cf框架目录下(Put the header files and library files in the cf framework directory) make && cp e*.h ${current}/src && cd .libs && cp $(printf "%s" "`ls | grep libeio | grep -v la`") ${current}/ + ## 2. 将 libeio 安装到 /usr/local 区域, 对其进行全局共享库链接. (Install `libeio` into the `/usr/local` zone and link it with global shared libraries.) + # make && make install + +echo "========== build lua ==========" && + cd ${current}/build/lua && make posix MYCFLAGS="-fPIC -DLUA_USE_DLOPEN" MYLIBS="-ldl" && + + ## 1. 将头文件与库文件放到cf框架目录下(Put the header files and library files in the cf framework directory) + cp lua.h luaconf.h lualib.h lauxlib.h ${current}/src && cp liblua.* ${current}/ + + ## 2. 将 lua 安装到 /usr/local 区域, 对其进行全局共享库链接. (Install `lua` into the `/usr/local` zone and link it with global shared libraries.) + # cp -rf lua.h luaconf.h lualib.h lauxlib.h /usr/local/include && cp liblua.* /usr/local/lib + +echo "Done." + echo "========== clean build ==========" && cd ${current} && rm -rf build From 6f5645df4c6f98936af0106de0e642d0d986809f Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 8 Sep 2020 01:08:07 +0800 Subject: [PATCH 633/956] =?UTF-8?q?=E4=BF=AE=E6=AD=A3AIO=E5=BA=93=E7=9A=84?= =?UTF-8?q?write=E8=A1=8C=E4=B8=BAbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/laio.c | 2 +- lualib/aio/init.lua | 39 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/luaclib/src/laio.c b/luaclib/src/laio.c index 3c7c5e9c..b5f5ccb1 100644 --- a/luaclib/src/laio.c +++ b/luaclib/src/laio.c @@ -457,7 +457,7 @@ static int laio_write(lua_State* L) { } /* 适用write来完成追加操作, 同时也不允许单线程覆盖写入. */ - eio_write(fd, (void*)buffer, buffer_size, -1, EIO_PRI_DEFAULT, AIO_RESPONSE_WRITE, (void*)t); + eio_write(fd, (void*)buffer, buffer_size, lua_tointeger(L, 4), EIO_PRI_DEFAULT, AIO_RESPONSE_WRITE, (void*)t); return 1; } diff --git a/lualib/aio/init.lua b/lualib/aio/init.lua index a309d3a8..aa4c35bc 100644 --- a/lualib/aio/init.lua +++ b/lualib/aio/init.lua @@ -49,6 +49,16 @@ function File:__gc() return true end +-- 重置标志位 +function File:lseek(read_offset, write_offset) + if read_offset then + self:read_lseek(read_offset) + end + if write_offset then + self:write_lseek(write_offset) + end +end + -- 重置read_offset; 这个方法一般情况下不会用到, 除非你非常明白自己在做什么. function File:read_lseek(read_offset) if toint(read_offset) and toint(read_offset) >= 0 then @@ -56,6 +66,13 @@ function File:read_lseek(read_offset) end end +-- 重置write_offset; 这个方法一般情况下不会用到, 除非你非常明白自己在做什么. +function File:write_lseek(write_offset) + if toint(write_offset) and toint(write_offset) >= 0 then + self.write_offset = write_offset + end +end + -- 读取文件指定大小内容; 除非调用read_lseek重置位置或有新内容写入, 否则超出文件长度后将会返回空字符串. function File:read( bytes ) if self.status == "closed" then @@ -112,11 +129,24 @@ function File:write( data ) return nil, "Invalid file write data." end self.__WRITE__ = assert(not self.__WRITE__, "File:write方法不可以在多个协程中并发调用.") - local size, err = aio._write(self.fd, data) + -- 如果没有构建write_offset, 则需要通过stat构建; 否则永远只从尾部开始写入. + if not self.write_offset then + if not self.stat then + self.stat = aio.stat(self.path) + end + self.write_offset = self.stat.size + end + local size, err = aio._write(self.fd, data, self.write_offset) + if type(size) == 'number' then + self.stat.size = self.stat.size + size + self.write_offset = self.write_offset + size + end self.__WRITE__ = nil return size, err end +-- function + -- 刷新缓存 function File:flush() if self.status == "closed" then @@ -147,6 +177,9 @@ function File:clean() if toint(self.read_offset) then self.read_offset = 0 end + if toint(self.write_offset) then + self.write_offset = 0 + end self.stat = stat self.__CLEAN__ = nil return true @@ -250,7 +283,7 @@ function aio._read(fd, bytes, offset) end -- 写入(追加)指定大小数据 -function aio._write(fd, data) +function aio._write(fd, data, offset) fd = assert(toint(fd) and toint(fd) >= 0 and toint(fd), "Invalid fd.") data = assert(type(data) == 'string' and data ~= '' and data, "Invalid write data.") local t = new_tab(0, 3) @@ -260,7 +293,7 @@ function aio._write(fd, data) return co_wakeup(t.current_co, size, err) end) aio[t] = true - aio_write(t.event_co, fd, data) + aio_write(t.event_co, fd, data, offset) return co_wait() end From f5ba7f5dee8dff420ea10220abec13f2f9ac5512 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 8 Sep 2020 01:08:22 +0800 Subject: [PATCH 634/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Drest=E8=AF=AD?= =?UTF-8?q?=E6=B3=95=E7=9A=84=E4=B8=80=E4=B8=AA=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Router.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index cd4706b5..6e6d04eb 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -86,7 +86,7 @@ function Router:to_regex (r) if not name then error("Invalid rest router syntex in [" .. r .. "], type is not support.") end - t, v = name, "string" + t, v = "string", name end local reg = "([^/]+)" if t == 'number' then From a06f3c48f80a93471867481aff3e48a15bb66932 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 8 Sep 2020 22:18:21 +0800 Subject: [PATCH 635/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0xml2lua=E7=89=88?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/xml2lua/xml2lua.lua | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/lualib/xml2lua/xml2lua.lua b/lualib/xml2lua/xml2lua.lua index dc44db8f..0df6977b 100755 --- a/lualib/xml2lua/xml2lua.lua +++ b/lualib/xml2lua/xml2lua.lua @@ -50,7 +50,7 @@ --@author Paul Chakravarti (paulc@passtheaardvark.com) --@author Manoel Campos da Silva Filho local xml2lua = {} -local XmlParser = require "xml2lua.XmlParser" +local XmlParser = require("xml2lua.XmlParser") ---Recursivelly prints a table in an easy-to-ready format --@param tb The table to be printed @@ -132,7 +132,9 @@ function xml2lua.loadFile(xmlFilePath) local f, e = io.open(xmlFilePath, "r") if f then --Gets the entire file content and stores into a string - return f:read("*a") + local content = f:read("*a") + f:close() + return content end error(e) @@ -174,10 +176,10 @@ end -- --@return a String representing the table content in XML function xml2lua.toXml(tb, tableName, level) - local level = level or 0 + local level = level or 1 local firstLevel = level local spaces = string.rep(' ', level*2) - local xmltb = level == 0 and {'<'..tableName..'>'} or {} + local xmltb = level == 1 and {'<'..tableName..'>'} or {} for k, v in pairs(tb) do if type(v) == "table" then @@ -193,10 +195,11 @@ function xml2lua.toXml(tb, tableName, level) if type(getFirstKey(v)) == "number" then table.insert(xmltb, spaces..xml2lua.toXml(v, k, level)) else - table.insert( - xmltb, - spaces..'<'..k..'>\n'.. xml2lua.toXml(v, level+1).. - '\n'..spaces..'') + local attrs = attrToXml(v._attr) + v._attr = nil + table.insert(xmltb, + spaces..'<'..k..attrs..'>\n'.. xml2lua.toXml(v, k, level+1).. + '\n'..spaces..'') end end else @@ -204,10 +207,10 @@ function xml2lua.toXml(tb, tableName, level) end end - if firstLevel == 0 then + if firstLevel == 1 then table.insert(xmltb, '\n') end return table.concat(xmltb, "\n") end -return xml2lua +return xml2lua \ No newline at end of file From 303496f012200d96c9b13b532ba4714d03dc6a69 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 9 Sep 2020 22:36:26 +0800 Subject: [PATCH 636/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0crypt=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/crypt/init.lua | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index 5deb5ffc..412f9227 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -113,6 +113,8 @@ function crypt.sha1(str, hex) return hash end +crypt.sha1 = crypt.sha128 + function crypt.sha224 (str, hex) local hash = sha224(str) if hash and hex then @@ -154,6 +156,8 @@ function crypt.hmac_sha1 (key, text, hex) return hash end +crypt.hmac_sha1 = crypt.hmac_sha128 + function crypt.hmac_sha256 (key, text, hex) local hash = hmac_sha256(key, text) if hash and hex then @@ -274,27 +278,45 @@ function crypt.aes_256_ecb_encrypt(key, text, iv, hex) return hash end -function crypt.aes_128_cbc_decrypt(key, text, iv) +function crypt.aes_128_cbc_decrypt(key, text, iv, hex) + if hex then + text = hexdecode(text) + end return aes_cbc_decrypt(16, #key == 16 and key or nil, text, iv) end -function crypt.aes_128_ecb_decrypt(key, text, iv) +function crypt.aes_128_ecb_decrypt(key, text, iv, hex) + if hex then + text = hexdecode(text) + end return aes_ecb_decrypt(16, #key == 16 and key or nil, text, iv) end -function crypt.aes_192_cbc_decrypt(key, text, iv) +function crypt.aes_192_cbc_decrypt(key, text, iv, hex) + if hex then + text = hexdecode(text) + end return aes_cbc_decrypt(24, #key == 24 and key or nil, text, iv) end -function crypt.aes_192_ecb_decrypt(key, text, iv) +function crypt.aes_192_ecb_decrypt(key, text, iv, hex) + if hex then + text = hexdecode(text) + end return aes_ecb_decrypt(24, #key == 24 and key or nil, text, iv) end -function crypt.aes_256_cbc_decrypt(key, text, iv) +function crypt.aes_256_cbc_decrypt(key, text, iv, hex) + if hex then + text = hexdecode(text) + end return aes_cbc_decrypt(32, #key == 32 and key or nil, text, iv) end -function crypt.aes_256_ecb_decrypt(key, text, iv) +function crypt.aes_256_ecb_decrypt(key, text, iv, hex) + if hex then + text = hexdecode(text) + end return aes_ecb_decrypt(32, #key == 32 and key or nil, text, iv) end From 14f3340b7351e32c33aeeac644434e765cc42198 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Fri, 11 Sep 2020 00:09:46 +0800 Subject: [PATCH 637/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dhttpc=E5=BA=93?= =?UTF-8?q?=E5=8F=AF=E8=83=BD=E8=BF=94=E5=9B=9E=E7=95=B8=E5=BD=A2=E6=95=B0?= =?UTF-8?q?=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lhttpparser/httpparser.c | 66 +++++++++++++++++---------- luaclib/src/lhttpparser/lhttpparser.c | 19 ++++---- 2 files changed, 50 insertions(+), 35 deletions(-) diff --git a/luaclib/src/lhttpparser/httpparser.c b/luaclib/src/lhttpparser/httpparser.c index b216356e..c069e505 100644 --- a/luaclib/src/lhttpparser/httpparser.c +++ b/luaclib/src/lhttpparser/httpparser.c @@ -241,6 +241,41 @@ static const char *is_complete(const char *buf, const char *buf_end, size_t last *valp_ += res_; \ } while (0) +/* returned pointer is always within [buf, buf_end), or null */ +static const char *parse_token(const char *buf, const char *buf_end, const char **token, size_t *token_len, char next_char, + int *ret) +{ + /* We use pcmpestri to detect non-token characters. This instruction can take no more than eight character ranges (8*2*8=128 + * bits that is the size of a SSE register). Due to this restriction, characters `|` and `~` are handled in the slow loop. */ + static const char ALIGNED(16) ranges[] = "\x00 " /* control chars and up to SP */ + "\"\"" /* 0x22 */ + "()" /* 0x28,0x29 */ + ",," /* 0x2c */ + "//" /* 0x2f */ + ":@" /* 0x3a-0x40 */ + "[]" /* 0x5b-0x5d */ + "{\xff"; /* 0x7b-0xff */ + const char *buf_start = buf; + int found; + buf = findchar_fast(buf, buf_end, ranges, sizeof(ranges) - 1, &found); + if (!found) { + CHECK_EOF(); + } + while (1) { + if (*buf == next_char) { + break; + } else if (!token_char_map[(unsigned char)*buf]) { + *ret = -1; + return NULL; + } + ++buf; + CHECK_EOF(); + } + *token = buf_start; + *token_len = buf - buf_start; + return buf; +} + /* returned pointer is always within [buf, buf_end), or null */ static const char *parse_http_version(const char *buf, const char *buf_end, int *minor_version, int *ret) { @@ -280,31 +315,10 @@ static const char *parse_headers(const char *buf, const char *buf_end, struct ph if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) { /* parsing name, but do not discard SP before colon, see * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */ - headers[*num_headers].name = buf; - static const char ALIGNED(16) ranges1[] = "\x00 " /* control chars and up to SP */ - "\"\"" /* 0x22 */ - "()" /* 0x28,0x29 */ - ",," /* 0x2c */ - "//" /* 0x2f */ - ":@" /* 0x3a-0x40 */ - "[]" /* 0x5b-0x5d */ - "{\377"; /* 0x7b-0xff */ - int found; - buf = findchar_fast(buf, buf_end, ranges1, sizeof(ranges1) - 1, &found); - if (!found) { - CHECK_EOF(); - } - while (1) { - if (*buf == ':') { - break; - } else if (!token_char_map[(unsigned char)*buf]) { - *ret = -1; - return NULL; - } - ++buf; - CHECK_EOF(); + if ((buf = parse_token(buf, buf_end, &headers[*num_headers].name, &headers[*num_headers].name_len, ':', ret)) == NULL) { + return NULL; } - if ((headers[*num_headers].name_len = buf - headers[*num_headers].name) == 0) { + if (headers[*num_headers].name_len == 0) { *ret = -1; return NULL; } @@ -352,7 +366,9 @@ static const char *parse_request(const char *buf, const char *buf_end, const cha } /* parse request line */ - ADVANCE_TOKEN(*method, *method_len); + if ((buf = parse_token(buf, buf_end, method, method_len, ' ', ret)) == NULL) { + return NULL; + } do { ++buf; CHECK_EOF(); diff --git a/luaclib/src/lhttpparser/lhttpparser.c b/luaclib/src/lhttpparser/lhttpparser.c index b2123d1a..4d507c88 100644 --- a/luaclib/src/lhttpparser/lhttpparser.c +++ b/luaclib/src/lhttpparser/lhttpparser.c @@ -10,20 +10,19 @@ lparser_response_chunked(lua_State *L){ size_t buf_len; const char* data = luaL_checklstring(L, 1, &buf_len); - luaL_Buffer B; - char *buf = luaL_buffinitsize(L, &B, buf_len); - luaL_addlstring(&B, data, buf_len); + char *buf = (buf_len < 65535) ? alloca(1 << 16) : lua_newuserdata(L, buf_len); + memcpy(buf, data, buf_len); struct phr_chunked_decoder decoder = { .consume_trailer = 1 }; + int last = phr_decode_chunked(&decoder, buf, &buf_len); - if (0 > last) { - luaL_pushresult(&B); - lua_pushnil(L); - lua_pushinteger(L, last); - return 2; + if (last >= 0) { + lua_pushlstring(L, (const char *)buf, buf_len); + return 1; } - luaL_pushresult(&B); - return 1; + lua_pushnil(L); + lua_pushinteger(L, last); + return 2; } static int From ca7ab4c4fb62f0ed21b94f3780ca17b57e151808 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Fri, 11 Sep 2020 00:19:45 +0800 Subject: [PATCH 638/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0sha128=E4=B8=8Ehmac?= =?UTF-8?q?=5Fsha128=E5=88=AB=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/crypt/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index 412f9227..5c97a2b6 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -113,7 +113,7 @@ function crypt.sha1(str, hex) return hash end -crypt.sha1 = crypt.sha128 +crypt.sha128 = crypt.sha1 function crypt.sha224 (str, hex) local hash = sha224(str) @@ -156,7 +156,7 @@ function crypt.hmac_sha1 (key, text, hex) return hash end -crypt.hmac_sha1 = crypt.hmac_sha128 +crypt.hmac_sha128 = crypt.hmac_sha1 function crypt.hmac_sha256 (key, text, hex) local hash = hmac_sha256(key, text) From 8d26fe2373678510fb5c82a73b1acaf95f15e751 Mon Sep 17 00:00:00 2001 From: "Mr.che" <869646063@qq.com> Date: Mon, 28 Sep 2020 16:12:47 +0800 Subject: [PATCH 639/956] Update url.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加urldecode遇到加号('+')会自动转换为空格(' ') --- luaclib/src/lcrypt/url.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/luaclib/src/lcrypt/url.c b/luaclib/src/lcrypt/url.c index e5d1e7d4..d92b36c3 100644 --- a/luaclib/src/lcrypt/url.c +++ b/luaclib/src/lcrypt/url.c @@ -7,25 +7,25 @@ /* url编码 */ int lurlencode(lua_State *L){ size_t url_len; - const char* url = luaL_checklstring(L, 1, &url_len); + const char* url = luaL_checklstring(L, 1, &url_len); if (!url) return luaL_error(L, "Invalid url text"); luaL_Buffer convert_url; luaL_buffinit(L, &convert_url); - while (*url) { - uint8_t ch = (uint8_t)*url++; - if (ch == ' ') { + for (int index = 0; index < url_len;) { + uint8_t ch = (uint8_t)url[index++]; + if (ch == (uint8_t)' ') { luaL_addlstring(&convert_url, "%20", 3); continue; } - if (is_normal_char(ch) || strchr("-_.!~*'()", ch)){ + if (is_normal_char(ch) || strchr("-_.!~*'()", ch)) { luaL_addchar(&convert_url, ch); continue; } - char ver[3] = {'%', hex_char(((uint8_t)ch) >> 4), hex_char(((uint8_t)ch) & 15)}; - luaL_addlstring(&convert_url, ver, 3); + char vert[3] = {'%', hex_char(((uint8_t)ch) >> 4), hex_char(((uint8_t)ch) & 15)}; + luaL_addlstring(&convert_url, vert, 3); } luaL_pushresult(&convert_url); @@ -35,25 +35,23 @@ int lurlencode(lua_State *L){ /* url解码 */ int lurldecode(lua_State *L){ size_t url_len; - const char* url = luaL_checklstring(L, 1, &url_len); + const char* url = luaL_checklstring(L, 1, &url_len); if (!url) return luaL_error(L, "Invalid url text"); luaL_Buffer convert_url; luaL_buffinit(L, &convert_url); - while (*url) { - uint8_t ch = (uint8_t)*url++; - if (ch != '%') { - luaL_addchar(&convert_url, ch); + for (int index = 0; index < url_len;) { + uint8_t ch = (uint8_t)url[index++]; + if (ch != (uint8_t)'%') { + luaL_addchar(&convert_url, ch == (uint8_t)'+' ? (uint8_t)' ' : ch); continue; } - char vert[2]; - vert[0] = (uint8_t)*url++; - vert[1] = (uint8_t)*url++; + uint8_t vert[] = { (uint8_t)url[index++], (uint8_t)url[index++] }; luaL_addchar(&convert_url, (uint8_t)((vert[0] - 48 - ((vert[0] >= 'A') ? 7 : 0) - ((vert[0] >= 'a') ? 32 : 0)) * 16 + (vert[1] - 48 - ((vert[1] >= 'A') ? 7 : 0) - ((vert[1] >= 'a') ? 32 : 0)))); } - + luaL_pushresult(&convert_url); return 1; } From dfdab4ba187d5223020f6303c008f5212fdd7a54 Mon Sep 17 00:00:00 2001 From: "Mr.che" <869646063@qq.com> Date: Mon, 28 Sep 2020 16:56:05 +0800 Subject: [PATCH 640/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0urlencode=E4=B8=8Eurl?= =?UTF-8?q?decode=E4=BB=A3=E7=A0=81=E5=B9=B6=E4=BC=98=E5=8C=96=E5=B9=B3?= =?UTF-8?q?=E5=8F=B0=E5=85=BC=E5=AE=B9=E6=80=A7.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/url.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/luaclib/src/lcrypt/url.c b/luaclib/src/lcrypt/url.c index d92b36c3..272dfdc6 100644 --- a/luaclib/src/lcrypt/url.c +++ b/luaclib/src/lcrypt/url.c @@ -14,7 +14,8 @@ int lurlencode(lua_State *L){ luaL_Buffer convert_url; luaL_buffinit(L, &convert_url); - for (int index = 0; index < url_len;) { + int index; + for (index = 0; index < url_len;) { uint8_t ch = (uint8_t)url[index++]; if (ch == (uint8_t)' ') { luaL_addlstring(&convert_url, "%20", 3); @@ -24,8 +25,8 @@ int lurlencode(lua_State *L){ luaL_addchar(&convert_url, ch); continue; } - char vert[3] = {'%', hex_char(((uint8_t)ch) >> 4), hex_char(((uint8_t)ch) & 15)}; - luaL_addlstring(&convert_url, vert, 3); + char vert[] = {'%', hex_char(((uint8_t)ch) >> 4), hex_char(((uint8_t)ch) & 0xF)}; + luaL_addlstring(&convert_url, (const char *)vert, 3); } luaL_pushresult(&convert_url); @@ -42,13 +43,14 @@ int lurldecode(lua_State *L){ luaL_Buffer convert_url; luaL_buffinit(L, &convert_url); - for (int index = 0; index < url_len;) { + int index; + for (index = 0; index < url_len;) { uint8_t ch = (uint8_t)url[index++]; if (ch != (uint8_t)'%') { luaL_addchar(&convert_url, ch == (uint8_t)'+' ? (uint8_t)' ' : ch); continue; } - uint8_t vert[] = { (uint8_t)url[index++], (uint8_t)url[index++] }; + char vert[] = { (uint8_t)url[index++], (uint8_t)url[index++] }; luaL_addchar(&convert_url, (uint8_t)((vert[0] - 48 - ((vert[0] >= 'A') ? 7 : 0) - ((vert[0] >= 'a') ? 32 : 0)) * 16 + (vert[1] - 48 - ((vert[1] >= 'A') ? 7 : 0) - ((vert[1] >= 'a') ? 32 : 0)))); } From b5d5c62dee3d58937077f839683efc73340cdfcd Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 28 Sep 2020 22:52:35 +0800 Subject: [PATCH 641/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Durlencode/urldecode?= =?UTF-8?q?=E5=9C=A8=E4=B8=8D=E5=90=8C=E5=B9=B3=E5=8F=B0=E4=B8=8B=E7=9A=84?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E5=B7=AE=E5=BC=82=E4=B8=8E=E7=BC=96=E7=A0=81?= =?UTF-8?q?=E8=A1=8C=E4=B8=BA=E9=97=AE=E9=A2=98.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/url.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/luaclib/src/lcrypt/url.c b/luaclib/src/lcrypt/url.c index 272dfdc6..a61f3e63 100644 --- a/luaclib/src/lcrypt/url.c +++ b/luaclib/src/lcrypt/url.c @@ -6,7 +6,7 @@ /* url编码 */ int lurlencode(lua_State *L){ - size_t url_len; + size_t url_len = 0; const char* url = luaL_checklstring(L, 1, &url_len); if (!url) return luaL_error(L, "Invalid url text"); @@ -35,8 +35,8 @@ int lurlencode(lua_State *L){ /* url解码 */ int lurldecode(lua_State *L){ - size_t url_len; - const char* url = luaL_checklstring(L, 1, &url_len); + size_t url_len = 0; + const char* url = luaL_checklstring(L, 1, &url_len); if (!url) return luaL_error(L, "Invalid url text"); @@ -45,12 +45,25 @@ int lurldecode(lua_State *L){ int index; for (index = 0; index < url_len;) { + uint8_t ch = (uint8_t)url[index++]; if (ch != (uint8_t)'%') { luaL_addchar(&convert_url, ch == (uint8_t)'+' ? (uint8_t)' ' : ch); continue; } - char vert[] = { (uint8_t)url[index++], (uint8_t)url[index++] }; + + char vert[2]; + if (index++ == url_len) { + luaL_addchar(&convert_url, '%'); + break; + } + vert[0] = url[index - 1]; + + if (index++ == url_len) { + luaL_addlstring(&convert_url, url + url_len - 2, 2); + break; + } + vert[1] = url[index - 1]; luaL_addchar(&convert_url, (uint8_t)((vert[0] - 48 - ((vert[0] >= 'A') ? 7 : 0) - ((vert[0] >= 'a') ? 32 : 0)) * 16 + (vert[1] - 48 - ((vert[1] >= 'A') ? 7 : 0) - ((vert[1] >= 'a') ? 32 : 0)))); } From 872c503999d4fc1e52b8826c25e0b9c675ca5ca2 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Fri, 2 Oct 2020 13:43:32 +0800 Subject: [PATCH 642/956] =?UTF-8?q?=E4=BC=98=E5=8C=96sendfile=E4=B8=8Essl?= =?UTF-8?q?=5Fsendfile=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 32 ++++++++++++++++++-------------- lualib/internal/TCP.lua | 33 +++++++++++++-------------------- 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index aba1dd42..0dbddd29 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -230,7 +230,7 @@ static int create_server_unixsock(const char* path, size_t path_len, int backlog if (0 >= sockfd){ LOG("ERROR", strerror(errno)); return -1; - } + } struct sockaddr_un UN; memset(&UN, 0x0, sizeof(UN)); @@ -384,11 +384,11 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ #ifdef EV_USE_KQUEUE int tag = 0; off_t nBytes = 0; for (;;) { -#if defined(__APPLE__) + #if defined(__APPLE__) tag = sendfile(sf->fd, io->fd, sf->pos, &nBytes, NULL, 0); -#else + #else tag = sendfile(sf->fd, io->fd, sf->pos, 0, NULL, &nBytes, SF_NODISKIO | SF_NOCACHE); -#endif + #endif sf->pos += nBytes; if (0 > tag) { if (errno == EINTR) continue; @@ -417,21 +417,17 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ #ifdef __MSYS__ char buf[sf->offset]; - for (;;) { - memset(buf, 0x0, sf->offset); - int rBytes = read(sf->fd, buf, sf->offset); - if (0 == rBytes) { lua_pushboolean(sf->L, 1); break; } // 所有数据写入发送完毕. + for(;;) { + int rBytes = pread(sf->fd, buf, sf->offset, sf->pos); + if (rBytes == 0) { lua_pushboolean(sf->L, 1); break; } // 所有数据写入发送完毕. int wBytes = write(io->fd, buf, rBytes); if (wBytes <= 0) { - /* 如果写入失败后需要重试, 则需要先恢复到上次写入位置*/ - lseek(sf->fd, lseek(sf->fd, 0, SEEK_CUR) - rBytes, SEEK_SET); - if (errno == EINTR) continue ; + if (errno == EINTR) continue; if (errno == EWOULDBLOCK) return; lua_pushboolean(sf->L, 0); break; } - // 如果文件发送字符数量小于读取数量, 就需要重新设置读写位置。 - if (rBytes > wBytes) lseek(sf->fd, lseek(sf->fd, 0, SEEK_CUR) - (rBytes - wBytes), SEEK_SET); + sf->pos += wBytes; } #endif core_set_watcher_userdata(io, NULL); @@ -440,16 +436,24 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ LOG("ERROR", lua_tostring(sf->L, -1)); LOG("ERROR", "Error Lua SENDFILE Method"); } + close(sf->fd); xfree(sf); } } static int tcp_sendfile(lua_State *L){ + + errno = 0; + core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); + int fd = open(luaL_checkstring(L, 3), O_RDONLY); + if (fd < 0) + return luaL_error(L, "[%s]: %s.", luaL_checkstring(L, 3), strerror(errno)); + struct io_sendfile *sf = xmalloc(sizeof(struct io_sendfile)); sf->L = lua_tothread(L, 2); - sf->fd = luaL_checkinteger(L, 3); + sf->fd = fd; sf->offset = luaL_checkinteger(L, 5); sf->pos = 0; diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index c57cbba3..63697f80 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -23,10 +23,6 @@ local co_wait = coroutine.yield local ti = require "internal.Timer" local ti_timeout = ti.timeout -local aio = require "aio" -local aio_close = aio._close -local aio_open = aio._open - local tcp = require "tcp" local tcp_new = tcp.new local tcp_ssl_new = tcp.new_ssl @@ -162,16 +158,12 @@ function TCP:ssl_set_password(password) return tcp_ssl_set_userdata_key(self.ssl, self.ssl_ctx, self.ssl_password) end --- sendfile的文件fd使用aio库来打开与关闭可以减少阻塞. +-- sendfile实现. function TCP:sendfile (filename, offset) if self.ssl or self.ssl_ctx then return self:ssl_sendfile(filename, offset) end if type(filename) == 'string' and filename ~= '' then - local fd, err = aio_open(filename) - if not fd then - return nil, err - end local co = co_self() self.SEND_IO = tcp_pop() self.sendfile_current_co = co_self() @@ -183,26 +175,27 @@ function TCP:sendfile (filename, offset) self.sendfile_current_co = nil return co_wakeup(co, ok) end) - tcp_sendfile(self.SEND_IO, self.sendfile_co, fd, self.fd, offset or 65535) - local ok = co_wait() - aio_close(fd) - return ok + tcp_sendfile(self.SEND_IO, self.sendfile_co, filename, self.fd, offset or 65535) + return co_wait() end end --- 假装自己是sendfile +-- ssl_sendfile实现 function TCP:ssl_sendfile(filename, offset) if type(filename) == 'string' and filename ~= '' then - local f, err = aio.open(filename) + local f, err = io.open(filename, "r") if not f then return nil, err end - local data, err = f:readall() - f:close() - if not data then - return nil, err + local ok = false + for buf in f:lines(offset or 65535) do + local ok = self:ssl_send(buf) + if not ok then + break + end end - return self:ssl_send(data) + f:close() + return ok end end From 911fa8018b0e98f832d8d9bce38d3d6fa8f7db36 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 22 Oct 2020 20:37:14 +0800 Subject: [PATCH 643/956] =?UTF-8?q?=E4=B8=BADB=E5=BA=93=E9=87=8D=E5=86=99M?= =?UTF-8?q?ySQL=E5=8D=8F=E8=AE=AE=E5=B9=B6=E6=96=B0=E5=A2=9EMSSQL=E4=B8=8E?= =?UTF-8?q?PGSQL=E5=8D=8F=E8=AE=AE=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/DB/init.lua | 251 +------ lualib/DB/mssql.lua | 143 ++++ lualib/DB/mysql.lua | 143 ++++ lualib/DB/pgsql.lua | 143 ++++ lualib/protocol/mssql.lua | 1049 ++++++++++++++++++++++++++++++ lualib/protocol/mysql.lua | 1301 +++++++++++++------------------------ lualib/protocol/pgsql.lua | 570 ++++++++++++++++ 7 files changed, 2493 insertions(+), 1107 deletions(-) create mode 100644 lualib/DB/mssql.lua create mode 100644 lualib/DB/mysql.lua create mode 100644 lualib/DB/pgsql.lua create mode 100644 lualib/protocol/mssql.lua create mode 100644 lualib/protocol/pgsql.lua diff --git a/lualib/DB/init.lua b/lualib/DB/init.lua index 47d2e63a..36b57870 100644 --- a/lualib/DB/init.lua +++ b/lualib/DB/init.lua @@ -1,250 +1 @@ -local class = require "class" - -local timer = require "internal.Timer" -local mysql = require "protocol.mysql" - -local log = require "logging" -local Log = log:new({ dump = true, path = 'DB'}) - -local crypt = require "crypt" -local hashkey = crypt.hashkey - -local co = require "internal.Co" -local co_self = co.self -local co_wait = co.wait -local co_wakeup = co.wakeup - -local type = type -local ipairs = ipairs -local assert = assert -local tostring = tostring -local tonumber = tonumber - -local fmt = string.format - -local insert = table.insert -local remove = table.remove -local concat = table.concat - --- 空闲连接时间 -local WAIT_TIMEOUT = 31536000 -local INTERACTIVE_TIMEOUT = 31536000 - --- 数据库连接创建函数 -local function DB_CREATE (opt) - local db - while 1 do - db = mysql:new() - db:set_timeout(3) - local connect, err = db:connect(opt) - if connect then - assert(db:query(fmt('SET wait_timeout=%u', WAIT_TIMEOUT))) - assert(db:query(fmt('SET interactive_timeout=%u', INTERACTIVE_TIMEOUT))) - if opt.stmts then - local stmts = opt.stmts - for _, rkey in ipairs(stmts) do - assert(db:prepare(stmts[rkey].sql)) - end - end - db:set_timeout(0) - break - end - Log:WARN("The connection failed. The reasons are: [" .. err .. "], Try to reconnect after 3 seconds") - timer.sleep(3) - db:close() - end - return db -end - -local function add_wait(self, co) - insert(self.co_pool, co) -end - -local function pop_wait(self) - return remove(self.co_pool) -end - -local function add_db(self, db) - insert(self.db_pool, db) -end - --- 负责创建连接/加入等待队列 -local function pop_db(self) - if #self.db_pool > 0 then - return remove(self.db_pool) - end - if self.current < self.max then - self.current = self.current + 1 - return DB_CREATE(self) - end - add_wait(self, co_self()) - return co_wait() -end - -local function run_query(self, query) - local db, ret, err - while 1 do - db = pop_db(self) - if db then - ret, err = db:query(query) - if db.state then - break - end - db:close() - self.current = self.current - 1 - db, ret, err = nil, nil, nil - end - end - local co = pop_wait(self) - if co then - co_wakeup(co, db) - return ret, err - end - add_db(self, db) - return ret, err -end - -local function run_prepare(self, query) - local db, ret, err - while 1 do - db = pop_db(self) - if db then - ret, err = db:prepare(query) - if db.state then - break - end - db:close() - self.current = self.current - 1 - db, ret, err = nil, nil, nil - end - end - local co = pop_wait(self) - if co then - co_wakeup(co, db) - return ret, err - end - add_db(self, db) - return ret, err -end - -local function run_execute(self, stmt, ...) - local db, ret, err - while 1 do - db = pop_db(self) - if db then - ret, err = db:execute(stmt, ...) - if db.state then - break - end - db:close() - self.current = self.current - 1 - db, ret, err = nil, nil, nil - end - end - local co = pop_wait(self) - if co then - co_wakeup(co, db) - return ret, err - end - add_db(self, db) - return ret, err -end - -local DB = class("DB") - -function DB:ctor(opt) - self.host = opt.host - self.port = opt.port - self.username = opt.username - self.password = opt.password - self.database = opt.database - self.charset = opt.charset or 'utf8' - self.max = opt.max or 50 - self.current = 0 - -- 协程池 - self.co_pool = {} - -- 连接池 - self.db_pool = {} -end - -function DB:connect () - if not self.INITIALIZATION then - add_db(self, pop_db(self)) - self.INITIALIZATION = true - return self.INITIALIZATION - end - return self.INITIALIZATION -end - -function DB:prepare(sql) - assert(self.INITIALIZATION, "DB needs to be initialized first.") - local stmts = self.stmts - if not self.stmts then - stmts = {} - self.stmts = stmts - end - local rkey = hashkey(sql, true) - if stmts[rkey] then - return rkey - end - local stmt = assert(run_prepare(self, sql)) - stmts[rkey] = { stmt = stmt, sql = sql } - stmts[#stmts + 1] = rkey - return rkey -end - --- 初始化所有预处理语句 -function DB:prepares(opt) - assert(self.INITIALIZATION, "DB needs to be initialized first.") - if type(opt) ~= 'table' or #opt < 1 then - return - end - local stmts = self.stmts - if not self.stmts then - stmts = {} - self.stmts = stmts - end - local rlist = {} - for _, sql in ipairs(opt) do - local rkey = hashkey(sql, true) - if not stmts[rkey] then - local stmt = assert(run_prepare(self, sql)) - stmts[rkey] = { stmt = stmt, sql = sql } - stmts[#stmts + 1] = rkey - end - rlist[#rlist + 1] = rkey - end - return rlist -end - --- 执行预处理语句 -function DB:execute(rkey, ...) - assert(self.INITIALIZATION, "DB needs to be initialized first.") - local stmts = self.stmts - if type(stmts) ~= 'table' or #stmts < 1 then - return nil, "DB has not any stmts." - end - local stmt = stmts[rkey] - if not stmt then - return nil, "DB Can't find this stmt." - end - return run_execute(self, stmt.stmt, ...) -end - --- 原始查询语句 -function DB:query(query) - assert(self.INITIALIZATION, "DB needs to be initialized first.") - return run_query(self, assert(type(query) == 'string' and query ~= '' and query , "Invalid MySQL syntax.")) -end - --- 字符串安全转义 -function DB.quote_to_str( str ) - return mysql.quote_to_str(str) -end - -function DB:count() - assert(self.INITIALIZATION, "DB needs to be initialized first.") - return self.current, self.max, #self.co_pool, #self.db_pool -end - -return DB +return require "DB.mysql" diff --git a/lualib/DB/mssql.lua b/lualib/DB/mssql.lua new file mode 100644 index 00000000..498d7de6 --- /dev/null +++ b/lualib/DB/mssql.lua @@ -0,0 +1,143 @@ +local class = require "class" + +local timer = require "internal.Timer" +local mssql = require "protocol.mssql" + +local log = require "logging" +local Log = log:new({ dump = true, path = 'DB'}) + +local crypt = require "crypt" +local hashkey = crypt.hashkey + +local co = require "internal.Co" +local co_self = co.self +local co_wait = co.wait +local co_wakeup = co.wakeup + +local type = type +local ipairs = ipairs +local assert = assert +local tostring = tostring +local tonumber = tonumber + +local fmt = string.format + +local insert = table.insert +local remove = table.remove +local concat = table.concat + +-- 空闲连接时间 +local WAIT_TIMEOUT = 31536000 +local INTERACTIVE_TIMEOUT = 31536000 + +-- 数据库连接创建函数 +local function DB_CREATE (opt) + local db + while 1 do + db = mssql:new(opt) + db:set_timeout(3) + local connect, err = db:connect() + if connect then + -- assert(db:query(fmt('SET wait_timeout=%u', WAIT_TIMEOUT))) + -- assert(db:query(fmt('SET interactive_timeout=%u', INTERACTIVE_TIMEOUT))) + db:set_timeout(0) + break + end + Log:WARN("The connection failed. The reasons are: [" .. err .. "], Try to reconnect after 3 seconds") + timer.sleep(3) + db:close() + end + return db +end + +local function add_wait(self, co) + insert(self.co_pool, co) +end + +local function pop_wait(self) + return remove(self.co_pool) +end + +local function add_db(self, db) + insert(self.db_pool, db) +end + +-- 负责创建连接/加入等待队列 +local function pop_db(self) + if #self.db_pool > 0 then + return remove(self.db_pool) + end + if self.current < self.max then + self.current = self.current + 1 + return DB_CREATE(self) + end + add_wait(self, co_self()) + return co_wait() +end + +local function run_query(self, query) + local db, ret, err + while 1 do + db = pop_db(self) + if db then + ret, err = db:query(query) + if db.state then + break + end + db:close() + self.current = self.current - 1 + db, ret, err = nil, nil, nil + end + end + local co = pop_wait(self) + if co then + co_wakeup(co, db) + return ret, err + end + add_db(self, db) + return ret, err +end + +local DB = class("DB") + +function DB:ctor(opt) + self.host = opt.host + self.port = opt.port + self.username = opt.username + self.password = opt.password + self.database = opt.database + self.TSQL = opt.TSQL + self.max = opt.max or 50 + self.current = 0 + -- 协程池 + self.co_pool = {} + -- 连接池 + self.db_pool = {} +end + +function DB:connect () + if not self.INITIALIZATION then + add_db(self, pop_db(self)) + self.INITIALIZATION = true + return self.INITIALIZATION + end + return self.INITIALIZATION +end + +-- 原始查询语句 +function DB:query(query) + assert(self.INITIALIZATION, "DB needs to be initialized first.") + return run_query(self, assert(type(query) == 'string' and query ~= '' and query , "Invalid MSSQL syntax.")) +end + +-- 字符串安全转义 +function DB.quote_to_str( str ) + return mssql.quote_to_str(str) +end + +function DB:count() + assert(self.INITIALIZATION, "DB needs to be initialized first.") + return self.current, self.max, #self.co_pool, #self.db_pool +end + +return DB diff --git a/lualib/DB/mysql.lua b/lualib/DB/mysql.lua new file mode 100644 index 00000000..c2da7346 --- /dev/null +++ b/lualib/DB/mysql.lua @@ -0,0 +1,143 @@ +local class = require "class" + +local timer = require "internal.Timer" +local mysql = require "protocol.mysql" + +local log = require "logging" +local Log = log:new({ dump = true, path = 'DB'}) + +local crypt = require "crypt" +local hashkey = crypt.hashkey + +local co = require "internal.Co" +local co_self = co.self +local co_wait = co.wait +local co_wakeup = co.wakeup + +local type = type +local ipairs = ipairs +local assert = assert +local tostring = tostring +local tonumber = tonumber + +local fmt = string.format + +local insert = table.insert +local remove = table.remove +local concat = table.concat + +-- 空闲连接时间 +local WAIT_TIMEOUT = 31536000 +local INTERACTIVE_TIMEOUT = 31536000 + +-- 数据库连接创建函数 +local function DB_CREATE (opt) + local db + while 1 do + db = mysql:new(opt) + db:set_timeout(3) + local connect, err = db:connect() + if connect then + assert(db:query(fmt('SET wait_timeout=%u', WAIT_TIMEOUT))) + assert(db:query(fmt('SET interactive_timeout=%u', INTERACTIVE_TIMEOUT))) + db:set_timeout(0) + break + end + Log:WARN("The connection failed. The reasons are: [" .. err .. "], Try to reconnect after 3 seconds") + timer.sleep(3) + db:close() + end + return db +end + +local function add_wait(self, co) + insert(self.co_pool, co) +end + +local function pop_wait(self) + return remove(self.co_pool) +end + +local function add_db(self, db) + insert(self.db_pool, db) +end + +-- 负责创建连接/加入等待队列 +local function pop_db(self) + if #self.db_pool > 0 then + return remove(self.db_pool) + end + if self.current < self.max then + self.current = self.current + 1 + return DB_CREATE(self) + end + add_wait(self, co_self()) + return co_wait() +end + +local function run_query(self, query) + local db, ret, err + while 1 do + db = pop_db(self) + if db then + ret, err = db:query(query) + if db.state then + break + end + db:close() + self.current = self.current - 1 + db, ret, err = nil, nil, nil + end + end + local co = pop_wait(self) + if co then + co_wakeup(co, db) + return ret, err + end + add_db(self, db) + return ret, err +end + +local DB = class("DB") + +function DB:ctor(opt) + self.host = opt.host + self.port = opt.port + self.username = opt.username + self.password = opt.password + self.database = opt.database + self.charset = opt.charset or 'utf8' + self.max = opt.max or 50 + self.current = 0 + -- 协程池 + self.co_pool = {} + -- 连接池 + self.db_pool = {} +end + +function DB:connect () + if not self.INITIALIZATION then + add_db(self, pop_db(self)) + self.INITIALIZATION = true + return self.INITIALIZATION + end + return self.INITIALIZATION +end + +-- 原始查询语句 +function DB:query(query) + assert(self.INITIALIZATION, "DB needs to be initialized first.") + return run_query(self, assert(type(query) == 'string' and query ~= '' and query , "Invalid MySQL syntax.")) +end + +-- 字符串安全转义 +function DB.quote_to_str( str ) + return mysql.quote_to_str(str) +end + +function DB:count() + assert(self.INITIALIZATION, "DB needs to be initialized first.") + return self.current, self.max, #self.co_pool, #self.db_pool +end + +return DB diff --git a/lualib/DB/pgsql.lua b/lualib/DB/pgsql.lua new file mode 100644 index 00000000..da5fb976 --- /dev/null +++ b/lualib/DB/pgsql.lua @@ -0,0 +1,143 @@ +local class = require "class" + +local timer = require "internal.Timer" +local pgsql = require "protocol.pgsql" + +local log = require "logging" +local Log = log:new({ dump = true, path = 'DB'}) + +local crypt = require "crypt" +local hashkey = crypt.hashkey + +local co = require "internal.Co" +local co_self = co.self +local co_wait = co.wait +local co_wakeup = co.wakeup + +local type = type +local ipairs = ipairs +local assert = assert +local tostring = tostring +local tonumber = tonumber + +local fmt = string.format + +local insert = table.insert +local remove = table.remove +local concat = table.concat + +-- 空闲连接时间 +local WAIT_TIMEOUT = 31536000 +local INTERACTIVE_TIMEOUT = 31536000 + +-- 数据库连接创建函数 +local function DB_CREATE (opt) + local db + while 1 do + db = pgsql:new(opt) + db:set_timeout(3) + local connect, err = db:connect() + if connect then + -- assert(db:query(fmt('SET wait_timeout=%u', WAIT_TIMEOUT))) + -- assert(db:query(fmt('SET interactive_timeout=%u', INTERACTIVE_TIMEOUT))) + db:set_timeout(0) + break + end + Log:WARN("The connection failed. The reasons are: [" .. err .. "], Try to reconnect after 3 seconds") + timer.sleep(3) + db:close() + end + return db +end + +local function add_wait(self, co) + insert(self.co_pool, co) +end + +local function pop_wait(self) + return remove(self.co_pool) +end + +local function add_db(self, db) + insert(self.db_pool, db) +end + +-- 负责创建连接/加入等待队列 +local function pop_db(self) + if #self.db_pool > 0 then + return remove(self.db_pool) + end + if self.current < self.max then + self.current = self.current + 1 + return DB_CREATE(self) + end + add_wait(self, co_self()) + return co_wait() +end + +local function run_query(self, query) + local db, ret, err + while 1 do + db = pop_db(self) + if db then + ret, err = db:query(query) + if db.state then + break + end + db:close() + self.current = self.current - 1 + db, ret, err = nil, nil, nil + end + end + local co = pop_wait(self) + if co then + co_wakeup(co, db) + return ret, err + end + add_db(self, db) + return ret, err +end + +local DB = class("DB") + +function DB:ctor(opt) + self.host = opt.host + self.port = opt.port + self.username = opt.username + self.password = opt.password + self.database = opt.database + self.charset = opt.charset + self.max = opt.max or 50 + self.current = 0 + -- 协程池 + self.co_pool = {} + -- 连接池 + self.db_pool = {} +end + +function DB:connect () + if not self.INITIALIZATION then + add_db(self, pop_db(self)) + self.INITIALIZATION = true + return self.INITIALIZATION + end + return self.INITIALIZATION +end + +-- 原始查询语句 +function DB:query(query) + assert(self.INITIALIZATION, "DB needs to be initialized first.") + return run_query(self, assert(type(query) == 'string' and query ~= '' and query , "Invalid PGSQL syntax.")) +end + +-- 字符串安全转义 +function DB.quote_to_str( str ) + return pgsql.quote_to_str(str) +end + +function DB:count() + assert(self.INITIALIZATION, "DB needs to be initialized first.") + return self.current, self.max, #self.co_pool, #self.db_pool +end + +return DB diff --git a/lualib/protocol/mssql.lua b/lualib/protocol/mssql.lua new file mode 100644 index 00000000..7c044a80 --- /dev/null +++ b/lualib/protocol/mssql.lua @@ -0,0 +1,1049 @@ +--[[ + LICENSE: BSD + Author: CandyMi[https://github.com/candymi] +]] + +local tcp = require "internal.TCP" + +local crypt = require "crypt" +local hexencode = crypt.hexencode + +local sys = require "sys" +local now = sys.now +local new_tab = sys.new_tab +local hostname = sys.hostname + +local null = null or NULL +local type = type +local pcall = pcall +local error = error +local strpack = string.pack +local strunpack = string.unpack + +local strchar = string.char +local strbyte = string.byte + +local fmt = string.format +local strgsub = string.gsub +local strsub = string.sub + +local toint = math.tointeger +local ceil = math.ceil +local random = math.random + +local os_date = os.date +local os_time = os.time + +local tabconcat = table.concat + +-- TDS公共头部类型 + +local PTYPE_QUERY = 0x01 -- 回应包类型 + +local PTYPE_RESPONSE = 0x04 -- 回应包类型 + +local PTYPE_LOGIN = 0x10 -- TDS-7.0 登录类型 + +-- TDS控制TOKEN类型 + +local ORDER_TOKEN = 0xA9 + +local ERROR_TOKEN = 0xAA + +local INFO_TOKEN = 0xAB + +local ACK_TOKEN = 0xAD + +local ENVCHANGE_TOKEN = 0xE3 + +local COLMETADATA_TOKEN = 0x81 + +local COLMETAROW_TOKEN = 0xD1 + +local COLMETAROW_TOKEN = 0xD1 + +local DONE_TOKEN = 0xFD + +-- TDS ENVCHANGE 字段表 +local TDS_ENV = { + "Database", + "Language", + "Character_set", + "Packet_size", + "Unicode_sorting_local_id", + "Unicode_sorting_comparison_flags", + "SQL_Collation", + "Begin_Transaction (described in [MSDN-BEGIN])", + "Commit_Transaction (described in [MSDN-COMMIT])", + "Rollback_Transaction", + "Enlist_DTC_Transaction", + "Defect_Transaction", + "Real_Time_Log_Shipping", + "Promote_Transaction", + "Transaction_Manager_Address", + "Transaction_ended", + "Acknowledgement", + "BackInfo", + "Routing", +} + +-- TDS字符集之间的转换兼容函数 + +-- 引入libiconv库实现 USC-2LE 与 UTF8 之间的转换 +local liconv, liconv_to, liconv_from +local ok, liconv_info = pcall(require, "liconv") +if ok then + liconv = liconv_info + liconv_to, liconv_from = liconv.to, liconv.from +end + +local function iconv_to (str, opcode) + return liconv and liconv_to(opcode, str) or str +end + +local function iconv_from (str, opcode) + return liconv and liconv_from(opcode, str) or str +end + +local function to_usc2le (s) + return strpack("z", s) +end + +local function from_usc2le (s) + local tab = new_tab(#s / 2, 0) + for index = 1, #s, 2 do + tab[#tab+1] = strchar(strbyte(s, index)) + end + return tabconcat(tab) +end + +local function TO_UCS2LE (s) + return liconv and iconv_to(s, "UCS-2LE") or strgsub(s, ".", to_usc2le) +end + +local function FROM_UCS2LE(s) + return liconv and iconv_from(s, "UCS-2LE") or from_usc2le(s) +end + +--[[ +加密算法原理: + 1. 对每个字符同时进行高/低位位移(4位); + 2. 将位移后的结果高位与低位进行"或"运算; + 3. 再将之后的运算结果异或0xA5(10100101); + 4. 最终的结果按位"与"0xFF取范围0~255; +C 函数原型: +uint8_t* tds7_crypt_pass(const uint8_t *clear_pass, int len, uint8_t *crypt_pass) { + for (int i = 0; i < len; i++) + crypt_pass[i] = ((clear_pass[i] << 4) | (clear_pass[i] >> 4)) ^ 0xA5; + return crypt_pass; +} +--]] +local function password_encrypt(password) + return strgsub(password, ".", function (ch) + return strchar(((strbyte(ch) << 4 | strbyte(ch) >> 4) ~ 0xA5 ) & 0xff) + end) +end + +-- TDS数据类型(N的位置不固定是因为官方文档命名的问题) +local TYPE_BITN = 0x68 -- (104) BITN +local TYPE_BIT = 0x32 -- (50) BIT + +local TYPE_INTN = 0x26 -- (38) INTN +local TYPE_INT1 = 0x30 -- (48) INT1 +local TYPE_INT2 = 0x34 -- (54) INT2 +local TYPE_INT4 = 0x38 -- (56) INT4 +local TYPE_INT8 = 0x7F -- (127) INT8 + +local TYPE_FLOAT32 = 0x3B -- (59) Float32 +local TYPE_FLOAT64 = 0x3E -- (62) Float64 +local TYPE_DECIMAL = 0x6A -- (106) Decimal +local TYPE_NUMERIC = 0x6C -- (108) Numeric +local TYPE_FLOATN = 0x6D -- (109) float32/64 + +local TYPE_MONEY8 = 0x3C -- (60) Money +local TYPE_MONEY4 = 0x7A -- (122) SmallMoney +local TYPE_MONEYN = 0x6E -- (110) NMoney + +local TYPE_BIGBIN = 0xAD -- (173) NBINARY +local TYPE_CHAR = 0xAF -- (175) Char +local TYPE_VARCHAR = 0xA7 -- (167) VarChar +local TYPE_NVARCHAR = 0xE7 -- (231) NVarChar +local TYPE_NCHAR = 0xEF -- (239) NChar +local TYPE_TEXT = 0x23 -- (35) TEXT +local TYPE_NTEXT = 0x63 -- (99) NTEXT + +local TYPE_DATETIMEN = 0x6F -- (111) DATETIME + +local TYPE_GEO = 0x22 -- (34) GEO + +local TYPE_GUID = 0x24 -- (36) GUID + +local TYPE_HIER = 0xA5 -- (165) Hierarchyid + +-- TDS Field转换方法 +local FTYPE_TAB = {} + +FTYPE_TAB[TYPE_HIER] = function (packet, pos) + return pos + 2 +end + +FTYPE_TAB[TYPE_GUID] = function (packet, pos) + return pos + 1 +end + +FTYPE_TAB[TYPE_GEO] = function (packet, pos) + local large_type_size, table_name_len + large_type_size, table_name_len, pos = strunpack("I2", packet, pos + 8)), + fmt("%02X", strunpack(">I6", packet, pos + 10)) + }, "-"), pos + 16 +end + +RTYPE_TAB[TYPE_BIT] = function (packet, pos) + local value + value, pos = strunpack("BBI2I2BB", OP_CODE, STATUS, LENGTH, CHANNEL, PACKNO, WINDOW) +end + +local function tds_unpack_header(packet) + return strunpack(">BBI2I2BB", packet) +end + +local function tds_login7( self ) + + local HOSTNAME = hostname() + -- print(HOSTNAME, #HOSTNAME) + + local APP_NAME = "cfadmin" + -- print(APP_NAME, #APP_NAME) + + local LOCALE = "us_english" + -- print(LOCALE, #LOCALE) + + local SERVER_NAME = self.host + local DATABASE = self.database + local USERNAME = self.username + local PASSWORD = self.password + + local msg = { + -- LOGIN 7 默认头部信息 + strpack("BBI2", 7, 1, 1), -- Client Version (7.1.1) + strpack(" 0 then + field_name = FROM_UCS2LE(strsub(packet, pos, pos + field_length * 2 - 1)) + pos = pos + field_length * 2 + end + fields[#fields+1] = { field_name = field_name, field_type = field_type, precision = precision, scale = scale } + end + return fields, pos +end + +local function tds_get_row_data (packet, pos, fields) + -- var_dump(fields) + local rows = new_tab(#fields, 0) + local value + for index = 1, #fields do + local field = fields[index] + local f = RTYPE_TAB[field.field_type] + -- print(pos) + if type(f) == 'function' then + value, pos = f(packet, pos, field.precision, field.scale) + else + error("Error: Unknown data type [" .. field.field_type .. "] ") + end + -- print(field.field_type, value, pos) + rows[#rows+1] = value + end + return rows, pos +end + +local function tds_done_to_tab(tab, status, operation, row_count) + -- 这个值应该被忽略 + tab["DONE_OPERATION"] = operation + -- 是否是最终的数据包 + tab["DONE_FINAL"] = status & 0x01 == 0x00 and true or false + -- 是否还有其他数据包 + tab["DONE_MORE"] = status & 0x01 == 0x01 and true or false + -- 是否是一个错误数据包 + tab["DONE_ERROR"] = status & 0x01 == 0x01 and true or false + -- 是否正在处理一个事务 + tab["DONE_TRANSACTION"] = status & 0x04 == 0x04 and true or false + -- TODO + tab["DONE_COUNT"] = status & 0x10 == 0x10 and row_count or 0 + -- TODO + tab["DONE_ATTN"] = status & 0x20 == 0x20 and true or false +end + +local function tds_get_done (packet, pos) + return strunpack("= #tds_data then + self:write(tds_pack_header(PTYPE_QUERY, 0x01, #tds_data + 8, 0, 1, 0) .. tds_data) + else + local fin = 0x00 + while 1 do + local body = strsub(tds_data, 1, self.max_packet_size) + if #tds_data <= self.max_packet_size then + fin = 0x01 + end + self:write(tds_pack_header(PTYPE_QUERY, fin, #body + 8, 0, 1, 0) .. body) + if fin == 0x01 then + break + end + tds_data = strsub(tds_data, self.max_packet_size + 1, -1) + end + end + return tds_read_response(self) +end + +local class = require "class" + +local mssql = class("mssql") + +function mssql:ctor(opt) + self.sock = tcp:new() + self.host = opt.host or "localhost" + self.port = opt.port or 1433 + self.TSQL = opt.TSQL == 1 and 1 or 0 + self.max_packet_size = opt.max_packet_size or 10240 + self.database = opt.database or "master" + self.username = opt.username or "sa" + self.password = opt.password + -- self.state = "connected" +end + +function mssql:read( bytes ) + local buffers = new_tab(32, 0) + local sock = self.sock + while 1 do + local data + if sock.ssl then + data = sock:ssl_recv(bytes) + else + data = sock:recv(bytes) + end + if not data then + return nil, "server close this session." + end + buffers[#buffers+1] = data + bytes = bytes - #data + if bytes <= 0 then + break + end + end + return tabconcat(buffers) +end + +function mssql:write(data) + return self.sock:send(data) +end + +function mssql:connect( ... ) + if not self.sock then + return nil, "Connection failed: please recreate the socket object." + end + + local ok, err = self.sock:connect(self.host, self.port) + if not ok then + return nil, err + end + + -- 发送TDS-7.0登录协议 + self:write(tds_login7(self)) + + local packet = tds_read_head(self) + if not packet then + return nil, "1. After sending the LOGIN data, the server disconnected." + end + + local OPCODE, STATUS, LENGTH, CHANNEL, PACKNO, WINDOW = tds_unpack_header(packet) + if OPCODE ~= PTYPE_RESPONSE then + return nil, "A protocol type not supported by TDS-7.0 was received." + end + + local packet = tds_read_body(self, LENGTH) + if not packet then + return nil, "2. After sending the LOGIN data, the server disconnected." + end + + local sever = new_tab(0, 6) + local pos = 1 + while 1 do + local token_type + token_type, pos = strunpack(" 0 then + if env_type ~= 0x07 then + new_value = FROM_UCS2LE(strsub(packet, pos, pos + new_len * 2 - 1)) + pos = pos + new_len * 2 + else + collate_codepage, collate_flags, collate_charset_id, pos = strunpack(" 0 then + old_value = FROM_UCS2LE(strsub(packet, pos, pos + old_len * 2 - 1)) + pos = pos + old_len * 2 + end + sever[type_name] = { + new_value = new_value, + old_value = old_value, + collate_codepage = collate_codepage, + collate_flags = collate_flags, + collate_charset_id = collate_charset_id + } + -- return sever + elseif token_type == INFO_TOKEN then + local info + info, pos = tds_get_errorinfo(packet, pos) + elseif token_type == ACK_TOKEN then + local len, interface, tds_version, server_name_len, server_name, server_version_max1, server_version_max2, server_version_min + len, pos = strunpack(" self._max_packet_size then - -- return nil, nil, "packet size too big: " .. len - -- end - - local num = strbyte(data, pos) - - --print("recv packet: packet no: ", num) - - self.packet_no = num - - local data, err = sock_recv(sock, len) - if not data then - self.state = nil - return nil, nil, "failed to read packet content: "..(err or "nil") - end - - local field_count, typ = strbyte(data, 1) - if field_count == 0x00 then - typ = "OK" - elseif field_count == 0xff then - typ = "ERR" - elseif field_count == 0xfe then - typ = "EOF" - else - typ = "DATA" - end - - return data, typ -end - - -local function _from_length_coded_bin(data, pos) - local first = strbyte(data, pos) - - if not first then - return nil, pos - end - - if first >= 0 and first <= 250 then - return first, pos + 1 - end - - if first == 251 then - return null, pos + 1 - end - - if first == 252 then - pos = pos + 1 - return _get_byte2(data, pos) - end - - if first == 253 then - pos = pos + 1 - return _get_byte3(data, pos) - end - - if first == 254 then - pos = pos + 1 - return _get_byte8(data, pos) - end - - return nil, pos + 1 + local filename = randomkey(8, true) .. '.pem' + local f = assert(io_open(filename, 'a'), "Can't Create public_key file to complate handshake.") + f:write(public_key):flush() + f:close() + return rsa_oaep_pkey_encode(xor_str(password, scramble), filename), io_remove(filename) end -local function _set_length_coded_bin(n) - if n < 251 then - return strchar(n) - end - - if n < (1 << 16) then - return strpack(" pos then + message, pos = strunpack("s1", packet, pos) + end + return { + auto_commit = server_status & 0x02 == 0x02 and true or false, transaction = server_status & 0x01 == 0x01 and true or false, + last_insertid = last_insertid, affected_rows = affected_rows, server_warnings = server_warnings, message = message + }, server_status & SERVER_MORE_RESULTS == SERVER_MORE_RESULTS end -function MySQL.ctor(self) +local function read_response (self, results) + local packet, err = read_packet(self) + if not packet then self.state = nil - self.sock = tcp:new() - self._VERSION = '0.22' -end - - -function MySQL.set_timeout(self, timeout) - self.sock._timeout = timeout -end - - -function MySQL.connect(self, opts) - local sock = self.sock - if not sock then - return nil, "not initialized" + return nil, "1. mysql server closed when client sended query packet." + end + local status = strbyte(packet, 1) + if status == RESP_ERROR then + return nil, get_mysql_error_packet(packet:sub(2)) + end + if status == RESP_OK then + local tab, again = get_ok(packet:sub(2), #packet - 1) + if again then -- 如果是`多结果集的数据 + if type(results) == 'table' then + results[#results+1] = tab + return read_response(self, results) + end + return read_response(self, { tab }) end - - local max_packet_size = opts.max_packet_size - if not max_packet_size then - max_packet_size = 16 * 1024 * 1024 -- default 4 MB + if type(results) == 'table' then + results[#results+1] = tab + tab = results end - self._max_packet_size = max_packet_size - - self.compact = opts.compact_arrays - - local database = opts.database or "" - local username = opts.username or "" - local password = opts.password or "" + return tab + end - local host = opts.host - if not host then - return nil, "not host" + local fields = new_tab(status, 0) + for index = 1, status do + local field, err = get_field(self) + if not field then + self.state = nil + return nil, err end + fields[#fields+1] = field + end - local port = opts.port or 3306 - - local ok = sock:connect(host, port) - if not ok then - return nil, "Connect failed" - end + local again = false + local len, err = read_head(self) + if not len then + self.state = nil + return nil, "2. mysql server closed when client sended query packet." + end + local status, err = read_status(self) + if not status then + self.state = nil + return nil, err + end + local sever = get_eof(self, len - 1) + if sever.status_flags & SERVER_MORE_RESULTS == SERVER_MORE_RESULTS then + again = true + end - local packet, typ, err = _recv_packet(self) + local rows = new_tab(32, 0) + while 1 do + local packet, err = read_packet(self) if not packet then - return nil, err - end - - if typ == "ERR" then - local errno, msg, sqlstate = _parse_err_packet(packet) - return nil, msg, errno, sqlstate + self.state = nil + return nil, err end - - self.protocol_ver = strbyte(packet) - - --print("protocol version: ", self.protocol_ver) - - local server_ver, pos = _from_cstring(packet, 2) - if not server_ver then - return nil, "bad handshake initialization packet: bad server version" - end - - --print("server version: ", server_ver) - - self._server_ver = server_ver - - local thread_id, pos = _get_byte4(packet, pos) - - --print("thread id: ", thread_id) - - local scramble = sub(packet, pos, pos + 8 - 1) - if not scramble then - return nil, "1st part of scramble not found" + if strbyte(packet, 1) == RESP_EOF and #packet < 9 then + local sever = get_eof_packet(packet) + if sever.status_flags & SERVER_MORE_RESULTS == SERVER_MORE_RESULTS then + again = true + end + break end + rows[#rows+1] = get_rows(packet, #fields) + end - pos = pos + 9 -- skip filler - - -- two lower bytes - local capabilities -- server capabilities - capabilities, pos = _get_byte2(packet, pos) - - -- print(format("server capabilities: %#x", capabilities)) - - self._server_lang = strbyte(packet, pos) - pos = pos + 1 - - --print("server lang: ", self._server_lang) - - self._server_status, pos = _get_byte2(packet, pos) - - --print("server status: ", self._server_status) - - local more_capabilities - more_capabilities, pos = _get_byte2(packet, pos) - - capabilities = capabilities | more_capabilities << 16 - - --print("server capabilities: ", capabilities) - - -- local len = strbyte(packet, pos) - local len = 21 - 8 - 1 - - --print("scramble len: ", len) - - pos = pos + 1 + 10 - - local scramble_part2 = sub(packet, pos, pos + len - 1) - if not scramble_part2 then - return nil, "2nd part of scramble not found" + local result = new_tab(#rows, 0) + for _, row in ipairs(rows) do + local tab = new_tab(0, #fields) + for index, item in ipairs(row) do + local field = fields[index] + local call = converts[field.field_type] + if not call then + -- print("not call") + tab[field.field_name] = item + else + -- print(field.field_type, field.field_name, item, call(item), item == null) + tab[field.field_name] = call(item) + end end + result[#result+1] = tab + end - scramble = scramble .. scramble_part2 - --print("scramble: ", _dump(scramble)) - - local req, token - local client_flags = 260047 - if find(packet, "caching_sha2_password") then - client_flags = client_flags | 0x80000 | 0x200000 - token = caching_sha2_password(password, scramble) - req = strpack(" 0 then + results[#results+1] = result + return read_response(self, results) else - token = mysq_native_password(password, scramble) - req = strpack(" 0 then - local bitmap = (num_params + 7) // 8 - local types = new_tab(num_params, 0) - local values = new_tab(num_params, 0) - for _, v in pairs(opt) do - local f = store_types[type(v)] - if not f then - error("execute has UnSupport value and type.") - end - types[#types+1], values[#values+1] = f(v) - end - query = query .. strrep("\0", bitmap) .. strchar(0x01) .. concat(types) .. concat(values) +local function mysql_login (self) + local sock = self.sock + if not sock or not sock:connect(self.host, self.port) then + return nil, "MySQL Server Connect failed." end - return _send_packet(self, query, #query) -end -function MySQL.read_prepare(self) - local data, typ = _recv_packet(self) - if typ == "ERR" then - local errno, msg, sqlstate = _parse_err_packet(data) - return nil, msg, errno, sqlstate - end - - local status, statement_id, num_columns, num_params, reserved, warning_count = _recv_prepare_status(data) - - if num_params > 0x00 then - for i = 1, num_params, 1 do - local ok, err = _recv_ignore(self) - if not ok then - if err then - return nil, err - end - break - end - end - end + local len, err = read_head(self) + if not len then + return nil, err + end - if num_columns > 0x00 then - for i = 1, num_columns, 1 do - local ok, err = _recv_ignore(self) - if not ok then - if err then - return nil, err - end - break - end - end - end - while 1 do - local ok, typ = _recv_ignore(self) - if not ok then - return nil, typ - end - if typ == "EOF" or typ == "ERR" then - break - end - end - return { success = typ == 'OK' and true or false, status = status, statement_id = statement_id, num_columns = num_columns, num_params = num_params, reserved = reserved, warning_count = warning_count } -end + local packet, err = read_body(self, len) + if not packet then + return nil, err + end + local protocol, version, tid, salt, salt_len, auth_plugin, pos + protocol, version, tid, pos = strunpack(" 2 then - if byte & (1 << j) == 0 then - null_fields[field_index - 2] = false - else - null_fields[field_index - 2] = true - end - end - field_index = field_index + 1 - end - end + local packet, err = read_packet(self) + if not packet then + return nil, "mysql server closed when client sended login packet." + end - local row = new_tab(ncols, 0) - for i = 1, ncols do - local col = cols[i] - local typ = col.type - local name = col.name - if not null_fields[i] then - local f = _binary_parser[typ] - if not f then - error("_parse_row_data_binary()error,unsupported field type " .. typ) - end - value, pos = f(data, pos) - if compact then - row[i] = value - else - row[name] = value - end - end - end + local status, method = strbyte(packet, 1), strbyte(packet, 2) - return row -end + if status == RESP_EOF then + return nil, "1. MySQL Authentication protocol not supported." + end -function MySQL.read_execute(self, est_nrows) - local packet, typ, err = _recv_packet(self, sock) - if not packet then + -- Auth Plugin Change + if auth_plugin == "caching_sha2_password" then + -- Need more authentication + if status == 0x01 and method == 0x04 then + self.packet_no = self.packet_no + 1 + send_packet(self, '\x02') + local public_key, err = read_packet(self) + if not public_key then return nil, err - --error( err ) - end - - if typ == "ERR" then - local errno, msg, sqlstate = _parse_err_packet(packet) - return nil, msg, errno, sqlstate - --error( strformat("errno:%d, msg:%s,sqlstate:%s",errno,msg,sqlstate)) - end - - if typ == "OK" then - local res = _parse_ok_packet(packet) - if res and res.server_status & SERVER_MORE_RESULTS_EXISTS ~= 0 then - return res, "again" - end - return res - end - - if typ ~= "DATA" then - return nil, "packet type " .. typ .. " not supported" - --error( "packet type " .. typ .. " not supported" ) - end - - local field_count, extra = _parse_result_set_header_packet(packet) - - local cols = new_tab(field_count, 0) - local col - while true do - packet, typ, err = _recv_packet(self, sock) - if typ == "EOF" then - local warning_count, status_flags = _parse_eof_packet(packet) - break - end - col = _parse_field_packet(packet) - if not col then - break - end - cols[#cols + 1] = col - end - - --没有记录集返回 - if #cols < 1 then - return {} - end - - local compact = self.compact - local rows = {} - local row - while true do - packet, typ, err = _recv_packet(self, sock) - if typ == "EOF" then - local warning_count, status_flags = _parse_eof_packet(packet) - if status_flags & SERVER_MORE_RESULTS_EXISTS ~= 0 then - return rows, "again" - end - break - end - row = _parse_row_data_binary(packet, cols, compact) - if not col then - break - end - rows[#rows + 1] = row - end - - return rows -end - -function MySQL.read_result(self, est_nrows) - - local packet, typ, err = _recv_packet(self) - if not packet then - self.state = nil + end + self.packet_no = self.packet_no + 1 + send_packet(self, rsa_encode(public_key:sub(2, -2), self.password .. "\x00", salt)) + packet, err = read_packet(self) + if not packet then return nil, err + end + status, method = strbyte(packet, 1), strbyte(packet, 2) end - - if typ == "ERR" then - local errno, msg, sqlstate = _parse_err_packet(packet) - return nil, msg, errno, sqlstate - end - - if typ == 'OK' then - local res = _parse_ok_packet(packet) - if res and (res.server_status & SERVER_MORE_RESULTS_EXISTS) ~= 0 then - return res, "again" - end - return res - end - - if typ ~= 'DATA' then - self.state = nil - return nil, "this type: " .. typ .. " is not supported 1" + -- Already Caching sha2 password. + if status == 0x01 and method == 0x03 then + packet, err = read_packet(self) + if not packet then + return nil, err + end + status, method = strbyte(packet, 1), strbyte(packet, 2) end + end - local field_count, extra = _parse_result_set_header_packet(packet) - - local cols = new_tab(field_count, 0) - for i = 1, field_count do - local col, err, errno, sqlstate = _recv_field_packet(self) - if not col then - return nil, err, errno, sqlstate - end - cols[i] = col - end + -- Server Send Error Response. + if status == RESP_ERROR then + return nil, get_mysql_error_packet(packet:sub(2)) + end - local packet, typ, err = _recv_packet(self) - if not packet then - self.state = nil - return nil, err - end + -- 不支持的协议. + if status ~= RESP_OK then + return nil, "2. MySQL Authentication protocol not supported." + end - if typ ~= 'EOF' then - return nil, "this type: " .. typ .. " is not supported 2" - end + self.sever = { protocol = protocol, version = version, tid = tid, auth_plugin = auth_plugin, status = get_ok(packet:sub(2), #packet - 1) } + self.state = "connected" + -- var_dump(self.sever) + return true +end - local compact = self.compact - local rows = new_tab(est_nrows or 16, 0) - while true do - packet, typ, err = _recv_packet(self) - if not packet then - self.state = nil - return nil, err - end - if typ == 'EOF' then - local warning_count, status_flags = _parse_eof_packet(packet) - if (status_flags & SERVER_MORE_RESULTS_EXISTS) ~= 0 then - return rows, "again" - end - break - end - rows[#rows + 1] = _parse_row_data_packet(packet, cols, compact) - end - return rows +local function mysql_query (self, sql) + send_packet(self, strpack("zzzzzzz", msg) + return strsub(severity, 2), strsub(text, 2), strsub(code, 2), strsub(message, 2), strsub(file, 2), strsub(line, 2), strsub(routine, 2) +end + +local function get_error_message_fmt(msg) + local _, etype, code, message = get_error_message(msg) + return nil, fmt("[%s] : {'%s', '%s'}", code, etype, message) +end + +local function get_error_message_tab(msg) + local severity, text, code, message, file, line, routine = string.unpack(">zzzzzzz", msg) + return { + severity = severity, + text = text, + code = code, + message = message, + file = file, + line = line, + routine = routine + } +end + +local function read_opcode_and_len (self) + local opstr = self.sock:recv(1) + if not opstr then + return nil, "client read: server close this session." + end + local opcode = RESPONSES[strbyte(opstr)] + local len_byte, err = self:read(4) + if not len_byte then + return nil, "client read: server close this session." + end + local len = strunpack(">I4", len_byte) + if not len then + return nil, "An unrecognized message type was received." + end + return opcode, len +end + +local function read_head (self) + local opcode, len = read_opcode_and_len(self) + return opcode, len, self:read(4) +end + +local function get_query_error_msg (self, data) + local _, etype, code, message = get_error_message(data) + local msg = fmt("[%s] : {'%s', '%s'}", code, etype, message) + local opcode, len = read_opcode_and_len(self) + if opcode ~= RESP_READY then + return nil, len + end + local _ = self.sock:recv(len - 4) + return nil, msg +end + +local function read_column_data (self, data_len) + local row_data = self:read(data_len) + if not row_data then + return nil, "client read: server close this session. " + end + local len, index = strunpack(">I2", row_data) + local columns = new_tab(len, 0) + for i = 1, len do + local column_name, pos = strunpack(">z", row_data, index) + -- print(column_name, pos) + local column_table_oid, pos = strunpack(">I4", row_data, pos) + -- print(column_table_oid, pos) + local column_index, pos = strunpack(">I2", row_data, pos) + -- print(column_index, pos) + local column_type_oid, pos = strunpack(">I4", row_data, pos) + -- print(column_type_oid, pos) + local column_length, pos = strunpack(">I2", row_data, pos) + -- print(column_length, pos) + local column_type_modifier, pos = strunpack(">i4", row_data, pos) + -- print(column_type_modifier, pos) + local column_format, pos = strunpack(">I2", row_data, pos) + -- print(column_format, pos) + index = pos + columns[#columns+1] = { + column_name = column_name, + column_type_oid = column_type_oid, + -- column_index = column_index, + -- column_length = column_length, + -- column_format = column_format, + -- column_table_oid = column_table_oid, + -- column_type_modifier = column_type_modifier, + } + end + return columns +end + +local function read_row_data (self) + local opcode, len = read_opcode_and_len(self) + if not opcode then + return nil, "server close this session." + end + if opcode == RESP_CMD_COMPLETION then + return opcode, len + end + local row_data = self:read(len - 4) + if not row_data then + return nil, "server close this session." + end + local index = 3 + local count = strunpack(">I2", row_data) + local row = new_tab(count, 0) + for i = 1, count do + local raw_len, pos = strunpack(">i4", row_data, index) + -- print(raw_len) + if raw_len > -1 then + row[#row + 1] = row_data:sub(pos, pos + raw_len - 1) + index = pos + raw_len + else + row[#row + 1] = null + index = pos + end + end + return row +end + +local function read_response (self) + local results = {} + while 1 do + local opcode, len = read_opcode_and_len(self) + if not opcode then + self.status = "closed" + return nil, "1. server close this session." + end + if opcode == RESP_ERROR then + return get_query_error_msg(self, self:read(len - 4):sub(5)) + end + local result + if opcode == RESP_STATUS then + local kv = self:read(len - 4) + if not kv then + self.status = "closed" + return nil, "2. server close this session." + end + local k, v = strunpack("zz", kv) + if not result then + result = { ok = true, [k] = v } + else + result[k] = v + end + result['ok'] = true + result['action'] = "SET" + result['status'] = "Idle" + results[#results + 1] = result + local opcode, len = read_opcode_and_len(self) + if not opcode then + self.status = "closed" + return nil, "3. server close this session." + end + if opcode == RESP_CMD_COMPLETION then + local _ = self:read(len - 4) + end + elseif opcode == RESP_CMD_COMPLETION then + local tab = new_tab(3, 0) + local content = self:read(len - 4) + if not content then + self.status = "closed" + return nil, "4. server close this session." + end + for v in strgmatch(content, "[^ \x00]+") do + tab[#tab+1] = v + end + if not result then + result = new_tab(0, 5) + end + local action = tab[1] + result['ok'] = true + result['status'] = "Idle" + result['action'] = action + if action == "INSERT" then + result['oid'] = toint(tab[2]) + result['affected_rows'] = toint(tab[3]) + elseif action == "UPDATE" or action == "DELETE" then + result['affected_rows'] = toint(tab[2]) + else + result["rows"] = toint(tab[2]) + end + results[#results + 1] = result + -- var_dump(results) + elseif opcode == RESP_READY then + local v = self:read(len - 4) + if not v then + self.status = "closed" + return nil, "5. server close this session." + end + break + elseif opcode == RESP_COLUMN then + local columns, err = read_column_data(self, len - 4) + if not columns then + self.status = "closed" + return nil, err + end + -- var_dump(columns) + local row, len + local rows = new_tab(128, 0) + while 1 do + row, len = read_row_data(self) + if type(row) == 'table' then + rows[#rows + 1] = row + elseif type(row) == 'number' then + break + else + self.status = "closed" + return nil, "6. server close this session." + end + end + -- var_dump(rows) + result = new_tab(#rows, 0) + for _, row in ipairs(rows) do + local tab = {} + for index, column in ipairs(columns) do + tab[column.column_name] = convert(column.column_type_oid, row[index]) + end + result[#result + 1] = tab + end + results[#results+1] = result + if row == RESP_CMD_COMPLETION then + local v = self:read(len - 4) + if not v then + self.status = "closed" + return nil, "7. server close this session." + end + end + end + end + return #results == 1 and results[1] or results +end + +-- AUTH_MD5 +local function auth_md5(auth_user, auth_password, auth_salt) + return "md5" .. md5(md5(auth_password .. auth_user, true) .. auth_salt, true) +end + +-- 设置客户端连接参数 +local function set_conn_param(key, value) + return strpack("zz", key, value) +end + +local class = require "class" + +local pgsql = class("pgsql") + +function pgsql:ctor(opt) + self.sock = tcp:new() + self.host = opt.host or "localhost" + self.port = opt.port or 3306 + self.database = opt.database or "postgres" + self.username = opt.username or "postgres" + self.password = opt.password or "postgres" + self.charset = opt.charset or "UTF8" + self.application_name = opt.application_name or "cfadmin" +end + +function pgsql:read(bytes) + local sock = self.sock + local buffers = new_tab(32, 0) + while 1 do + local data = sock:recv(bytes) + if not data then + return nil, "server close this session." + end + buffers[#buffers+1] = data + bytes = bytes - #data + if bytes <= 0 then + break + end + end + return tconcat(buffers) +end + +function pgsql:write(data) + return self.sock:send(data) +end + +function pgsql:startup () + local connect_params = tconcat { + strpack(">I2I2", 3, 0), -- 使用3.0交互协议 + set_conn_param("user", self.username), -- 设置客户端登录名 + set_conn_param("database", self.database), -- 设置客户端数据库 + set_conn_param("client_encoding", self.charset), -- 设置客户端字符集 + set_conn_param("application_name", self.application_name), -- 设置客户端应用名 + "\x00", + } + return strpack(">I4", #connect_params + 4) .. connect_params +end + +function pgsql:authmd5 (data) + return strpack(">BI4z", strbyte("p"), 40, auth_md5(self.username, self.password, data)) +end + +function pgsql:connect() + + local ok, err = self.sock:connect(self.host, self.port) + if not ok then + return nil, err + end + + -- 发送启动协议 + self:write(self:startup()) + + local opcode, len, auth_type = read_head(self) + if opcode ~= RESP_OK or not auth_type then + return nil, "1. Malformed response packets." + end + if opcode == RESP_ERROR then + return get_error_message_fmt(auth_type .. self:read(len - 8) ) + end + + auth_type = strunpack(">I4", auth_type) + if auth_type == RESP_AUTHMD5 then + self:write(self:authmd5(self:read(len - 8))) + opcode, len, status = read_head(self) + if not opcode or not status then + return nil, "2. Malformed response packets." + end + if opcode == RESP_ERROR then + return get_error_message_fmt(status .. self:read(len - 8) ) + end + end + + -- 获取服务器配置信息 + local server = new_tab(0, 16) + while 1 do + local opcode, len = read_opcode_and_len(self) + if opcode == RESP_STATUS then + local k, v = strunpack("zz", self:read(len - 4)) + server[k] = v == 'on' and true or v + elseif opcode == RESP_STATUS_END then + server["pid"] = strunpack(">I4", self:read(4)) + server["key"] = strunpack(">I4", self:read(4)) + elseif opcode == RESP_READY then + server["status"] = "Idle" + self:read(len - 4) -- 读取并丢弃无用的数据 + break + elseif opcode == RESP_ERROR then + return get_error_message_fmt(self:read(len - 4) ) + else + return nil, "3. Malformed response packets." + end + end + + self.status = "connected" + self.server = server + return server +end + +function pgsql:query (sql) + if type(sql) ~= 'string' or sql == '' then + return nil, "Invalid SQL." + end + self:write(strpack(">BI4z", OP_QUERY, #sql + 5, sql)) + return read_response(self) +end + +local escape_map = { + ['\0'] = "\\0", + ['\b'] = "\\b", + ['\n'] = "\\n", + ['\r'] = "\\r", + ['\t'] = "\\t", + ['\26'] = "\\Z", + ['\\'] = "\\\\", + ["'"] = "\\'", + ['"'] = '\\"', +} + +function pgsql.quote_to_str (sql) + return fmt("%s", string.gsub(sql, "[\0\b\n\r\t\26\\\'\"]", escape_map)) +end + +function pgsql:set_timeout(timeout) + if self.sock and tonumber(timeout) then + self.sock._timeout = timeout + end +end + +function pgsql:close() + if self.status == "connected" then + self.status = "closed" + self:write(strpack(">BI4", OP_TERMINATE, 4)) + end + if self.sock then + self.sock:close() + self.sock = nil + end +end + +return pgsql From 984bfcba10cdb19371c2bab3b3c51981299140ed Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 22 Oct 2020 20:38:02 +0800 Subject: [PATCH 644/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=8F=AF=E8=83=BD?= =?UTF-8?q?=E5=87=BA=E7=8E=B0=E7=9A=84sql=E6=89=A7=E8=A1=8C=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E7=9A=84=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/http/dashboard.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/admin/http/dashboard.lua b/lualib/admin/http/dashboard.lua index a3359723..a17f6280 100644 --- a/lualib/admin/http/dashboard.lua +++ b/lualib/admin/http/dashboard.lua @@ -27,7 +27,7 @@ local function verify_permission (content, db) if logout then -- 注销登录 local tk = Cookie.getCookie('CFTOKEN') if tk then -- 注销的时候有token必须清除 - user_token.token_delete(db, nil, tk) + user_token.token_delete(db, 0, tk) end return false, config.login_render end From 49cd1cf7da30ab2dd6de9c27ce64d0e29a2726ba Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 22 Oct 2020 20:38:26 +0800 Subject: [PATCH 645/956] =?UTF-8?q?httpc=E5=A2=9E=E5=8A=A0=E8=BF=94?= =?UTF-8?q?=E5=9B=9Eheaders=E7=9A=84=E7=89=B9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/class.lua | 28 ++++++++++++++-------------- lualib/httpc/init.lua | 28 ++++++++++++++-------------- lualib/httpc/protocol.lua | 14 +++++++------- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/lualib/httpc/class.lua b/lualib/httpc/class.lua index 0c3a480c..89ca8196 100644 --- a/lualib/httpc/class.lua +++ b/lualib/httpc/class.lua @@ -167,12 +167,12 @@ function httpc:raw( parameter ) return false, err end - local code, msg = httpc_response(self.sock, opt.protocol) + local code, msg, headers = httpc_response(self.sock, opt.protocol) if not code then self.sock:close() self.sock = nil end - return code, msg + return code, msg, headers end @@ -205,12 +205,12 @@ function httpc:get (domain, headers, args, timeout) return false, err end - local code, msg = httpc_response(self.sock, opt.protocol) + local code, msg, headers = httpc_response(self.sock, opt.protocol) if not code then self.sock:close() self.sock = nil end - return code, msg + return code, msg, headers end -- post 请求 @@ -242,12 +242,12 @@ function httpc:post (domain, headers, body, timeout) return false, err end - local code, msg = httpc_response(self.sock, opt.protocol) + local code, msg, headers = httpc_response(self.sock, opt.protocol) if not code then self.sock:close() self.sock = nil end - return code, msg + return code, msg, headers end -- delete 请求 @@ -279,12 +279,12 @@ function httpc:delete (domain, headers, body, timeout) return false, err end - local code, msg = httpc_response(self.sock, opt.protocol) + local code, msg, headers = httpc_response(self.sock, opt.protocol) if not code then self.sock:close() self.sock = nil end - return code, msg + return code, msg, headers end -- put 请求 @@ -316,12 +316,12 @@ function httpc:put (domain, headers, body, timeout) return false, err end - local code, msg = httpc_response(self.sock, opt.protocol) + local code, msg, headers = httpc_response(self.sock, opt.protocol) if not code then self.sock:close() self.sock = nil end - return code, msg + return code, msg, headers end -- json 请求 @@ -356,12 +356,12 @@ function httpc:json (domain, headers, json, timeout) return false, err end - local code, msg = httpc_response(self.sock, opt.protocol) + local code, msg, headers = httpc_response(self.sock, opt.protocol) if not code then self.sock:close() self.sock = nil end - return code, msg + return code, msg, headers end -- file 请求 @@ -393,12 +393,12 @@ function httpc:file (domain, headers, files, timeout) return false, err end - local code, msg = httpc_response(self.sock, opt.protocol) + local code, msg, headers = httpc_response(self.sock, opt.protocol) if not code then self.sock:close() self.sock = nil end - return code, msg + return code, msg, headers end -- 异步请求 diff --git a/lualib/httpc/init.lua b/lualib/httpc/init.lua index a44dafb8..d21963dd 100644 --- a/lualib/httpc/init.lua +++ b/lualib/httpc/init.lua @@ -104,9 +104,9 @@ local function raw( parameter ) sock:close() return ok, err end - local code, msg = httpc_response(sock, opt.protocol) + local code, msg, headers = httpc_response(sock, opt.protocol) sock:close() - return code, msg + return code, msg, headers end -- HTTP GET @@ -133,9 +133,9 @@ local function get(domain, headers, args, timeout) sock:close() return ok, err end - local code, msg = httpc_response(sock, opt.protocol) + local code, msg, headers = httpc_response(sock, opt.protocol) sock:close() - return code, msg + return code, msg, headers end -- HTTP POST @@ -162,9 +162,9 @@ local function post(domain, headers, body, timeout) sock:close() return ok, err end - local code, msg = httpc_response(sock, opt.protocol) + local code, msg, headers = httpc_response(sock, opt.protocol) sock:close() - return code, msg + return code, msg, headers end -- HTTP DELETE @@ -192,9 +192,9 @@ local function delete(domain, headers, body, timeout) sock:close() return ok, err end - local code, msg = httpc_response(sock, opt.protocol) + local code, msg, headers = httpc_response(sock, opt.protocol) sock:close() - return code, msg + return code, msg, headers end -- HTTP PUT @@ -222,9 +222,9 @@ local function put(domain, headers, body, timeout) sock:close() return ok, err end - local code, msg = httpc_response(sock, opt.protocol) + local code, msg, headers = httpc_response(sock, opt.protocol) sock:close() - return code, msg + return code, msg, headers end local function json(domain, headers, json, timeout) @@ -253,9 +253,9 @@ local function json(domain, headers, json, timeout) sock:close() return ok, err end - local code, msg = httpc_response(sock, opt.protocol) + local code, msg, headers = httpc_response(sock, opt.protocol) sock:close() - return code, msg + return code, msg, headers end local function file(domain, headers, files, timeout) @@ -282,9 +282,9 @@ local function file(domain, headers, files, timeout) sock:close() return ok, err end - local code, msg = httpc_response(sock, opt.protocol) + local code, msg, headers = httpc_response(sock, opt.protocol) sock:close() - return code, msg + return code, msg, headers end local function multi_request (opt) diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua index b1e7f849..3be94c33 100644 --- a/lualib/httpc/protocol.lua +++ b/lualib/httpc/protocol.lua @@ -160,14 +160,14 @@ local function httpc_response(sock, SSL) if not CODE or not HEADER then return nil, SSL.." can't resolvable protocol." end - if CODE == 302 or CODE == 301 then - return CODE, HEADER['Location'] or HEADER['location'] + if CODE == 302 or CODE == 301 or CODE == 303 or CODE == 307 then + return CODE, HEADER['Location'] or HEADER['location'], HEADER end local Content_Encoding = HEADER['Content-Encoding'] or HEADER['content-encoding'] local Content_Length = toint(HEADER['Content-Length'] or HEADER['content-length']) local Chunked = HEADER['Transfer-Encoding'] or HEADER['transfer-encoding'] if not Content_Length and not Chunked then - return CODE, STATUS + return CODE, STATUS, HEADER end if Content_Length then if (#DATA - posB) == Content_Length then @@ -175,7 +175,7 @@ local function httpc_response(sock, SSL) if Content_Encoding == "gzip" then res = gzuncompress(res) end - return CODE, res + return CODE, res, HEADER end local content = new_tab(8, 0) content[#content+1] = split(DATA, posB + 1, #DATA) @@ -192,7 +192,7 @@ local function httpc_response(sock, SSL) if Content_Encoding == "gzip" then res = gzuncompress(res) end - return CODE, res + return CODE, res, HEADER end end end @@ -210,7 +210,7 @@ local function httpc_response(sock, SSL) if Content_Encoding == "gzip" then res = gzuncompress(res) end - return CODE, res + return CODE, res, HEADER end insert(content, buf) end @@ -230,7 +230,7 @@ local function httpc_response(sock, SSL) if Content_Encoding == "gzip" then res = gzuncompress(res) end - return CODE, res + return CODE, res, HEADER end end end From 9cdfcb0c349d8cf443c60831ec5c894e1a66d9f5 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 24 Oct 2020 17:07:01 +0800 Subject: [PATCH 646/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0admin=E5=BA=93?= =?UTF-8?q?=E7=9A=84=E6=9D=83=E9=99=90=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/db/permission.lua | 26 ++++++++++++++------------ lualib/admin/html/deny.html | 19 +++++++++++++++++++ lualib/admin/http/system/header.lua | 4 +++- lualib/admin/http/system/menu.lua | 4 +++- lualib/admin/http/system/role.lua | 4 +++- lualib/admin/http/system/user.lua | 4 +++- lualib/admin/utils/init.lua | 8 ++++++++ lualib/admin/view.lua | 22 ++++++++++++++-------- 8 files changed, 67 insertions(+), 24 deletions(-) create mode 100644 lualib/admin/html/deny.html diff --git a/lualib/admin/db/permission.lua b/lualib/admin/db/permission.lua index 389afe68..6ec00ed5 100644 --- a/lualib/admin/db/permission.lua +++ b/lualib/admin/db/permission.lua @@ -4,20 +4,22 @@ local permission = {} -- 用户是否有此菜单的权限 function permission.user_have_menu_permission (db, uid, url) - local ret, err = db:query(fmt([[ - SELECT - count(`cfadmin_menus`.`id`) AS count - FROM - cfadmin_users, cfadmin_menus, cfadmin_permissions - WHERE - (`cfadmin_users`.id = '%s' AND `cfadmin_users`.active = '1') AND - (`cfadmin_menus`.active = '1' AND `cfadmin_menus`.url = '%s') AND `cfadmin_permissions`.active = '1' AND - `cfadmin_users`.role = `cfadmin_permissions`.role_id AND `cfadmin_permissions`.menu_id = `cfadmin_menus`.id]], - uid, url)) - if ret and ret[1] and ret[1]['count'] == 1 then + -- 查询用户Role ID + local uinfo, err = db:query(fmt([[SELECT id, role AS role_id FROM cfadmin_users WHERE `cfadmin_users`.id = %u AND `cfadmin_users`.active = 1 LIMIT 1]], uid))[1] + if type(uinfo) ~= 'table' then + return false + end + -- 查询菜单Menu ID + local minfo, err = db:query(fmt([[SELECT * FROM cfadmin_menus WHERE `cfadmin_menus`.url = '%s' AND `cfadmin_menus`.active = '1' LIMIT 1]], url))[1] + if type(minfo) ~= 'table' then return true end - return false + -- 检查权限 + local role, err = db:query(fmt([[SELECT * FROM cfadmin_permissions p WHERE p.`active` = 1 AND p.`role_id` = %u AND p.`menu_id` = %u LIMIT 1]], uinfo.role_id, minfo.id)) + if type(role) == 'table' then + return role[1] + end + return role, err end return permission diff --git a/lualib/admin/html/deny.html b/lualib/admin/html/deny.html new file mode 100644 index 00000000..0b095c3c --- /dev/null +++ b/lualib/admin/html/deny.html @@ -0,0 +1,19 @@ + + + + Access Deny + + + + + + + diff --git a/lualib/admin/http/system/header.lua b/lualib/admin/http/system/header.lua index d2f95d89..1fec3833 100644 --- a/lualib/admin/http/system/header.lua +++ b/lualib/admin/http/system/header.lua @@ -21,7 +21,9 @@ local tostring = tostring local os_date = os.date local toint = math.tointeger +local get_path = utils.get_path local get_locale = utils.get_locale +local access_deny = utils.access_deny local template_path = 'lualib/admin/html/system/header/' @@ -36,7 +38,7 @@ local function verify_permission (content, db) end local info = user.user_info(db, exists.uid) if not info or info.is_admin ~= 1 then - return false, config.login_render + return false, access_deny(get_path(content)) end info.token = exists.token info.roles = role.role_permissions(db, info.role) diff --git a/lualib/admin/http/system/menu.lua b/lualib/admin/http/system/menu.lua index f08050fc..d1f433fa 100644 --- a/lualib/admin/http/system/menu.lua +++ b/lualib/admin/http/system/menu.lua @@ -21,7 +21,9 @@ local ipairs = ipairs local os_date = os.date local toint = math.tointeger +local get_path = utils.get_path local get_locale = utils.get_locale +local access_deny = utils.access_deny local template_path = 'lualib/admin/html/system/menu/' @@ -36,7 +38,7 @@ local function verify_permission (content, db) end local info = user.user_info(db, exists.uid) if not info or info.is_admin ~= 1 then - return false, config.login_render + return false, access_deny(get_path(content)) end info.token = exists.token info.roles = role.role_permissions(db, info.role) diff --git a/lualib/admin/http/system/role.lua b/lualib/admin/http/system/role.lua index 5dae7141..4107c8df 100644 --- a/lualib/admin/http/system/role.lua +++ b/lualib/admin/http/system/role.lua @@ -20,7 +20,9 @@ local ipairs = ipairs local os_date = os.date local toint = math.tointeger +local get_path = utils.get_path local get_locale = utils.get_locale +local access_deny = utils.access_deny local role_already_selected = utils.role_already_selected local template_path = 'lualib/admin/html/system/role/' @@ -36,7 +38,7 @@ local function verify_permission (content, db) end local info = user.user_info(db, exists.uid) if not info or info.is_admin ~= 1 then - return false, config.login_render + return false, access_deny(get_path(content)) end info.token = exists.token info.roles = role.role_permissions(db, info.role) diff --git a/lualib/admin/http/system/user.lua b/lualib/admin/http/system/user.lua index 38957aab..cb757813 100644 --- a/lualib/admin/http/system/user.lua +++ b/lualib/admin/http/system/user.lua @@ -20,7 +20,9 @@ local ipairs = ipairs local os_date = os.date local toint = math.tointeger +local get_path = utils.get_path local get_locale = utils.get_locale +local access_deny = utils.access_deny local template_path = 'lualib/admin/html/system/user/' @@ -35,7 +37,7 @@ local function verify_permission (content, db) end local info = user.user_info(db, exists.uid) if not info or info.is_admin ~= 1 then - return false, config.login_render + return false, access_deny(get_path(content)) end info.token = exists.token info.roles = role.role_permissions(db, info.role) diff --git a/lualib/admin/utils/init.lua b/lualib/admin/utils/init.lua index 5c206e8a..3647d9d3 100644 --- a/lualib/admin/utils/init.lua +++ b/lualib/admin/utils/init.lua @@ -12,6 +12,14 @@ local concat = table.concat local utils = {} +-- 页面验证失败 +function utils.access_deny (path) + return template.compile("lualib/admin/html/deny.html") { + path = path or "unknown", + cdn = config.cdn, + } +end + -- 页面重定向 function utils.redirect(path, args) assert(path ~= '' or type(path) ~= 'string' , '试图传递一个非法的path') diff --git a/lualib/admin/view.lua b/lualib/admin/view.lua index b280ff86..4edfbf1e 100644 --- a/lualib/admin/view.lua +++ b/lualib/admin/view.lua @@ -15,21 +15,27 @@ local pcall = pcall local assert = assert local get_path = utils.get_path +local redirect = utils.redirect +local get_locale = utils.get_locale +local access_deny = utils.access_deny -- 用户自定义view页面需要验权 local function verify_permission (content, db) local token = Cookie.getCookie("CFTOKEN") if not token then - return false, config.login_render + return false, redirect(config.login_render) end -- 无效token则需要登录 local info = user_token.token_to_userinfo(db, token) if not info then - return false, config.login_render + return false, redirect(config.login_render) end -- 有效token需要验证访问权限 - if info.is_admin ~= 1 and permission.user_have_menu_permission(db, info.id, get_path(content)) then - return false, config.login_render + if info.is_admin ~= 1 then + local path = get_path(content) + if path ~= config.home and not permission.user_have_menu_permission(db, info.id, path) then + return false, access_deny(path) + end end return true end @@ -43,9 +49,9 @@ function view.use (path, f) local db, app = config.db, config.app assert(db and app, "view.use need db session and http context.") return app:use(path, function (content) - local ok, url = verify_permission(content, db) + local ok, info = verify_permission(content, db) if not ok then - return utils.redirect(url) + return info end if not config.cache then template.cache = {} @@ -87,7 +93,7 @@ function view.home(path, f) end local info = user_token.token_to_userinfo(db, token) if not info then - return utils.redirect(config.login_render) + return redirect(config.login_render) end local ok, res = pcall(f, httpctx:new{content = content}, db) if not ok then @@ -99,7 +105,7 @@ end -- 获取当前用户语言表 function view.get_locale () - return utils.get_locale(Cookie.getCookie("CFLANG")) + return get_locale(Cookie.getCookie("CFLANG")) end -- 获取静态文件前缀 From a099cb0705ca71b02a40b2d65b124acd1f73c44d Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 31 Oct 2020 00:27:50 +0800 Subject: [PATCH 647/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dmysql=E9=A9=B1?= =?UTF-8?q?=E5=8A=A8=E7=9A=84=E5=85=BC=E5=AE=B9=E9=AA=8C=E8=AF=81=E7=AE=97?= =?UTF-8?q?=E6=B3=95=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/mysql.lua | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua index 4e20c9dc..f8b3d322 100644 --- a/lualib/protocol/mysql.lua +++ b/lualib/protocol/mysql.lua @@ -141,7 +141,7 @@ local function sock_read (self, bytes) end -- mysql_native认证 -local function mysq_native_password(password, scramble) +local function mysql_native_password(password, scramble) if not password or password == "" then return "" end @@ -487,7 +487,7 @@ local function mysql_login (self) token = caching_sha2_password(self.password, salt) req = strpack(" Date: Sat, 31 Oct 2020 11:54:12 +0800 Subject: [PATCH 648/956] =?UTF-8?q?=E5=AE=8C=E5=96=84mysql-auth-plugig?= =?UTF-8?q?=E6=94=AF=E6=8C=81(native/sha256/caching=5Fsha256)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/mysql.lua | 54 ++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua index f8b3d322..369a0724 100644 --- a/lualib/protocol/mysql.lua +++ b/lualib/protocol/mysql.lua @@ -142,22 +142,27 @@ end -- mysql_native认证 local function mysql_native_password(password, scramble) - if not password or password == "" then - return "" - end - local stage1 = sha1(password) - local stage2 = sha1(scramble .. sha1(stage1)) - return xor_str(stage2, stage1) + if type(password) ~= 'string' or password == "" then + return "" + end + local stage1 = sha1(password) + local stage2 = sha1(scramble .. sha1(stage1)) + return xor_str(stage2, stage1) +end + +-- mysql_sha256认证 +local function mysql_sha256_password(password, scramble) + if type(password) ~= 'string' or password == "" then + return "" + end + local stage1 = sha2(password) + local stage2 = sha2(sha2(stage1) .. scramble) + return xor_str(stage1, stage2) end -- caching_sha2认证 local function caching_sha2_password(password, scramble) - if not password or password == "" then - return "" - end - local stage1 = sha2(password) - local stage2 = sha2(sha2(stage1) .. scramble) - return xor_str(stage1, stage2) + return mysql_sha256_password(password, scramble) end -- RSA扩展公钥认证 @@ -511,8 +516,8 @@ local function mysql_login (self) return nil, err end status, method = strbyte(packet, 1), strbyte(packet, 2) - else -- specify auth method switch algorithm : caching_sha2_password / mysql_native_password - + elseif status ~= RESP_ERROR then + -- specify auth method switch algorithm : caching_sha2_password / mysql_native_password -- 1. Auth Plugin Need caching_sha2_password if status == 0x01 and method == 0x04 then self.packet_no = self.packet_no + 1 @@ -531,13 +536,22 @@ local function mysql_login (self) -- 2. Auth Plugin Need mysql_native_password if status == 0xFE then - local auth_plugin - auth_plugin, pos = strunpack("z", packet, 2) - if auth_plugin ~= "mysql_native_password" then + local auth_plugin, pos = strunpack("z", packet, 2) + if auth_plugin == "mysql_native_password" then + self.packet_no = self.packet_no + 1 + send_packet(self, mysql_native_password(self.password, strunpack(" Date: Thu, 19 Nov 2020 21:46:51 +0800 Subject: [PATCH 649/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0pbc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lpbc/lpb.c | 1568 +++++++++++++------- luaclib/src/lpbc/lpb.h | 917 ++++++------ lualib/{protobuf.lua => protobuf/init.lua} | 0 lualib/protobuf/protoc.lua | 1116 ++++++++++++++ 4 files changed, 2598 insertions(+), 1003 deletions(-) rename lualib/{protobuf.lua => protobuf/init.lua} (100%) create mode 100644 lualib/protobuf/protoc.lua diff --git a/luaclib/src/lpbc/lpb.c b/luaclib/src/lpbc/lpb.c index dc586c71..ab92c65c 100644 --- a/luaclib/src/lpbc/lpb.c +++ b/luaclib/src/lpbc/lpb.c @@ -1,17 +1,33 @@ -#define PB_STATIC_API +#ifdef _MSC_VER +# define _CRT_SECURE_NO_WARNINGS +# define _CRT_NONSTDC_NO_WARNINGS +# pragma warning(disable: 4244) /* int -> char */ +# pragma warning(disable: 4706) /* = in if condition */ +# pragma warning(disable: 4709) /* comma in array index */ +# pragma warning(disable: 4127) /* const in if condition */ +#endif +#define PB_STATIC_API #include "lpb.h" +PB_NS_BEGIN + + #define LUA_LIB +#include + + +/* Lua util routines */ #define PB_STATE "pb.State" #define PB_BUFFER "pb.Buffer" #define PB_SLICE "pb.Slice" -#define check_buffer(L,idx) ((pb_Buffer*)checkudata(L,idx,PB_BUFFER)) -#define test_buffer(L,idx) ((pb_Buffer*)testudata(L,idx,PB_BUFFER)) -#define check_slice(L,idx) ((pb_SliceExt*)checkudata(L,idx,PB_SLICE)) -#define test_slice(L,idx) ((pb_SliceExt*)testudata(L,idx,PB_SLICE)) +#define check_buffer(L,idx) ((pb_Buffer*)luaL_checkudata(L,idx,PB_BUFFER)) +#define test_buffer(L,idx) ((pb_Buffer*)luaL_testudata(L,idx,PB_BUFFER)) +#define check_slice(L,idx) ((pb_Slice*)luaL_checkudata(L,idx,PB_SLICE)) +#define test_slice(L,idx) ((pb_Slice*)luaL_testudata(L,idx,PB_SLICE)) +#define push_slice(L,s) lua_pushlstring((L), (s).p, pb_len((s))) #define return_self(L) { lua_settop(L, 1); return 1; } #if LUA_VERSION_NUM < 502 @@ -24,14 +40,14 @@ (luaL_getmetatable((L), (name)), lua_setmetatable(L, -2)) static int relindex(int idx, int offset) -{ return idx < 0 && idx > LUA_REGISTRYINDEX ? idx + offset : idx; } +{ return idx < 0 && idx > LUA_REGISTRYINDEX ? idx - offset : idx; } -void lua_rawgetp(lua_State *L, int idx, const void *p) { +static void lua_rawgetp(lua_State *L, int idx, const void *p) { lua_pushlightuserdata(L, (void*)p); lua_rawget(L, relindex(idx, 1)); } -void lua_rawsetp(lua_State *L, int idx, const void *p) { +static void lua_rawsetp(lua_State *L, int idx, const void *p) { lua_pushlightuserdata(L, (void*)p); lua_insert(L, -2); lua_rawset(L, relindex(idx, 1)); @@ -51,10 +67,24 @@ static lua_Number lua_tonumberx(lua_State *L, int idx, int *isnum) { if (isnum) *isnum = (i != 0 || lua_type(L, idx) == LUA_TNUMBER); return i; } + +static void *luaL_testudata(lua_State *L, int idx, const char *type) { + void *p = lua_touserdata(L, idx); + if (p != NULL && lua_getmetatable(L, idx)) { + lua_getfield(L, LUA_REGISTRYINDEX, type); + if (!lua_rawequal(L, -2, -1)) + p = NULL; + lua_pop(L, 2); + return p; + } + return NULL; +} + #endif #ifdef LUAI_BITSINT /* not LuaJIT */ #include + static int luaL_fileresult(lua_State *L, int stat, const char *fname) { int en = errno; if (stat) { lua_pushboolean(L, 1); return 1; } @@ -65,6 +95,7 @@ static int luaL_fileresult(lua_State *L, int stat, const char *fname) { lua_pushinteger(L, en); return 3; } + #endif /* not LuaJIT */ #endif @@ -73,7 +104,7 @@ static int luaL_fileresult(lua_State *L, int stat, const char *fname) { # define lua53_getfield lua_getfield # define lua53_rawgeti lua_rawgeti # define lua53_rawgetp lua_rawgetp -#else +#else /* not Lua 5.3 */ static int lua53_getfield(lua_State *L, int idx, const char *field) { lua_getfield(L, idx, field); return lua_type(L, -1); } static int lua53_rawgeti(lua_State *L, int idx, lua_Integer i) @@ -83,40 +114,108 @@ static int lua53_rawgetp(lua_State *L, int idx, const void *p) #endif -typedef struct pb_SliceExt { - pb_Slice base; - const char *head; -} pb_SliceExt; +/* protobuf global state */ -static int lpb_offset(pb_SliceExt *s) { return (int)(s->base.p-s->head) + 1; } +#define default_state(L) (default_lstate(L)->state) +#define lpb_state(LS) ((LS)->state) +#define lpb_name(LS,s) pb_name(lpb_state(LS), (s), &(LS)->cache) -static pb_SliceExt lpb_initext(pb_Slice s) -{ pb_SliceExt ext; ext.base = s, ext.head = s.p; return ext; } +static const pb_State *global_state = NULL; +static const char state_name[] = PB_STATE; -static void lpb_addlength(lua_State *L, pb_Buffer *b, size_t len) -{ if (pb_addlength(b, len) == 0) luaL_error(L, "encode bytes fail"); } +enum lpb_Int64Mode { LPB_NUMBER, LPB_STRING, LPB_HEXSTRING }; +enum lpb_DefMode { LPB_DEFDEF, LPB_COPYDEF, LPB_METADEF, LPB_NODEF }; -static int typeerror(lua_State *L, int idx, const char *type) { - lua_pushfstring(L, "%s expected, got %s", type, luaL_typename(L, idx)); - return luaL_argerror(L, idx, lua_tostring(L, -1)); +typedef struct lpb_State { + const pb_State *state; + pb_State local; + pb_Cache cache; + pb_Buffer buffer; + int defs_index; + int hooks_index; + unsigned use_hooks : 1; /* lpb_Int64Mode */ + unsigned enum_as_value : 1; + unsigned default_mode : 2; /* lpb_DefMode */ + unsigned int64_mode : 2; /* lpb_Int64Mode */ +} lpb_State; + +static int lpb_reftable(lua_State *L, int ref) { + if (ref != LUA_NOREF) { + lua_rawgeti(L, LUA_REGISTRYINDEX, ref); + return ref; + } else { + lua_newtable(L); + lua_pushvalue(L, -1); + return luaL_ref(L, LUA_REGISTRYINDEX); + } } -static void *testudata(lua_State *L, int idx, const char *type) { - void *p = lua_touserdata(L, idx); - if (p != NULL && lua_getmetatable(L, idx)) { - lua_getfield(L, LUA_REGISTRYINDEX, type); - if (!lua_rawequal(L, -2, -1)) - p = NULL; - lua_pop(L, 2); - return p; +static void lpb_pushdeftable(lua_State *L, lpb_State *LS) +{ LS->defs_index = lpb_reftable(L, LS->defs_index); } + +static void lpb_pushhooktable(lua_State *L, lpb_State *LS) +{ LS->hooks_index = lpb_reftable(L, LS->hooks_index); } + +static int Lpb_delete(lua_State *L) { + lpb_State *LS = (lpb_State*)luaL_testudata(L, 1, PB_STATE); + if (LS != NULL) { + const pb_State *GS = global_state; + pb_free(&LS->local); + if (&LS->local == GS) + global_state = NULL; + LS->state = NULL; + pb_resetbuffer(&LS->buffer); + luaL_unref(L, LUA_REGISTRYINDEX, LS->defs_index); + luaL_unref(L, LUA_REGISTRYINDEX, LS->hooks_index); } - return NULL; + return 0; } -static void *checkudata(lua_State *L, int idx, const char *type) { - void *p = testudata(L, idx, type); - if (p == NULL) typeerror(L, idx, type); - return p; +static lpb_State *default_lstate(lua_State *L) { + lpb_State *LS; + if (lua53_rawgetp(L, LUA_REGISTRYINDEX, state_name) == LUA_TUSERDATA) { + LS = (lpb_State*)lua_touserdata(L, -1); + lua_pop(L, 1); + } else { + lua_pop(L, 1); + LS = (lpb_State*)lua_newuserdata(L, sizeof(lpb_State)); + memset(LS, 0, sizeof(lpb_State)); + LS->defs_index = LUA_NOREF; + LS->hooks_index = LUA_NOREF; + LS->state = &LS->local; + pb_init(&LS->local); + pb_initbuffer(&LS->buffer); + luaL_setmetatable(L, PB_STATE); + lua_rawsetp(L, LUA_REGISTRYINDEX, state_name); + } + return LS; +} + +static int Lpb_state(lua_State *L) { + int top = lua_gettop(L); + default_lstate(L); + lua_rawgetp(L, LUA_REGISTRYINDEX, state_name); + if (top != 0) { + if (lua_isnil(L, 1)) + lua_pushnil(L); + else { + luaL_checkudata(L, 1, PB_STATE); + lua_pushvalue(L, 1); + } + lua_rawsetp(L, LUA_REGISTRYINDEX, state_name); + } + return 1; +} + + +/* protobuf util routines */ + +static void lpb_addlength(lua_State *L, pb_Buffer *b, size_t len) +{ if (pb_addlength(b, len) == 0) luaL_error(L, "encode bytes fail"); } + +static int typeerror(lua_State *L, int idx, const char *type) { + lua_pushfstring(L, "%s expected, got %s", type, luaL_typename(L, idx)); + return luaL_argerror(L, idx, lua_tostring(L, -1)); } static lua_Integer posrelat(lua_Integer pos, size_t len) { @@ -125,39 +224,40 @@ static lua_Integer posrelat(lua_Integer pos, size_t len) { else return (lua_Integer)len + pos + 1; } -static lua_Integer rangerelat(lua_State *L, int idx, lua_Integer *i, lua_Integer *j, size_t len) { - *i = posrelat(luaL_optinteger(L, idx, 1), len); - *j = posrelat(luaL_optinteger(L, idx+1, len), len); - if (*i < 1) *i = 1; - if (*j > (lua_Integer)len) *j = len; - return *i <= *j ? *j - *i + 1 : 0; +static lua_Integer rangerelat(lua_State *L, int idx, lua_Integer r[2], size_t len) { + r[0] = posrelat(luaL_optinteger(L, idx, 1), len); + r[1] = posrelat(luaL_optinteger(L, idx+1, len), len); + if (r[0] < 1) r[0] = 1; + if (r[1] > (lua_Integer)len) r[1] = len; + return r[0] <= r[1] ? r[1] - r[0] + 1 : 0; } -static int argerror(lua_State *L, int idx, const char *fmt, ...) { - va_list l; - va_start(l, fmt); - lua_pushvfstring(L, fmt, l); - va_end(l); - return luaL_argerror(L, idx, lua_tostring(L, -1)); +static int argcheck(lua_State *L, int cond, int idx, const char *fmt, ...) { + if (!cond) { + va_list l; + va_start(l, fmt); + lua_pushvfstring(L, fmt, l); + va_end(l); + return luaL_argerror(L, idx, lua_tostring(L, -1)); + } + return 1; } static pb_Slice lpb_toslice(lua_State *L, int idx) { int type = lua_type(L, idx); - pb_Slice ret = { NULL, NULL }; if (type == LUA_TSTRING) { size_t len; const char *s = lua_tolstring(L, idx, &len); - ret = pb_lslice(s, len); - } - else if (type == LUA_TUSERDATA) { + return pb_lslice(s, len); + } else if (type == LUA_TUSERDATA) { pb_Buffer *buffer; - pb_SliceExt *s; + pb_Slice *s; if ((buffer = test_buffer(L, idx)) != NULL) - ret = pb_result(buffer); + return pb_result(buffer); else if ((s = test_slice(L, idx)) != NULL) - ret = s->base; + return *s; } - return ret; + return pb_slice(NULL); } static pb_Slice lpb_checkslice(lua_State *L, int idx) { @@ -166,135 +266,232 @@ static pb_Slice lpb_checkslice(lua_State *L, int idx) { return ret; } -static void lpb_readbytes(lua_State *L, pb_SliceExt *s, pb_SliceExt *pv) { +static void lpb_readbytes(lua_State *L, pb_Slice *s, pb_Slice *pv) { uint64_t len = 0; - if (pb_readvarint64(&s->base, &len) == 0 || len > PB_MAX_SIZET) + if (pb_readvarint64(s, &len) == 0 || len > PB_MAX_SIZET) luaL_error(L, "invalid bytes length: %d (at offset %d)", - (int)len, lpb_offset(s)); - if (pb_readslice(&s->base, (size_t)len, &pv->base) == 0 && len != 0) + (int)len, pb_pos(*s)+1); + if (pb_readslice(s, (size_t)len, pv) == 0 && len != 0) luaL_error(L, "un-finished bytes (len %d at offset %d)", - (int)len, lpb_offset(s)); - pv->head = pv->base.p; + (int)len, pb_pos(*s)+1); +} + +static int lpb_hexchar(char ch) { + switch (ch) { + case '0': return 0; + case '1': return 1; case '2': return 2; case '3': return 3; + case '4': return 4; case '5': return 5; case '6': return 6; + case '7': return 7; case '8': return 8; case '9': return 9; + case 'a': case 'A': return 10; case 'b': case 'B': return 11; + case 'c': case 'C': return 12; case 'd': case 'D': return 13; + case 'e': case 'E': return 14; case 'f': case 'F': return 15; + } + return -1; +} + +static uint64_t lpb_tointegerx(lua_State *L, int idx, int *isint) { + int neg = 0; + const char *s, *os; +#if LUA_VERSION_NUM >= 503 + uint64_t v = (uint64_t)lua_tointegerx(L, idx, isint); + if (*isint) return v; +#else + uint64_t v = 0; + lua_Number nv = lua_tonumberx(L, idx, isint); + if (*isint) { + if (nv < (lua_Number)INT64_MIN || nv > (lua_Number)INT64_MAX) + luaL_error(L, "number has no integer representation"); + return (uint64_t)(int64_t)nv; + } +#endif + if ((os = s = lua_tostring(L, idx)) == NULL) return 0; + while (*s == '#' || *s == '+' || *s == '-') + neg = (*s == '-') ^ neg, ++s; + if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { + for (s += 2; *s != '\0'; ++s) { + int n = lpb_hexchar(*s); + if (n < 0) break; + v = v << 4 | n; + } + } else { + for (; *s != '\0'; ++s) { + int n = lpb_hexchar(*s); + if (n < 0 || n > 10) break; + v = v * 10 + n; + } + } + if (*s != '\0') luaL_error(L, "integer format error: '%s'", os); + *isint = 1; + return neg ? ~v + 1 : v; +} + +static uint64_t lpb_checkinteger(lua_State *L, int idx) { + int isint; + uint64_t v = lpb_tointegerx(L, idx, &isint); + if (!isint) typeerror(L, idx, "number/string"); + return v; +} + +static void lpb_pushinteger(lua_State *L, int64_t n, int mode) { + if (mode != LPB_NUMBER && (n < INT_MIN || n > UINT_MAX)) { + char buff[32], *p = buff + sizeof(buff) - 1; + int neg = n < 0; + uint64_t un = neg ? ~(uint64_t)n + 1 : (uint64_t)n; + if (mode == LPB_STRING) { + for (*p = '\0'; un > 0; un /= 10) + *--p = "0123456789"[un % 10]; + } else if (mode == LPB_HEXSTRING) { + for (*p = '\0'; un > 0; un >>= 4) + *--p = "0123456789ABCDEF"[un & 0xF]; + *--p = 'x', *--p = '0'; + } + if (neg) *--p = '-'; + *--p = '#'; + lua_pushstring(L, p); + } else if (LUA_VERSION_NUM >= 503 && sizeof(lua_Integer) >= 8) + lua_pushinteger(L, (lua_Integer)n); + else + lua_pushnumber(L, (lua_Number)n); } typedef union lpb_Value { - pb_SliceExt s[1]; - uint32_t u32; - uint64_t u64; + pb_Slice s[1]; + uint32_t u32; + uint64_t u64; lua_Integer lint; - lua_Number lnum; + lua_Number lnum; } lpb_Value; -static int lpb_addtype(lua_State *L, pb_Buffer *b, int idx, int type) { +static int lpb_addtype(lua_State *L, pb_Buffer *b, int idx, int type, size_t *plen) { int ret = 0, expected = LUA_TNUMBER; lpb_Value v; + size_t len = 0; switch (type) { case PB_Tbool: - pb_addvarint32(b, lua_toboolean(L, idx)); + len = pb_addvarint32(b, ret = lua_toboolean(L, idx)); + if (ret) len = 0; ret = 1; break; case PB_Tdouble: v.lnum = lua_tonumberx(L, idx, &ret); - if (ret) pb_addfixed64(b, pb_encode_double((double)v.lnum)); + if (ret) len = pb_addfixed64(b, pb_encode_double((double)v.lnum)); + if (v.lnum != 0.0) len = 0; break; case PB_Tfloat: v.lnum = lua_tonumberx(L, idx, &ret); - if (ret) pb_addfixed32(b, pb_encode_float((float)v.lnum)); + if (ret) len = pb_addfixed32(b, pb_encode_float((float)v.lnum)); + if (v.lnum != 0.0) len = 0; break; case PB_Tfixed32: - v.lint = lua_tointegerx(L, idx, &ret); - if (ret) pb_addfixed32(b, (uint32_t)v.lint); + v.u64 = lpb_tointegerx(L, idx, &ret); + if (ret) len = pb_addfixed32(b, v.u32); + if (v.u64 != 0) len = 0; break; case PB_Tsfixed32: - v.lint = lua_tointegerx(L, idx, &ret); - if (ret) pb_addfixed32(b, (int32_t)v.lint); + v.u64 = lpb_tointegerx(L, idx, &ret); + if (ret) len = pb_addfixed32(b, v.u32); + if (v.u64 != 0) len = 0; break; - case PB_Tint32: case PB_Tuint32: - v.lint = lua_tointegerx(L, idx, &ret); - if (ret) pb_addvarint32(b, (uint32_t)lua_tointeger(L, idx)); + case PB_Tint32: + v.u64 = lpb_tointegerx(L, idx, &ret); + if (ret) len = pb_addvarint64(b, pb_expandsig((uint32_t)v.u64)); + if (v.u64 != 0) len = 0; + break; + case PB_Tuint32: + v.u64 = lpb_tointegerx(L, idx, &ret); + if (ret) len = pb_addvarint32(b, v.u32); + if (v.u64 != 0) len = 0; break; case PB_Tsint32: - v.lint = lua_tointegerx(L, idx, &ret); - if (ret) pb_addvarint32(b, pb_encode_sint32((int32_t)v.lint)); + v.u64 = lpb_tointegerx(L, idx, &ret); + if (ret) len = pb_addvarint32(b, pb_encode_sint32(v.u32)); + if (v.u64 != 0) len = 0; break; case PB_Tfixed64: - v.lint = lua_tointegerx(L, idx, &ret); - if (ret) pb_addfixed64(b, (uint64_t)v.lint); + v.u64 = lpb_tointegerx(L, idx, &ret); + if (ret) len = pb_addfixed64(b, v.u64); + if (v.u64 != 0) len = 0; break; case PB_Tsfixed64: - v.lint = lua_tointegerx(L, idx, &ret); - if (ret) pb_addfixed64(b, (int64_t)v.lint); + v.u64 = lpb_tointegerx(L, idx, &ret); + if (ret) len = pb_addfixed64(b, v.u64); + if (v.u64 != 0) len = 0; break; case PB_Tint64: case PB_Tuint64: - v.lint = lua_tointegerx(L, idx, &ret); - if (ret) pb_addvarint64(b, (uint64_t)v.lint); + v.u64 = lpb_tointegerx(L, idx, &ret); + if (ret) len = pb_addvarint64(b, v.u64); + if (v.u64 != 0) len = 0; break; case PB_Tsint64: - v.lint = lua_tointegerx(L, idx, &ret); - if (ret) pb_addvarint64(b, pb_encode_sint64((int64_t)v.lint)); + v.u64 = lpb_tointegerx(L, idx, &ret); + if (ret) len = pb_addvarint64(b, pb_encode_sint64(v.u64)); + if (v.u64 != 0) len = 0; break; case PB_Tbytes: case PB_Tstring: - v.s->base = lpb_toslice(L, idx); - if ((ret = v.s->base.p != NULL)) pb_addbytes(b, v.s->base); + *v.s = lpb_toslice(L, idx); + if ((ret = (v.s->p != NULL))) len = pb_addbytes(b, *v.s); + if (pb_len(*v.s) != 0) len = 0; expected = LUA_TSTRING; break; - /* NOT REACHED */ - /* default: - * argerror(L, idx, "unknown type %s", pb_typename(type, "")); */ + default: + lua_pushfstring(L, "unknown type %s", pb_typename(type, "")); + if (idx > 0) argcheck(L, 0, idx, lua_tostring(L, -1)); + lua_error(L); } + if (plen) *plen = len; return ret ? 0 : expected; } -static void lpb_readtype(lua_State *L, int type, pb_SliceExt *s) { +static void lpb_readtype(lua_State *L, lpb_State *LS, int type, pb_Slice *s) { lpb_Value v; switch (type) { +#define pushinteger(n) lpb_pushinteger((L), (n), LS->int64_mode) case PB_Tbool: case PB_Tenum: case PB_Tint32: case PB_Tuint32: case PB_Tsint32: case PB_Tint64: case PB_Tuint64: case PB_Tsint64: - if (pb_readvarint64(&s->base, &v.u64) == 0) - luaL_error(L, "invalid varint value at offset %d", lpb_offset(s)); + if (pb_readvarint64(s, &v.u64) == 0) + luaL_error(L, "invalid varint value at offset %d", pb_pos(*s)+1); switch (type) { case PB_Tbool: lua_pushboolean(L, v.u64 != 0); break; - /*case PB_Tenum: lua_pushinteger(L, v.u64); break; [> NOT REACHED <]*/ - case PB_Tint32: lua_pushinteger(L, (int32_t)v.u64); break; - case PB_Tuint32: lua_pushinteger(L, (uint32_t)v.u64); break; - case PB_Tsint32: lua_pushinteger(L, pb_decode_sint32((uint32_t)v.u64)); break; - case PB_Tint64: lua_pushinteger(L, (int64_t)v.u64); break; - case PB_Tuint64: lua_pushinteger(L, (uint64_t)v.u64); break; - case PB_Tsint64: lua_pushinteger(L, pb_decode_sint64(v.u64)); break; + /*case PB_Tenum: pushinteger(v.u64); break; [> NOT REACHED <]*/ + case PB_Tint32: pushinteger((int32_t)v.u64); break; + case PB_Tuint32: pushinteger((uint32_t)v.u64); break; + case PB_Tsint32: pushinteger(pb_decode_sint32((uint32_t)v.u64)); break; + case PB_Tint64: pushinteger((int64_t)v.u64); break; + case PB_Tuint64: pushinteger((uint64_t)v.u64); break; + case PB_Tsint64: pushinteger(pb_decode_sint64(v.u64)); break; } break; case PB_Tfloat: case PB_Tfixed32: case PB_Tsfixed32: - if (pb_readfixed32(&s->base, &v.u32) == 0) - luaL_error(L, "invalid fixed32 value at offset %d", lpb_offset(s)); + if (pb_readfixed32(s, &v.u32) == 0) + luaL_error(L, "invalid fixed32 value at offset %d", pb_pos(*s)+1); switch (type) { case PB_Tfloat: lua_pushnumber(L, pb_decode_float(v.u32)); break; - case PB_Tfixed32: lua_pushinteger(L, v.u32); break; - case PB_Tsfixed32: lua_pushinteger(L, (int32_t)v.u32); break; + case PB_Tfixed32: pushinteger(v.u32); break; + case PB_Tsfixed32: pushinteger((int32_t)v.u32); break; } break; case PB_Tdouble: case PB_Tfixed64: case PB_Tsfixed64: - if (pb_readfixed64(&s->base, &v.u64) == 0) - luaL_error(L, "invalid fixed64 value at offset %d", lpb_offset(s)); + if (pb_readfixed64(s, &v.u64) == 0) + luaL_error(L, "invalid fixed64 value at offset %d", pb_pos(*s)+1); switch (type) { case PB_Tdouble: lua_pushnumber(L, pb_decode_double(v.u64)); break; - case PB_Tfixed64: lua_pushinteger(L, v.u64); break; - case PB_Tsfixed64: lua_pushinteger(L, (int64_t)v.u64); break; + case PB_Tfixed64: pushinteger(v.u64); break; + case PB_Tsfixed64: pushinteger((int64_t)v.u64); break; } break; case PB_Tbytes: case PB_Tstring: case PB_Tmessage: lpb_readbytes(L, s, v.s); - lua_pushlstring(L, v.s->base.p, pb_len(v.s->base)); + push_slice(L, *v.s); break; - /* NOT REACHED */ - /*default: - * luaL_error(L, "unknown type %s", pb_typename(type, NULL));*/ + default: + luaL_error(L, "unknown type %s (%d)", pb_typename(type, NULL), type); } } @@ -369,7 +566,7 @@ static int Lio_dump(lua_State *L) { return res; } -LUALIB_API int luaopen_pb_io(lua_State *L) { +LUALIB_API int luaopen_lprotobuf_io(lua_State *L) { luaL_Reg libs[] = { #define ENTRY(name) { #name, Lio_##name } ENTRY(read), @@ -386,56 +583,65 @@ LUALIB_API int luaopen_pb_io(lua_State *L) { /* protobuf integer conversion */ static int Lconv_encode_int32(lua_State *L) { - lua_pushinteger(L, pb_expandsig((int32_t)luaL_checkinteger(L, 1))); + unsigned mode = default_lstate(L)->int64_mode; + uint64_t v = pb_expandsig((int32_t)lpb_checkinteger(L, 1)); + lpb_pushinteger(L, v, mode); return 1; } static int Lconv_encode_uint32(lua_State *L) { - lua_pushinteger(L, (uint32_t)luaL_checkinteger(L, 1)); + unsigned mode = default_lstate(L)->int64_mode; + lpb_pushinteger(L, (uint32_t)lpb_checkinteger(L, 1), mode); return 1; } static int Lconv_encode_sint32(lua_State *L) { - lua_pushinteger(L, pb_encode_sint32((int32_t)luaL_checkinteger(L, 1))); + unsigned mode = default_lstate(L)->int64_mode; + lpb_pushinteger(L, pb_encode_sint32((int32_t)lpb_checkinteger(L, 1)), mode); return 1; } static int Lconv_decode_sint32(lua_State *L) { - lua_pushinteger(L, pb_decode_sint32((uint32_t)luaL_checkinteger(L, 1))); + unsigned mode = default_lstate(L)->int64_mode; + lpb_pushinteger(L, pb_decode_sint32((uint32_t)lpb_checkinteger(L, 1)), mode); return 1; } static int Lconv_encode_sint64(lua_State *L) { - lua_pushinteger(L, pb_encode_sint64(luaL_checkinteger(L, 1))); + unsigned mode = default_lstate(L)->int64_mode; + lpb_pushinteger(L, pb_encode_sint64(lpb_checkinteger(L, 1)), mode); return 1; } static int Lconv_decode_sint64(lua_State *L) { - lua_pushinteger(L, pb_decode_sint64(luaL_checkinteger(L, 1))); + unsigned mode = default_lstate(L)->int64_mode; + lpb_pushinteger(L, pb_decode_sint64(lpb_checkinteger(L, 1)), mode); return 1; } static int Lconv_encode_float(lua_State *L) { - lua_pushinteger(L, pb_encode_float((float)luaL_checknumber(L, 1))); + unsigned mode = default_lstate(L)->int64_mode; + lpb_pushinteger(L, pb_encode_float((float)luaL_checknumber(L, 1)), mode); return 1; } static int Lconv_decode_float(lua_State *L) { - lua_pushnumber(L, pb_decode_float((uint32_t)luaL_checkinteger(L, 1))); + lua_pushnumber(L, pb_decode_float((uint32_t)lpb_checkinteger(L, 1))); return 1; } static int Lconv_encode_double(lua_State *L) { - lua_pushinteger(L, pb_encode_double(luaL_checknumber(L, 1))); + unsigned mode = default_lstate(L)->int64_mode; + lpb_pushinteger(L, pb_encode_double(luaL_checknumber(L, 1)), mode); return 1; } static int Lconv_decode_double(lua_State *L) { - lua_pushnumber(L, pb_decode_double(luaL_checkinteger(L, 1))); + lua_pushnumber(L, pb_decode_double(lpb_checkinteger(L, 1))); return 1; } -LUALIB_API int luaopen_pb_conv(lua_State *L) { +LUALIB_API int luaopen_lprotobuf_conv(lua_State *L) { luaL_Reg libs[] = { { "decode_uint32", Lconv_encode_uint32 }, { "decode_int32", Lconv_encode_int32 }, @@ -460,21 +666,11 @@ LUALIB_API int luaopen_pb_conv(lua_State *L) { /* protobuf encode routine */ -static int lpb_typefmt(const char *fmt) { - switch (*fmt) { - case 'b': return PB_Tbool; - case 'f': return PB_Tfloat; - case 'F': return PB_Tdouble; - case 'i': return PB_Tint32; - case 'j': return PB_Tsint32; - case 'u': return PB_Tuint32; - case 'x': return PB_Tfixed32; - case 'y': return PB_Tsfixed32; - case 'I': return PB_Tint64; - case 'J': return PB_Tsint64; - case 'U': return PB_Tuint64; - case 'X': return PB_Tfixed64; - case 'Y': return PB_Tsfixed64; +static int lpb_typefmt(int fmt) { + switch (fmt) { +#define X(name,type,fmt) case fmt: return PB_T##name; + PB_TYPES(X) +#undef X } return -1; } @@ -483,15 +679,15 @@ static int lpb_packfmt(lua_State *L, int idx, pb_Buffer *b, const char **pfmt, i const char *fmt = *pfmt; int type, ltype; size_t len; - luaL_argcheck(L, 1, level <= 100, "format level overflow"); + argcheck(L, level <= 100, 1, "format level overflow"); for (; *fmt != '\0'; ++fmt) { switch (*fmt) { - case 'v': pb_addvarint64(b, (uint64_t)luaL_checkinteger(L, idx++)); break; - case 'd': pb_addfixed32(b, (uint32_t)luaL_checkinteger(L, idx++)); break; - case 'q': pb_addfixed64(b, (uint64_t)luaL_checkinteger(L, idx++)); break; + case 'v': pb_addvarint64(b, (uint64_t)lpb_checkinteger(L, idx++)); break; + case 'd': pb_addfixed32(b, (uint32_t)lpb_checkinteger(L, idx++)); break; + case 'q': pb_addfixed64(b, (uint64_t)lpb_checkinteger(L, idx++)); break; case 'c': pb_addslice(b, lpb_checkslice(L, idx++)); break; case 's': pb_addbytes(b, lpb_checkslice(L, idx++)); break; - case '#': lpb_addlength(L, b, (size_t)luaL_checkinteger(L, idx++)); break; + case '#': lpb_addlength(L, b, (size_t)lpb_checkinteger(L, idx++)); break; case '(': len = pb_bufflen(b); ++fmt; @@ -504,16 +700,16 @@ static int lpb_packfmt(lua_State *L, int idx, pb_Buffer *b, const char **pfmt, i return idx; case '\0': default: - if ((type = lpb_typefmt(fmt)) < 0) - argerror(L, 1, "invalid formater: '%c'", *fmt); - if ((ltype = lpb_addtype(L, b, idx, type)) != 0) - argerror(L, idx, "%s expected for type '%s', got %s", - lua_typename(L, ltype), pb_typename(type, ""), - luaL_typename(L, idx)); + argcheck(L, (type = lpb_typefmt(*fmt)) >= 0, + 1, "invalid formater: '%c'", *fmt); + ltype = lpb_addtype(L, b, idx, type, NULL); + argcheck(L, ltype == 0, idx, "%s expected for type '%s', got %s", + lua_typename(L, ltype), pb_typename(type, ""), + luaL_typename(L, idx)); ++idx; } } - if (level != 0) luaL_argerror(L, 1, "unmatch '(' in format"); + if (level != 0) luaL_argerror(L, 2, "unmatch '(' in format"); *pfmt = fmt; return idx; } @@ -522,26 +718,52 @@ static int Lpb_tohex(lua_State *L) { pb_Slice s = lpb_checkslice(L, 1); const char *hexa = "0123456789ABCDEF"; char hex[4] = "XX "; - lua_Integer i = 1, j = -1; + lua_Integer r[2] = { 1, -1 }; luaL_Buffer lb; - rangerelat(L, 2, &i, &j, pb_len(s)); + rangerelat(L, 2, r, pb_len(s)); luaL_buffinit(L, &lb); - for (; i <= j; ++i) { - unsigned int ch = s.p[i-1]; + for (; r[0] <= r[1]; ++r[0]) { + unsigned int ch = s.p[r[0]-1]; hex[0] = hexa[(ch>>4)&0xF]; hex[1] = hexa[(ch )&0xF]; - if (i == j) hex[2] = '\0'; + if (r[0] == r[1]) hex[2] = '\0'; luaL_addstring(&lb, hex); } luaL_pushresult(&lb); return 1; } +static int Lpb_fromhex(lua_State *L) { + pb_Slice s = lpb_checkslice(L, 1); + lua_Integer r[2] = { 1, -1 }; + luaL_Buffer lb; + int curr = 0, idx = 0, num; + rangerelat(L, 2, r, pb_len(s)); + luaL_buffinit(L, &lb); + for (; r[0] <= r[1]; ++r[0]) { + switch (num = s.p[r[0]-1]) { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': num -= '0'; break; + case 'A': case 'a': num = 10; break; + case 'B': case 'b': num = 11; break; + case 'C': case 'c': num = 12; break; + case 'D': case 'd': num = 13; break; + case 'E': case 'e': num = 14; break; + case 'F': case 'f': num = 15; break; + default: continue; + } + curr = curr<<4 | num; + if (++idx % 2 == 0) luaL_addchar(&lb, curr), curr = 0; + } + luaL_pushresult(&lb); + return 1; +} + static int Lpb_result(lua_State *L) { pb_Slice s = lpb_checkslice(L, 1); - lua_Integer i = 1, j = -1; - lua_Integer range = rangerelat(L, 2, &i, &j, pb_len(s)); - lua_pushlstring(L, s.p+i-1, (size_t)range); + lua_Integer r[2] = {1, -1}, range = rangerelat(L, 2, r, pb_len(s)); + lua_pushlstring(L, s.p+r[0]-1, (size_t)range); return 1; } @@ -555,6 +777,12 @@ static int Lbuf_new(lua_State *L) { return 1; } +static int Lbuf_delete(lua_State *L) { + pb_Buffer *buf = test_buffer(L, 1); + if (buf) pb_resetbuffer(buf); + return 0; +} + static int Lbuf_libcall(lua_State *L) { int i, top = lua_gettop(L); pb_Buffer *buf = (pb_Buffer*)lua_newuserdata(L, sizeof(pb_Buffer)); @@ -574,7 +802,7 @@ static int Lbuf_tostring(lua_State *L) { static int Lbuf_reset(lua_State *L) { pb_Buffer *buf = check_buffer(L, 1); int i, top = lua_gettop(L); - pb_resetbuffer(buf); + pb_bufflen(buf) = 0; for (i = 2; i <= top; ++i) pb_addslice(buf, lpb_checkslice(L, i)); return_self(L); @@ -596,19 +824,20 @@ static int Lbuf_pack(lua_State *L) { lua_settop(L, 1); else { pb_Slice ret = pb_result(pb); - lua_pushlstring(L, ret.p, pb_len(ret)); + push_slice(L, ret); pb_resetbuffer(pb); } return 1; } -LUALIB_API int luaopen_pb_buffer(lua_State *L) { +LUALIB_API int luaopen_lprotobuf_buffer(lua_State *L) { luaL_Reg libs[] = { { "__tostring", Lbuf_tostring }, { "__len", Lbuf_len }, - { "__gc", Lbuf_reset }, - { "delete", Lbuf_reset }, + { "__gc", Lbuf_delete }, + { "delete", Lbuf_delete }, { "tohex", Lpb_tohex }, + { "fromhex", Lpb_fromhex }, { "result", Lpb_result }, #define ENTRY(name) { #name, Lbuf_##name } ENTRY(new), @@ -632,45 +861,45 @@ LUALIB_API int luaopen_pb_buffer(lua_State *L) { /* protobuf decode routine */ -#define LPB_INITSTACKLEN 16 +#define LPB_INITSTACKLEN 2 typedef struct lpb_Slice { - pb_SliceExt curr; - pb_SliceExt *buff; + pb_Slice curr; + pb_Slice *buff; size_t used; size_t size; - pb_SliceExt init_buff[LPB_INITSTACKLEN]; + pb_Slice init_buff[LPB_INITSTACKLEN]; } lpb_Slice; -static void lpb_resetslice(lua_State *L, lpb_Slice *s) { - if (s->buff != s->init_buff) - xfree(s->buff); - memset(s, 0, sizeof(lpb_Slice)); - s->buff = s->init_buff; - s->size = LPB_INITSTACKLEN; +static void lpb_resetslice(lua_State *L, lpb_Slice *s, size_t size) { + if (size == sizeof(lpb_Slice)) { + if (s->buff != s->init_buff) free(s->buff); + memset(s, 0, sizeof(lpb_Slice)); + s->buff = s->init_buff; + s->size = LPB_INITSTACKLEN; + } lua_pushnil(L); lua_rawsetp(L, LUA_REGISTRYINDEX, s); } -static pb_SliceExt lpb_checkview(lua_State *L, int idx, pb_SliceExt *ps) { +static pb_Slice lpb_checkview(lua_State *L, int idx, pb_Slice *ps) { pb_Slice src = lpb_checkslice(L, idx); - lua_Integer i = 1, j = -1; - lua_Integer range = rangerelat(L, idx+1, &i, &j, pb_len(src)); - pb_SliceExt ret; - if (ps) ps->base = src, ps->head = src.p; - ret.base.p = src.p + i - 1; - ret.base.end = ret.base.p + range; - ret.head = src.p; + lua_Integer r[2] = {1, -1}, range = rangerelat(L, idx+1, r, pb_len(src)); + pb_Slice ret; + if (ps) *ps = src, ps->start = src.p; + ret.p = src.p + r[0] - 1; + ret.end = ret.p + range; + ret.start = src.p; return ret; } -static void lpb_enterview(lua_State *L, lpb_Slice *s, pb_SliceExt view) { +static void lpb_enterview(lua_State *L, lpb_Slice *s, pb_Slice view) { if (s->used >= s->size) { size_t newsize = s->size * 2; - pb_SliceExt *oldp = s->buff != s->init_buff ? s->buff : NULL; - pb_SliceExt *newp = (pb_SliceExt*)xrealloc(oldp, newsize*sizeof(pb_SliceExt)); + pb_Slice *oldp = s->buff != s->init_buff ? s->buff : NULL; + pb_Slice *newp = (pb_Slice*)realloc(oldp, newsize*sizeof(pb_Slice)); if (newp == NULL) { luaL_error(L, "out of memory"); return; } - if (oldp == NULL) memcpy(newp, s->buff, s->used*sizeof(pb_SliceExt)); + if (oldp == NULL) memcpy(newp, s->buff, s->used*sizeof(pb_Slice)); s->buff = newp; s->size = newsize; } @@ -678,49 +907,51 @@ static void lpb_enterview(lua_State *L, lpb_Slice *s, pb_SliceExt view) { s->curr = view; } -static void lpb_initslice(lua_State *L, int idx, lpb_Slice *s) { - memset(s, 0, sizeof(lpb_Slice)); - s->buff = s->init_buff; - s->size = LPB_INITSTACKLEN; +static void lpb_initslice(lua_State *L, int idx, lpb_Slice *s, size_t size) { + if (size == sizeof(lpb_Slice)) { + memset(s, 0, sizeof(lpb_Slice)); + s->buff = s->init_buff; + s->size = LPB_INITSTACKLEN; + } if (!lua_isnoneornil(L, idx)) { - pb_SliceExt base, view = lpb_checkview(L, idx, &base); + pb_Slice base, view = lpb_checkview(L, idx, &base); s->curr = base; - lpb_enterview(L, s, view); + if (size == sizeof(lpb_Slice)) lpb_enterview(L, s, view); lua_pushvalue(L, idx); lua_rawsetp(L, LUA_REGISTRYINDEX, s); } } -static int lpb_unpackscalar(lua_State *L, int *pidx, int top, int fmt, pb_SliceExt *s) { - lua_Integer i; +static int lpb_unpackscalar(lua_State *L, int *pidx, int top, int fmt, pb_Slice *s) { + unsigned mode = default_lstate(L)->int64_mode; lpb_Value v; switch (fmt) { case 'v': - if (pb_readvarint64(&s->base, &v.u64) == 0) - luaL_error(L, "invalid varint value at offset %d", lpb_offset(s)); - lua_pushinteger(L, v.u64); + if (pb_readvarint64(s, &v.u64) == 0) + luaL_error(L, "invalid varint value at offset %d", pb_pos(*s)+1); + lpb_pushinteger(L, v.u64, mode); break; case 'd': - if (pb_readfixed32(&s->base, &v.u32) == 0) - luaL_error(L, "invalid fixed32 value at offset %d", lpb_offset(s)); - lua_pushinteger(L, v.u32); + if (pb_readfixed32(s, &v.u32) == 0) + luaL_error(L, "invalid fixed32 value at offset %d", pb_pos(*s)+1); + lpb_pushinteger(L, v.u32, mode); break; case 'q': - if (pb_readfixed64(&s->base, &v.u64) == 0) - luaL_error(L, "invalid fixed64 value at offset %d", lpb_offset(s)); - lua_pushinteger(L, v.u64); + if (pb_readfixed64(s, &v.u64) == 0) + luaL_error(L, "invalid fixed64 value at offset %d", pb_pos(*s)+1); + lpb_pushinteger(L, v.u64, mode); break; case 's': - if (pb_readbytes(&s->base, &v.s->base) == 0) - luaL_error(L, "invalid bytes value at offset %d", lpb_offset(s)); - lua_pushlstring(L, v.s->base.p, pb_len(v.s->base)); + if (pb_readbytes(s, v.s) == 0) + luaL_error(L, "invalid bytes value at offset %d", pb_pos(*s)+1); + push_slice(L, *v.s); break; case 'c': - luaL_argcheck(L, 1, *pidx <= top, "format argument exceed"); - i = luaL_checkinteger(L, *pidx++); - if (pb_readslice(&s->base, (size_t)i, &v.s->base) == 0) - luaL_error(L, "invalid sub string at offset %d", lpb_offset(s)); - lua_pushlstring(L, v.s->base.p, pb_len(v.s->base)); + argcheck(L, *pidx <= top, 1, "format argument exceed"); + v.lint = luaL_checkinteger(L, *pidx++); + if (pb_readslice(s, (size_t)v.lint, v.s) == 0) + luaL_error(L, "invalid sub string at offset %d", pb_pos(*s)+1); + push_slice(L, *v.s); break; default: return 0; @@ -728,24 +959,24 @@ static int lpb_unpackscalar(lua_State *L, int *pidx, int top, int fmt, pb_SliceE return 1; } -static int lpb_unpackloc(lua_State *L, int *pidx, int top, int fmt, pb_SliceExt *s, int *prets) { +static int lpb_unpackloc(lua_State *L, int *pidx, int top, int fmt, pb_Slice *s, int *prets) { lua_Integer li; - size_t len = s->base.end - s->head; + size_t len = s->end - s->start; switch (fmt) { case '@': - lua_pushinteger(L, lpb_offset(s)); + lua_pushinteger(L, pb_pos(*s)+1); ++*prets; break; case '*': case '+': - luaL_argcheck(L, 1, *pidx <= top, "format argument exceed"); + argcheck(L, *pidx <= top, 1, "format argument exceed"); if (fmt == '*') li = posrelat(luaL_checkinteger(L, *pidx++), len); else - li = lpb_offset(s) + luaL_checkinteger(L, *pidx++); + li = pb_pos(*s) + luaL_checkinteger(L, *pidx++) + 1; if (li == 0) li = 1; - if (li > (lua_Integer)len) li = len + 1; - s->base.p = s->head + li - 1; + if (li > (lua_Integer)len) li = (lua_Integer)len + 1; + s->p = s->start + li - 1; break; default: @@ -754,28 +985,35 @@ static int lpb_unpackloc(lua_State *L, int *pidx, int top, int fmt, pb_SliceExt return 1; } -static int lpb_unpackfmt(lua_State *L, int idx, const char *fmt, pb_SliceExt *s) { +static int lpb_unpackfmt(lua_State *L, int idx, const char *fmt, pb_Slice *s) { int rets = 0, top = lua_gettop(L), type; for (; *fmt != '\0'; ++fmt) { if (lpb_unpackloc(L, &idx, top, *fmt, s, &rets)) continue; - if (s->base.p >= s->base.end) { lua_pushnil(L); return rets + 1; } + if (s->p >= s->end) { lua_pushnil(L); return rets + 1; } luaL_checkstack(L, 1, "too many values"); if (!lpb_unpackscalar(L, &idx, top, *fmt, s)) { - if ((type = lpb_typefmt(fmt)) < 0) - argerror(L, 1, "invalid formater: '%c'", *fmt); - lpb_readtype(L, type, s); + argcheck(L, (type = lpb_typefmt(*fmt)) >= 0, + 1, "invalid formater: '%c'", *fmt); + lpb_readtype(L, default_lstate(L), type, s); } ++rets; } return rets; } +static lpb_Slice *check_lslice(lua_State *L, int idx) { + pb_Slice *s = check_slice(L, idx); + argcheck(L, lua_rawlen(L, 1) == sizeof(lpb_Slice), + idx, "unsupport operation for raw mode slice"); + return (lpb_Slice*)s; +} + static int Lslice_new(lua_State *L) { lpb_Slice *s; lua_settop(L, 3); s = (lpb_Slice*)lua_newuserdata(L, sizeof(lpb_Slice)); - lpb_initslice(L, 1, s); + lpb_initslice(L, 1, s, sizeof(lpb_Slice)); luaL_setmetatable(L, PB_SLICE); return 1; } @@ -784,36 +1022,45 @@ static int Lslice_libcall(lua_State *L) { lpb_Slice *s; lua_settop(L, 4); s = (lpb_Slice*)lua_newuserdata(L, sizeof(lpb_Slice)); - lpb_initslice(L, 2, s); + lpb_initslice(L, 2, s, sizeof(lpb_Slice)); luaL_setmetatable(L, PB_SLICE); return 1; } static int Lslice_reset(lua_State *L) { lpb_Slice *s = (lpb_Slice*)check_slice(L, 1); - lpb_resetslice(L, s); + size_t size = lua_rawlen(L, 1); + lpb_resetslice(L, s, size); if (!lua_isnoneornil(L, 2)) - lpb_initslice(L, 2, s); + lpb_initslice(L, 2, s, size); return_self(L); } static int Lslice_tostring(lua_State *L) { - lpb_Slice *s = (lpb_Slice*)check_slice(L, 1); - lua_pushfstring(L, "pb.Slice: %p", s); + pb_Slice *s = check_slice(L, 1); + lua_pushfstring(L, "pb.Slice: %p%s", s, + lua_rawlen(L, 1) == sizeof(lpb_Slice) ? "" : " (raw)"); return 1; } static int Lslice_len(lua_State *L) { - lpb_Slice *s = (lpb_Slice*)check_slice(L, 1); - lua_pushinteger(L, (lua_Integer)pb_len(s->curr.base)); - lua_pushinteger(L, (lua_Integer)lpb_offset(&s->curr)); + pb_Slice *s = check_slice(L, 1); + lua_pushinteger(L, (lua_Integer)pb_len(*s)); + lua_pushinteger(L, (lua_Integer)pb_pos(*s)+1); return 2; } +static int Lslice_unpack(lua_State *L) { + pb_Slice view, *s = test_slice(L, 1); + const char *fmt = luaL_checkstring(L, 2); + if (s == NULL) view = lpb_checkslice(L, 1), s = &view; + return lpb_unpackfmt(L, 3, fmt, s); +} + static int Lslice_level(lua_State *L) { - lpb_Slice *s = (lpb_Slice*)check_slice(L, 1); + lpb_Slice *s = check_lslice(L, 1); if (!lua_isnoneornil(L, 2)) { - pb_SliceExt *se; + pb_Slice *se; lua_Integer level = posrelat(luaL_checkinteger(L, 2), s->used); if (level > (lua_Integer)s->used) return 0; @@ -821,9 +1068,9 @@ static int Lslice_level(lua_State *L) { se = &s->curr; else se = &s->buff[level]; - lua_pushinteger(L, se->base.p - s->buff[0].head + 1); - lua_pushinteger(L, se->head - s->buff[0].head + 1); - lua_pushinteger(L, se->base.end - s->buff[0].head); + lua_pushinteger(L, (lua_Integer)(se->p - s->buff[0].start) + 1); + lua_pushinteger(L, (lua_Integer)(se->start - s->buff[0].start) + 1); + lua_pushinteger(L, (lua_Integer)(se->end - s->buff[0].start)); return 3; } lua_pushinteger(L, s->used); @@ -831,37 +1078,34 @@ static int Lslice_level(lua_State *L) { } static int Lslice_enter(lua_State *L) { - lpb_Slice *s = (lpb_Slice*)check_slice(L, 1); - pb_SliceExt view; + lpb_Slice *s = check_lslice(L, 1); + pb_Slice view; if (lua_isnoneornil(L, 2)) { - if (pb_readbytes(&s->curr.base, &view.base) == 0) - return argerror(L, 1, "bytes wireformat expected at offset %d", - lpb_offset(&s->curr)); - view.head = view.base.p; + argcheck(L, pb_readbytes(&s->curr, &view) != 0, + 1, "bytes wireformat expected at offset %d", pb_pos(s->curr)+1); + view.start = view.p; lpb_enterview(L, s, view); - } - else { - lua_Integer i = 1, j = -1; - lua_Integer range = rangerelat(L, 2, &i, &j, s->curr.base.end - s->curr.head); - view.base.p = s->curr.head + i - 1; - view.base.end = view.base.p + range; - view.head = s->curr.head; + } else { + lua_Integer r[] = {1, -1}; + lua_Integer range = rangerelat(L, 2, r, pb_len(s->curr)); + view.p = s->curr.start + r[0] - 1; + view.end = view.p + range; + view.start = s->curr.p; lpb_enterview(L, s, view); } return_self(L); } static int Lslice_leave(lua_State *L) { - lpb_Slice *s = (lpb_Slice*)check_slice(L, 1); + lpb_Slice *s = check_lslice(L, 1); lua_Integer count = posrelat(luaL_optinteger(L, 2, 1), s->used); if (count > (lua_Integer)s->used) - argerror(L, 2, "level (%d) exceed max level %d", + argcheck(L, 0, 2, "level (%d) exceed max level %d", (int)count, (int)s->used); else if (count == (lua_Integer)s->used) { s->curr = s->buff[0]; s->used = 1; - } - else { + } else { s->used -= (size_t)count; s->curr = s->buff[s->used]; } @@ -870,20 +1114,21 @@ static int Lslice_leave(lua_State *L) { return 2; } -static int Lslice_unpack(lua_State *L) { - pb_SliceExt view, *s = test_slice(L, 1); - const char *fmt = luaL_checkstring(L, 2); - if (s == NULL) view = lpb_initext(lpb_checkslice(L, 1)), s = &view; - return lpb_unpackfmt(L, 3, fmt, s); +LUALIB_API int lpb_newslice(lua_State *L, const char *s, size_t len) { + pb_Slice *ls = (pb_Slice*)lua_newuserdata(L, sizeof(pb_Slice)); + *ls = pb_lslice(s, len); + luaL_setmetatable(L, PB_SLICE); + return 1; } -LUALIB_API int luaopen_pb_slice(lua_State *L) { +LUALIB_API int luaopen_lprotobuf_slice(lua_State *L) { luaL_Reg libs[] = { { "__tostring", Lslice_tostring }, { "__len", Lslice_len }, { "__gc", Lslice_reset }, { "delete", Lslice_reset }, { "tohex", Lpb_tohex }, + { "fromhex", Lpb_fromhex }, { "result", Lpb_result }, #define ENTRY(name) { #name, Lslice_##name } ENTRY(new), @@ -910,151 +1155,99 @@ LUALIB_API int luaopen_pb_slice(lua_State *L) { /* high level typeinfo/encode/decode routines */ -#define default_state(L) ((pb_State*)default_lstate(L)) - -static const char state_name[] = PB_STATE; - -typedef struct lpb_State { - pb_State base; - int enum_as_value; -} lpb_State; - -static int Lpb_delete(lua_State *L) { - if (lua53_rawgetp(L, LUA_REGISTRYINDEX, state_name) == LUA_TUSERDATA) { - lpb_State *LS = (lpb_State*)lua_touserdata(L, -1); - if (LS != NULL) { - pb_free(&LS->base); - lua_pushnil(L); - lua_setfield(L, LUA_REGISTRYINDEX, PB_STATE); - } - } - return 0; -} - -static lpb_State *default_lstate(lua_State *L) { - lpb_State *LS; - if (lua53_rawgetp(L, LUA_REGISTRYINDEX, state_name) == LUA_TUSERDATA) { - LS = (lpb_State*)lua_touserdata(L, -1); - lua_pop(L, 1); - } - else { - LS = lua_newuserdata(L, sizeof(lpb_State)); - lua_replace(L, -2); - memset(LS, 0, sizeof(lpb_State)); - pb_init(&LS->base); - lua_createtable(L, 0, 1); - lua_pushcfunction(L, Lpb_delete); - lua_setfield(L, -2, "__gc"); - lua_setmetatable(L, -2); - lua_rawsetp(L, LUA_REGISTRYINDEX, state_name); - } - return LS; -} - -static pb_Type *lpb_type(pb_State *S, const char *name) { - pb_Type *t; - if (name == NULL || *name == '.') - t = pb_type(S, pb_name(S, name)); +static const pb_Type *lpb_type(lpb_State *LS, pb_Slice s) { + const pb_Type *t; + if (s.p == NULL || *s.p == '.') + t = pb_type(lpb_state(LS), lpb_name(LS, s)); else { pb_Buffer b; pb_initbuffer(&b); - pb_addchar(&b, '.'); - pb_addslice(&b, pb_slice(name)); - pb_addchar(&b, '\0'); - t = pb_type(S, pb_name(S, pb_buffer(&b))); + *pb_prepbuffsize(&b, 1) = '.'; + pb_addsize(&b, 1); + pb_addslice(&b, s); + t = pb_type(lpb_state(LS), pb_name(lpb_state(LS),pb_result(&b),NULL)); pb_resetbuffer(&b); } return t; } -static pb_Field *lpb_checkfield(lua_State *L, int idx, pb_Type *t) { +static const pb_Field *lpb_field(lua_State *L, int idx, const pb_Type *t) { + lpb_State *LS = default_lstate(L); int isint, number = (int)lua_tointegerx(L, idx, &isint); if (isint) return pb_field(t, number); - return pb_fname(t, pb_name(default_state(L), luaL_checkstring(L, idx))); -} - -static int Lpb_clear(lua_State *L) { - pb_State *S = default_state(L); - if (lua_isnoneornil(L, 1)) - pb_free(S), pb_init(S); - else if (lua_isnoneornil(L, 2)) - pb_deltype(S, lpb_type(S, luaL_checkstring(L, 1))); - else { - pb_Type *t = lpb_type(S, luaL_checkstring(L, 1)); - pb_Field *f = lpb_checkfield(L, 2, t); - if (f) pb_delfield(S, t, f); - } - return 0; + return pb_fname(t, lpb_name(LS, lpb_checkslice(L, idx))); } static int Lpb_load(lua_State *L) { - pb_State *S = default_state(L); - pb_SliceExt s = lpb_initext(lpb_checkslice(L, 1)); - lua_pushboolean(L, pb_load(S, &s.base) == PB_OK); - lua_pushinteger(L, lpb_offset(&s)); + lpb_State *LS = default_lstate(L); + pb_Slice s = lpb_checkslice(L, 1); + int r = pb_load(&LS->local, &s); + if (r == PB_OK) global_state = &LS->local; + lua_pushboolean(L, r == PB_OK); + lua_pushinteger(L, pb_pos(s)+1); return 2; } static int Lpb_loadfile(lua_State *L) { - pb_State *S = default_state(L); + lpb_State *LS = default_lstate(L); const char *filename = luaL_checkstring(L, 1); size_t size; pb_Buffer b; - pb_SliceExt s; + pb_Slice s; int ret; FILE *fp = fopen(filename, "rb"); if (fp == NULL) return luaL_fileresult(L, 0, filename); pb_initbuffer(&b); do { - void *d = pb_prepbuffsize(&b, BUFSIZ); + char *d = pb_prepbuffsize(&b, BUFSIZ); if (d == NULL) { fclose(fp); return luaL_error(L, "out of memory"); } size = fread(d, 1, BUFSIZ, fp); pb_addsize(&b, size); } while (size == BUFSIZ); fclose(fp); - s = lpb_initext(pb_result(&b)); - ret = pb_load(S, &s.base); + s = pb_result(&b); + ret = pb_load(&LS->local, &s); + if (ret == PB_OK) global_state = &LS->local; pb_resetbuffer(&b); lua_pushboolean(L, ret == PB_OK); - lua_pushinteger(L, lpb_offset(&s)); + lua_pushinteger(L, pb_pos(s)+1); return 2; } -static int lpb_pushtype(lua_State *L, pb_Type *t) { +static int lpb_pushtype(lua_State *L, const pb_Type *t) { if (t == NULL) return 0; - lua_pushstring(L, (char*)t->name); - lua_pushstring(L, (char*)t->basename); + lua_pushstring(L, (const char*)t->name); + lua_pushstring(L, (const char*)t->basename); lua_pushstring(L, t->is_map ? "map" : t->is_enum ? "enum" : "message"); return 3; } -static int lpb_pushfield(lua_State *L, pb_Type *t, pb_Field *f) { - pb_OneofEntry *e; +static int lpb_pushfield(lua_State *L, const pb_Type *t, const pb_Field *f) { if (f == NULL) return 0; - lua_pushstring(L, (char*)f->name); + lua_pushstring(L, (const char*)f->name); lua_pushinteger(L, f->number); - lua_pushstring(L, f->type ? (char*)f->type->name : + lua_pushstring(L, f->type ? + (const char*)f->type->name : pb_typename(f->type_id, "")); - lua_pushstring(L, (char*)f->default_value); - lua_pushstring(L, f->packed ? "packed" : - f->repeated ? "repeated" : "optional"); - e = (pb_OneofEntry*)pb_gettable(&t->oneof_index, (pb_Key)f); - if (e) { - lua_pushstring(L, (const char*)e->name); - lua_pushinteger(L, e->index-1); + lua_pushstring(L, (const char*)f->default_value); + lua_pushstring(L, f->repeated ? + (f->packed ? "packed" : "repeated") : + "optional"); + if (f->oneof_idx > 0) { + lua_pushstring(L, (const char*)pb_oneofname(t, f->oneof_idx)); + lua_pushinteger(L, f->oneof_idx-1); return 7; } return 5; } static int Lpb_typesiter(lua_State *L) { - pb_State *S = default_state(L); - pb_Type *t = lpb_type(S, lua_tostring(L, 2)); + lpb_State *LS = default_lstate(L); + const pb_Type *t = lpb_type(LS, lpb_toslice(L, 2)); if ((t == NULL && !lua_isnoneornil(L, 2))) return 0; - while (pb_nexttype(S, &t) && t->field_count == 0) - continue; + pb_nexttype(lpb_state(LS), &t); return lpb_pushtype(L, t); } @@ -1066,9 +1259,9 @@ static int Lpb_types(lua_State *L) { } static int Lpb_fieldsiter(lua_State *L) { - pb_State *S = default_state(L); - pb_Type *t = lpb_type(S, luaL_checkstring(L, 1)); - pb_Field *f = pb_fname(t, pb_name(S, lua_tostring(L, 2))); + lpb_State *LS = default_lstate(L); + const pb_Type *t = lpb_type(LS, lpb_checkslice(L, 1)); + const pb_Field *f = pb_fname(t, lpb_name(LS, lpb_toslice(L, 2))); if ((f == NULL && !lua_isnoneornil(L, 2)) || !pb_nextfield(t, &f)) return 0; return lpb_pushfield(L, t, f); @@ -1082,154 +1275,281 @@ static int Lpb_fields(lua_State *L) { } static int Lpb_type(lua_State *L) { - pb_State *S = default_state(L); - pb_Type *t = lpb_type(S, luaL_checkstring(L, 1)); - if (t == NULL || t->field_count == 0) + lpb_State *LS = default_lstate(L); + const pb_Type *t = lpb_type(LS, lpb_checkslice(L, 1)); + if (t == NULL || t->is_dead) return 0; return lpb_pushtype(L, t); } static int Lpb_field(lua_State *L) { - pb_State *S = default_state(L); - pb_Type *t = lpb_type(S, luaL_checkstring(L, 1)); - return lpb_pushfield(L, t, lpb_checkfield(L, 2, t)); + lpb_State *LS = default_lstate(L); + const pb_Type *t = lpb_type(LS, lpb_checkslice(L, 1)); + return lpb_pushfield(L, t, lpb_field(L, 2, t)); } static int Lpb_enum(lua_State *L) { - pb_State *S = default_state(L); - pb_Type *t = lpb_type(S, luaL_checkstring(L, 1)); - pb_Field *f = lpb_checkfield(L, 2, t); + lpb_State *LS = default_lstate(L); + const pb_Type *t = lpb_type(LS, lpb_checkslice(L, 1)); + const pb_Field *f = lpb_field(L, 2, t); if (f == NULL) return 0; if (lua_type(L, 2) == LUA_TNUMBER) - lua_pushstring(L, (char*)f->name); + lua_pushstring(L, (const char*)f->name); else - lua_pushinteger(L, f->number); + lpb_pushinteger(L, f->number, LS->int64_mode); return 1; } -static int lpb_pushdefault(lua_State *L, lpb_State *S, pb_Field *f) { - lua_Number ln; - lua_Integer li; +static int lpb_pushdefault(lua_State *L, lpb_State *LS, const pb_Field *f, int is_proto3) { + int ret = 0; + const pb_Type *type; char *end; + if (f == NULL) return 0; + if (is_proto3 && f->repeated) { lua_newtable(L); return 1; } switch (f->type_id) { case PB_Tbytes: case PB_Tstring: - lua_pushstring(L, (char*)f->default_value); + if (f->default_value) + ret = 1, lua_pushstring(L, (const char*)f->default_value); + else if (is_proto3) + ret = 1, lua_pushliteral(L, ""); break; case PB_Tenum: - if (!(f = pb_fname(f->type, f->default_value))) return 0; - if (S->enum_as_value) lua_pushinteger(L, (lua_Integer)f->number); - else lua_pushstring(L, (char*)f->name); + if ((type = f ? f->type : NULL) == NULL) return 0; + if ((f = pb_fname(type, f->default_value)) != NULL) { + if (LS->enum_as_value) + ret = 1, lpb_pushinteger(L, f->number, LS->int64_mode); + else + ret = 1, lua_pushstring(L, (const char*)f->name); + } else if (is_proto3) { + if ((f = pb_field(type, 0)) == NULL || LS->enum_as_value) + ret = 1, lua_pushinteger(L, 0); + else + ret = 1, lua_pushstring(L, (const char*)f->name); + } break; + case PB_Tmessage: + return 0; case PB_Tbool: - if (f->default_value == pb_name(&S->base, "true")) - lua_pushboolean(L, 1); - else if (f->default_value == pb_name(&S->base, "false")) - lua_pushboolean(L, 0); - else return 0; + if (f->default_value) { + if (f->default_value == lpb_name(LS, pb_slice("true"))) + ret = 1, lua_pushboolean(L, 1); + else if (f->default_value == lpb_name(LS, pb_slice("false"))) + ret = 1, lua_pushboolean(L, 0); + } else if (is_proto3) ret = 1, lua_pushboolean(L, 0); break; case PB_Tdouble: case PB_Tfloat: - ln = (lua_Number)strtod((char*)f->default_value, &end); - if ((char*)f->default_value == end) return 0; - lua_pushnumber(L, ln); + if (f->default_value) { + lua_Number ln = (lua_Number)strtod((const char*)f->default_value, &end); + if ((const char*)f->default_value == end) return 0; + ret = 1, lua_pushnumber(L, ln); + } else if (is_proto3) ret = 1, lua_pushnumber(L, 0.0); break; + default: - li = (lua_Integer)strtol((char*)f->default_value, &end, 10); - if ((char*)f->default_value == end) return 0; - lua_pushinteger(L, li); - break; + if (f->default_value) { + lua_Integer li = (lua_Integer)strtol((const char*)f->default_value, &end, 10); + if ((const char*)f->default_value == end) return 0; + ret = 1, lpb_pushinteger(L, li, LS->int64_mode); + } else if (is_proto3) ret = 1, lua_pushinteger(L, 0); } - return 1; + return ret; } -static int Lpb_defaults(lua_State *L) { - lpb_State *S = default_lstate(L); - pb_Type *t = lpb_type(&S->base, luaL_checkstring(L, 1)); - pb_Field *f = NULL; - if (!lua_istable(L, 2)) { - lua_settop(L, 1); +static void lpb_pushdefaults(lua_State *L, lpb_State *LS, const pb_Type *t) { + lpb_pushdeftable(L, LS); + if (lua53_rawgetp(L, -1, t) != LUA_TTABLE) { + const pb_Field *f = NULL; + lua_pop(L, 1); lua_newtable(L); + while (pb_nextfield(t, &f)) + if (!f->repeated && lpb_pushdefault(L, LS, f, t->is_proto3)) + lua_setfield(L, -2, (const char*)f->name); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + lua_pushvalue(L, -1); + lua_rawsetp(L, -3, t); } - while (pb_nextfield(t, &f)) { - int type = lua53_getfield(L, 2, (char*)f->name); - if (f->default_value && type == LUA_TNIL && lpb_pushdefault(L, S, f)) - lua_setfield(L, 2, (char*)f->name); - lua_pop(L, 1); + lua_remove(L, -2); +} + +static void lpb_cleardefaults(lua_State *L, lpb_State *LS, const pb_Type *t) { + lpb_pushdeftable(L, LS); + lua_pushnil(L); + lua_rawsetp(L, -2, t); + lua_pop(L, 1); +} + +static int Lpb_defaults(lua_State *L) { + lpb_State *LS = default_lstate(L); + const pb_Type *t = lpb_type(LS, lpb_checkslice(L, 1)); + int clear = lua_toboolean(L, 2); + if (t == NULL) luaL_argerror(L, 1, "type not found"); + lpb_pushdefaults(L, LS, t); + if (clear) lpb_cleardefaults(L, LS, t); + return 1; +} + +static int Lpb_hook(lua_State *L) { + lpb_State *LS = default_lstate(L); + const pb_Type *t = lpb_type(LS, lpb_checkslice(L, 1)); + int type = lua_type(L, 2); + if (t == NULL) luaL_argerror(L, 1, "type not found"); + if (type != LUA_TNONE && type != LUA_TNIL && type != LUA_TFUNCTION) + typeerror(L, 2, "function"); + lua_settop(L, 2); + lpb_pushhooktable(L, LS); + lua_rawgetp(L, 3, t); + if (type != LUA_TNONE) { + lua_pushvalue(L, 2); + lua_rawsetp(L, 3, t); } return 1; } +static int Lpb_clear(lua_State *L) { + lpb_State *LS = default_lstate(L); + pb_State *S = (pb_State*)LS->state; + pb_Type *t; + if (lua_isnoneornil(L, 1)) { + pb_free(&LS->local), pb_init(&LS->local); + luaL_unref(L, LUA_REGISTRYINDEX, LS->defs_index); + LS->defs_index = LUA_NOREF; + luaL_unref(L, LUA_REGISTRYINDEX, LS->hooks_index); + LS->hooks_index = LUA_NOREF; + return 0; + } + LS->state = &LS->local; + t = (pb_Type*)lpb_type(LS, lpb_checkslice(L, 1)); + if (lua_isnoneornil(L, 2)) pb_deltype(&LS->local, t); + else pb_delfield(&LS->local, t, (pb_Field*)lpb_field(L, 2, t)); + LS->state = S; + lpb_cleardefaults(L, LS, t); + return 0; +} + +static int Lpb_typefmt(lua_State *L) { + pb_Slice s = lpb_checkslice(L, 1); + const char *r = NULL; + char buf[2] = {0}; + int type; + if (pb_len(s) == 1) + r = pb_typename(type = lpb_typefmt(*s.p), "!"); + else if (lpb_type(default_lstate(L), s)) + r = "message", type = PB_TBYTES; + else if ((type = pb_typebyname(s.p, PB_Tmessage)) != PB_Tmessage) { + switch (type) { +#define X(name,type,fmt) case PB_T##name: buf[0] = fmt, r = buf; break; + PB_TYPES(X) +#undef X + } + type = pb_wtypebytype(type); + } else if ((type = pb_wtypebyname(s.p, PB_Tmessage)) != PB_Tmessage) { + switch (type) { +#define X(id,name,fmt) case PB_T##id: buf[0] = fmt, r = buf; break; + PB_WIRETYPES(X) +#undef X + } + } + lua_pushstring(L, r ? r : "!"); + lua_pushinteger(L, type); + return 2; +} -/* encode protobuf */ -static void lpb_encode (lua_State *L, pb_Buffer *b, pb_Type *t); +/* protobuf encode */ -static void lpb_checktable(lua_State *L, pb_Field *f) { - if (!lua_istable(L, -1)) - argerror(L, 2, "table expected at field '%s', got %s", - (char*)f->name, luaL_typename(L, -1)); +typedef struct lpb_Env { + lua_State *L; + lpb_State *LS; + pb_Buffer *b; + pb_Slice *s; +} lpb_Env; + +static void lpb_encode (lpb_Env *e, const pb_Type *t); + +static void lpb_checktable(lua_State *L, const pb_Field *f) { + argcheck(L, lua_istable(L, -1), + 2, "table expected at field '%s', got %s", + (const char*)f->name, luaL_typename(L, -1)); } -static void lpbE_enum(lua_State *L, pb_Buffer *b, pb_Field *f) { +static void lpbE_enum(lpb_Env *e, const pb_Field *f) { + lua_State *L = e->L; + pb_Buffer *b = e->b; + const pb_Field *ev; int type = lua_type(L, -1); - if (type == LUA_TNUMBER) { - lua_Integer v = lua_tointeger(L, -1); - pb_addvarint64(b, (uint64_t)v); - } - else if (type != LUA_TSTRING && type != LUA_TUSERDATA) - argerror(L, 2, "number/string expected at field '%s', got %s", - (char*)f->name, luaL_typename(L, -1)); - else { - pb_State *S = default_state(L); - pb_Field *ev = pb_fname(f->type, pb_name(S, lua_tostring(L, -1))); - if (ev == NULL) - argerror(L, 2, "can not encode unknown enum '%s' at field '%s'", - lua_tostring(L, -1), (char*)f->name); + if (type == LUA_TNUMBER) + pb_addvarint64(b, (uint64_t)lua_tonumber(L, -1)); + else if ((ev = pb_fname(f->type, + lpb_name(e->LS, lpb_toslice(L, -1)))) != NULL) pb_addvarint32(b, ev->number); - } + else if (type != LUA_TSTRING) + argcheck(L, 0, 2, "number/string expected at field '%s', got %s", + (const char*)f->name, luaL_typename(L, -1)); + else + argcheck(L, 0, 2, "can not encode unknown enum '%s' at field '%s'", + lua_tostring(L, -1), (const char*)f->name); } -static void lpbE_field(lua_State *L, pb_Buffer *b, pb_Field *f, int hastag) { +static void lpbE_field(lpb_Env *e, const pb_Field *f, size_t *plen) { + lua_State *L = e->L; + pb_Buffer *b = e->b; size_t len; int ltype; - if (hastag) pb_addvarint32(b, pb_pair(f->number, pb_wtypebytype(f->type_id))); + if (plen) *plen = 0; switch (f->type_id) { case PB_Tenum: - lpbE_enum(L, b, f); + lpbE_enum(e, f); break; case PB_Tmessage: lpb_checktable(L, f); len = pb_bufflen(b); - lpb_encode(L, b, f->type); + lpb_encode(e, f->type); lpb_addlength(L, b, len); break; default: - if ((ltype = lpb_addtype(L, b, -1, f->type_id)) != 0) - argerror(L, 2, "%s expected at field '%s', got %s", - lua_typename(L, ltype), - (char*)f->name, luaL_typename(L, -1)); + ltype = lpb_addtype(L, b, -1, f->type_id, plen); + argcheck(L, ltype == 0, + 2, "%s expected for field '%s', got %s", + lua_typename(L, ltype), + (const char*)f->name, luaL_typename(L, -1)); } } -static void lpbE_map(lua_State *L, pb_Buffer *b, pb_Field *f) { - pb_Field *kf = pb_field(f->type, 1); - pb_Field *vf = pb_field(f->type, 2); - size_t len; +static void lpbE_tagfield(lpb_Env *e, const pb_Field *f, int ignorezero) { + size_t hlen = pb_addvarint32(e->b, + pb_pair(f->number, pb_wtypebytype(f->type_id))); + size_t ignoredlen; + lpbE_field(e, f, &ignoredlen); + if (ignoredlen != 0 && ignorezero) + e->b->size -= (unsigned)(ignoredlen + hlen); +} + +static void lpbE_map(lpb_Env *e, const pb_Field *f) { + lua_State *L = e->L; + const pb_Field *kf = pb_field(f->type, 1); + const pb_Field *vf = pb_field(f->type, 2); if (kf == NULL || vf == NULL) return; lpb_checktable(L, f); lua_pushnil(L); while (lua_next(L, -2)) { - pb_addvarint32(b, pb_pair(f->number, PB_TBYTES)); - len = pb_bufflen(b); - lpbE_field(L, b, vf, 1); + size_t len; + pb_addvarint32(e->b, pb_pair(f->number, PB_TBYTES)); + len = pb_bufflen(e->b); + lua_pushvalue(L, -2); + lpbE_tagfield(e, kf, 1); lua_pop(L, 1); - lpbE_field(L, b, kf, 1); - lpb_addlength(L, b, len); + lpbE_tagfield(e, vf, 1); + lua_pop(L, 1); + lpb_addlength(L, e->b, len); } } -static void lpbE_repeated(lua_State *L, pb_Buffer *b, pb_Field *f) { +static void lpbE_repeated(lpb_Env *e, const pb_Field *f) { + lua_State *L = e->L; + pb_Buffer *b = e->b; int i; lpb_checktable(L, f); if (f->packed) { @@ -1237,248 +1557,283 @@ static void lpbE_repeated(lua_State *L, pb_Buffer *b, pb_Field *f) { pb_addvarint32(b, pb_pair(f->number, PB_TBYTES)); len = pb_bufflen(b); for (i = 1; lua53_rawgeti(L, -1, i) != LUA_TNIL; ++i) { - lpbE_field(L, b, f, 0); + lpbE_field(e, f, NULL); lua_pop(L, 1); } lpb_addlength(L, b, len); - } - else { + } else { for (i = 1; lua53_rawgeti(L, -1, i) != LUA_TNIL; ++i) { - lpbE_field(L, b, f, 1); + lpbE_tagfield(e, f, 0); lua_pop(L, 1); } } lua_pop(L, 1); } -static void lpb_encode(lua_State *L, pb_Buffer *b, pb_Type *t) { +static void lpb_encode(lpb_Env *e, const pb_Type *t) { + lua_State *L = e->L; luaL_checkstack(L, 3, "message too many levels"); lua_pushnil(L); while (lua_next(L, -2)) { if (lua_type(L, -2) == LUA_TSTRING) { - pb_State *S = default_state(L); - pb_Field *f = pb_fname(t, pb_name(S, lua_tostring(L, -2))); + const pb_Field *f = + pb_fname(t, lpb_name(e->LS, lpb_toslice(L, -2))); if (f == NULL) /* skip */; else if (f->type && f->type->is_map) - lpbE_map(L, b, f); + lpbE_map(e, f); else if (f->repeated) - lpbE_repeated(L, b, f); - else if (!f->type || f->type->field_count != 0) - lpbE_field(L, b, f, 1); + lpbE_repeated(e, f); + else if (!f->type || !f->type->is_dead) + lpbE_tagfield(e, f, t->is_proto3 && !f->oneof_idx); } lua_pop(L, 1); } } -static int lpb_encode_helper(lua_State *L) { - pb_Buffer *b = (pb_Buffer*)lua_touserdata(L, 1); - pb_Type *t = (pb_Type*)lua_touserdata(L, 2); - pb_Buffer *r = test_buffer(L, 3); - lpb_encode(L, b, t); - if (r != b) - lua_pushlstring(L, b->buff, b->size); - else - lua_pop(L, 1); - return 1; -} - static int Lpb_encode(lua_State *L) { - pb_State *S = default_state(L); - pb_Type *t = lpb_type(S, luaL_checkstring(L, 1)); - pb_Buffer buf, *b = test_buffer(L, 3); + lpb_State *LS = default_lstate(L); + const pb_Type *t = lpb_type(LS, lpb_checkslice(L, 1)); + lpb_Env e; + argcheck(L, t!=NULL, 1, "type '%s' does not exists", lua_tostring(L, 1)); luaL_checktype(L, 2, LUA_TTABLE); - if (t == NULL) { - lua_pushnil(L); - lua_pushfstring(L, "type '%s' does not exists", lua_tostring(L, 1)); - return 2; - } - if (b == NULL) pb_initbuffer(&buf), b = &buf; - lua_pushcfunction(L, lpb_encode_helper); - lua_pushlightuserdata(L, b); - lua_pushlightuserdata(L, t); - lua_pushvalue(L, 3); + e.L = L, e.LS = LS, e.b = test_buffer(L, 3); + if (e.b == NULL) pb_resetbuffer(e.b = &LS->buffer); lua_pushvalue(L, 2); - if (lua_pcall(L, 4, 1, 0) != LUA_OK) { - lua_pushnil(L); - lua_insert(L, -2); - return 2; + lpb_encode(&e, t); + if (e.b != &LS->buffer) + lua_settop(L, 3); + else { + lua_pushlstring(L, pb_buffer(e.b), pb_bufflen(e.b)); + pb_resetbuffer(e.b); } - if (b == &buf) pb_resetbuffer(&buf); return 1; } -/* decode protobuf */ +/* protobuf decode */ + +#define lpb_withinput(e,ns,stmt) ((e)->s = (ns), (stmt), (e)->s = s) -static int lpb_decode(lua_State *L, pb_SliceExt *s, pb_Type *t); +static int lpbD_message(lpb_Env *e, const pb_Type *t); + +static void lpb_usehooks(lua_State *L, lpb_State *LS, const pb_Type *t) { + lpb_pushhooktable(L, LS); + if (lua53_rawgetp(L, -1, t) != LUA_TNIL) { + lua_pushvalue(L, -3); + lua_call(L, 1, 1); + if (!lua_isnil(L, -1)) { + lua_pushvalue(L, -1); + lua_replace(L, -4); + } + } + lua_pop(L, 2); +} + +static void lpb_pushtypetable(lua_State *L, lpb_State *LS, const pb_Type *t) { + const pb_Field *f = NULL; + int mode = LS->default_mode; + lua_createtable(L, 0, t->field_count); + switch (t->is_proto3 && mode == LPB_DEFDEF ? LPB_COPYDEF : mode) { + case LPB_COPYDEF: + while (pb_nextfield(t, &f)) + if (!f->oneof_idx && lpb_pushdefault(L, LS, f, t->is_proto3)) + lua_setfield(L, -2, (const char*)f->name); + break; + case LPB_METADEF: + while (pb_nextfield(t, &f)) + if (f->repeated && lpb_pushdefault(L, LS, f, t->is_proto3)) + lua_setfield(L, -2, (const char*)f->name); + lpb_pushdefaults(L, LS, t); + lua_setmetatable(L, -2); + break; + default: /* no default value */ + break; + } +} -static void lpb_fetchtable(lua_State *L, pb_Field *f) { - if (lua53_getfield(L, -1, (char*)f->name) == LUA_TNIL) { +static void lpb_fetchtable(lpb_Env *e, const pb_Field *f) { + lua_State *L = e->L; + if (lua53_getfield(L, -1, (const char*)f->name) == LUA_TNIL) { lua_pop(L, 1); lua_newtable(L); lua_pushvalue(L, -1); - lua_setfield(L, -3, (char*)f->name); + lua_setfield(L, -3, (const char*)f->name); } } -static void lpbD_field(lua_State *L, pb_SliceExt *s, pb_Field *f, uint32_t tag) { - pb_SliceExt sv; - pb_Field *ev; +static void lpbD_rawfield(lpb_Env *e, const pb_Field *f) { + lua_State *L = e->L; + pb_Slice sv, *s = e->s; + const pb_Field *ev = NULL; uint64_t u64; - switch (f->type_id) { case PB_Tenum: - if (pb_readvarint64(&s->base, &u64) == 0) - luaL_error(L, "invalid varint value at offset %d", lpb_offset(s)); - ev = pb_field(f->type, (int32_t)u64); - if (default_lstate(L)->enum_as_value) ev = NULL; - if (ev) lua_pushstring(L, (char*)ev->name); - else lua_pushinteger(L, (lua_Integer)u64); + if (pb_readvarint64(s, &u64) == 0) + luaL_error(L, "invalid varint value at offset %d", pb_pos(*s)+1); + if (!default_lstate(L)->enum_as_value) + ev = pb_field(f->type, (int32_t)u64); + if (ev) lua_pushstring(L, (const char*)ev->name); + else lpb_pushinteger(L, (lua_Integer)u64, default_lstate(L)->int64_mode); + if (e->LS->use_hooks) lpb_usehooks(L, e->LS, f->type); break; case PB_Tmessage: lpb_readbytes(L, s, &sv); - if (f->type) { - lua_newtable(L); - lpb_decode(L, &sv, f->type); + if (f->type == NULL || f->type->is_dead) + lua_pushnil(L); + else { + lpb_pushtypetable(L, e->LS, f->type); + lpb_withinput(e, &sv, lpbD_message(e, f->type)); } break; default: - if (!f->packed && pb_wtypebytype(f->type_id) != (int)pb_gettype(tag)) - luaL_error(L, "type mismatch at offset %d, %s expected for type %s, got %s", - lpb_offset(s), - pb_wtypename(pb_wtypebytype(f->type_id), NULL), - pb_typename(f->type_id, NULL), - pb_wtypename(pb_gettype(tag), NULL)); - lpb_readtype(L, f->type_id, s); + lpb_readtype(L, e->LS, f->type_id, s); } } -static void lpbD_map(lua_State *L, pb_SliceExt *s, pb_Field *f) { - pb_SliceExt p; +static void lpbD_field(lpb_Env *e, const pb_Field *f, uint32_t tag) { + if (pb_wtypebytype(f->type_id) == (int)pb_gettype(tag)) { + lpbD_rawfield(e, f); + return; + } + luaL_error(e->L, + "type mismatch for %s%sfield '%s' at offset %d, " + "%s expected for type %s, got %s", + f->packed ? "packed " : "", f->repeated ? "repeated " : "", + (const char*)f->name, + pb_pos(*e->s)+1, + pb_wtypename(pb_wtypebytype(f->type_id), NULL), + pb_typename(f->type_id, NULL), + pb_wtypename(pb_gettype(tag), NULL)); +} + +static void lpbD_map(lpb_Env *e, const pb_Field *f) { + lua_State *L = e->L; + pb_Slice p, *s = e->s; int mask = 0, top = lua_gettop(L) + 1; - uint32_t tag = 0; - lpb_fetchtable(L, f); + uint32_t tag; + lpb_fetchtable(e, f); lpb_readbytes(L, s, &p); if (f->type == NULL) return; lua_pushnil(L); lua_pushnil(L); - while (pb_readvarint32(&p.base, &tag)) { + while (pb_readvarint32(&p, &tag)) { int n = pb_gettag(tag); if (n == 1 || n == 2) { mask |= n; - lpbD_field(L, &p, pb_field(f->type, n), tag); + lpb_withinput(e, &p, lpbD_field(e, pb_field(f->type, n), tag)); lua_replace(L, top+n); } } + if (!(mask & 1) && lpb_pushdefault(L, e->LS, pb_field(f->type, 1), 1)) + lua_replace(L, top + 1), mask |= 1; + if (!(mask & 2) && lpb_pushdefault(L, e->LS, pb_field(f->type, 2), 1)) + lua_replace(L, top + 2), mask |= 2; if (mask == 3) lua_rawset(L, -3); else lua_pop(L, 2); lua_pop(L, 1); } -static void lpbD_repeated(lua_State *L, pb_SliceExt *s, pb_Field *f, uint32_t tag) { - lpb_fetchtable(L, f); - if (f->packed) { - int len = lua_rawlen(L, -1); - pb_SliceExt p; +static void lpbD_repeated(lpb_Env *e, const pb_Field *f, uint32_t tag) { + lua_State *L = e->L; + lpb_fetchtable(e, f); + if (pb_gettype(tag) != PB_TBYTES + || (!f->packed && pb_wtypebytype(f->type_id) == PB_TBYTES)) { + lpbD_field(e, f, tag); + lua_rawseti(L, -2, (lua_Integer)lua_rawlen(L, -2) + 1); + } else { + int len = (int)lua_rawlen(L, -1); + pb_Slice p, *s = e->s; lpb_readbytes(L, s, &p); - while (p.base.p < p.base.end) { - lpbD_field(L, &p, f, tag); + while (p.p < p.end) { + lpb_withinput(e, &p, lpbD_rawfield(e, f)); lua_rawseti(L, -2, ++len); } } - else { - lpbD_field(L, s, f, tag); - lua_rawseti(L, -2, lua_rawlen(L, -2) + 1); - } lua_pop(L, 1); } -static int lpb_decode(lua_State *L, pb_SliceExt *s, pb_Type *t) { - uint32_t tag = 0; - while (pb_readvarint32(&s->base, &tag)) { - pb_Field *f = pb_field(t, pb_gettag(tag)); +static int lpbD_message(lpb_Env *e, const pb_Type *t) { + lua_State *L = e->L; + pb_Slice *s = e->s; + uint32_t tag; + while (pb_readvarint32(s, &tag)) { + const pb_Field *f = pb_field(t, pb_gettag(tag)); if (f == NULL) - pb_skipvalue(&s->base, tag); + pb_skipvalue(s, tag); else if (f->type && f->type->is_map) - lpbD_map(L, s, f); + lpbD_map(e, f); else if (f->repeated) - lpbD_repeated(L, s, f, tag); - else if (!f->type || f->type->field_count != 0) { - if (f->type && f->type_id == PB_Tmessage) { - pb_SliceExt sv; - lpb_fetchtable(L, f); - lpb_readbytes(L, s, &sv); - lpb_decode(L, &sv, f->type); - lua_pop(L, 1); - } - else { - lua_pushstring(L, (char*)f->name); - lpbD_field(L, s, f, tag); - lua_rawset(L, -3); - } + lpbD_repeated(e, f, tag); + else { + lua_pushstring(L, (const char*)f->name); + lpbD_field(e, f, tag); + lua_rawset(L, -3); } } + if (e->LS->use_hooks) lpb_usehooks(L, e->LS, t); return 1; } -static int lpb_decode_helper(lua_State *L) { - return lpb_decode(L, - (pb_SliceExt*)lua_touserdata(L, 1), - (pb_Type*)lua_touserdata(L, 2)); +static int lpb_decode(lua_State *L, pb_Slice s, int start) { + lpb_State *LS = default_lstate(L); + const pb_Type *t = lpb_type(LS, lpb_checkslice(L, 1)); + lpb_Env e; + argcheck(L, t!=NULL, 1, "type '%s' does not exists", lua_tostring(L, 1)); + lua_settop(L, start); + if (!lua_istable(L, start)) { + lua_pop(L, 1); + lpb_pushtypetable(L, LS, t); + } + e.L = L, e.LS = LS, e.s = &s; + return lpbD_message(&e, t); } static int Lpb_decode(lua_State *L) { - pb_State *S = default_state(L); - pb_Type *t = lpb_type(S, luaL_checkstring(L, 1)); - pb_SliceExt s; - if (t == NULL) { - lua_pushnil(L); - lua_pushfstring(L, "type '%s' does not exists", lua_tostring(L, 1)); - return 2; - } - s = lpb_initext(lpb_checkslice(L, 2)); - if (!lua_istable(L, 3)) { - lua_settop(L, 2); - lua_newtable(L); - } - lua_pushcfunction(L, lpb_decode_helper); - lua_pushlightuserdata(L, &s); - lua_pushlightuserdata(L, t); - lua_pushvalue(L, 3); - if (lua_pcall(L, 3, 1, 0) != LUA_OK) { - lua_pushnil(L); - lua_insert(L, -2); - return 2; - } - return 1; + return lpb_decode(L, lua_isnoneornil(L, 2) ? + pb_lslice(NULL, 0) : + lpb_checkslice(L, 2), 3); } /* pb module interface */ static int Lpb_option(lua_State *L) { +#define OPTS(X) \ + X(0, enum_as_name, LS->enum_as_value = 0) \ + X(1, enum_as_value, LS->enum_as_value = 1) \ + X(2, int64_as_number, LS->int64_mode = LPB_NUMBER) \ + X(3, int64_as_string, LS->int64_mode = LPB_STRING) \ + X(4, int64_as_hexstring, LS->int64_mode = LPB_HEXSTRING) \ + X(5, auto_default_values, LS->default_mode = LPB_DEFDEF) \ + X(6, no_default_values, LS->default_mode = LPB_NODEF) \ + X(7, use_default_values, LS->default_mode = LPB_COPYDEF) \ + X(8, use_default_metatable, LS->default_mode = LPB_METADEF) \ + X(9, enable_hooks, LS->use_hooks = 1) \ + X(10, disable_hooks, LS->use_hooks = 0) \ + static const char *opts[] = { - "enum_as_value", "enum_as_name", NULL +#define X(ID,NAME,CODE) #NAME, + OPTS(X) +#undef X + NULL }; lpb_State *LS = default_lstate(L); switch (luaL_checkoption(L, 1, NULL, opts)) { - case 0: /* enum_as_value */ - LS->enum_as_value = 1; - break; - case 1: /* enum_as_name */ - LS->enum_as_value = 0; - break; +#define X(ID,NAME,CODE) case ID: CODE; break; + OPTS(X) +#undef X } return 0; +#undef OPTS } LUALIB_API int luaopen_lprotobuf(lua_State *L) { luaL_Reg libs[] = { - { "pack", Lbuf_pack }, - { "unpack", Lslice_unpack }, + { "pack", Lbuf_pack }, + { "unpack", Lslice_unpack }, #define ENTRY(name) { #name, Lpb_##name } ENTRY(clear), ENTRY(load), @@ -1489,14 +1844,75 @@ LUALIB_API int luaopen_lprotobuf(lua_State *L) { ENTRY(fields), ENTRY(type), ENTRY(field), + ENTRY(typefmt), ENTRY(enum), ENTRY(defaults), + ENTRY(hook), ENTRY(tohex), + ENTRY(fromhex), ENTRY(result), ENTRY(option), + ENTRY(state), #undef ENTRY { NULL, NULL } }; + luaL_Reg meta[] = { + { "__gc", Lpb_delete }, + { "setdefault", Lpb_state }, + { NULL, NULL } + }; + if (luaL_newmetatable(L, PB_STATE)) { + luaL_setfuncs(L, meta, 0); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + } luaL_newlib(L, libs); return 1; } + +static int Lpb_decode_unsafe(lua_State *L) { + const char *data = (const char *)lua_touserdata(L, 2); + size_t size = (size_t)luaL_checkinteger(L, 3); + if (data == NULL) typeerror(L, 2, "userdata"); + return lpb_decode(L, pb_lslice(data, size), 4); +} + +static int Lpb_slice_unsafe(lua_State *L) { + const char *data = (const char *)lua_touserdata(L, 1); + size_t size = (size_t)luaL_checkinteger(L, 2); + if (data == NULL) typeerror(L, 1, "userdata"); + return lpb_newslice(L, data, size); +} + +static int Lpb_touserdata(lua_State *L) { + pb_Slice s = lpb_toslice(L, 1); + lua_pushlightuserdata(L, (void*)s.p); + lua_pushinteger(L, pb_len(s)); + return 2; +} + +static int Lpb_use(lua_State *L) { + const char *opts[] = { "global", "local", NULL }; + lpb_State *LS = default_lstate(L); + const pb_State *GS = global_state; + switch (luaL_checkoption(L, 1, NULL, opts)) { + case 0: if (GS) LS->state = GS; break; + case 1: LS->state = &LS->local; break; + } + lua_pushboolean(L, GS != NULL); + return 1; +} + +LUALIB_API int luaopen_lprotobuf_unsafe(lua_State *L) { + luaL_Reg libs[] = { + { "decode", Lpb_decode_unsafe }, + { "slice", Lpb_slice_unsafe }, + { "touserdata", Lpb_touserdata }, + { "use", Lpb_use }, + { NULL, NULL } + }; + luaL_newlib(L, libs); + return 1; +} + +PB_NS_END \ No newline at end of file diff --git a/luaclib/src/lpbc/lpb.h b/luaclib/src/lpbc/lpb.h index e4c03cea..de69c89e 100644 --- a/luaclib/src/lpbc/lpb.h +++ b/luaclib/src/lpbc/lpb.h @@ -48,6 +48,14 @@ typedef signed int int32_t; typedef unsigned long long uint64_t; typedef signed long long int64_t; +#ifndef INT64_MIN +# define INT64_MIN LLONG_MIN +#endif + +#ifndef INT64_MAX +# define INT64_MAX LLONG_MAX +#endif + #elif defined(__SCO__) || defined(__USLC__) || defined(__MINGW32__) # include #else @@ -63,37 +71,39 @@ typedef signed long long int64_t; # endif /* __sun__ || __digital__ */ #endif -#include "../../../src/core.h" +#include +#include PB_NS_BEGIN /* types */ -#define PB_WIRETYPES(X) /* X(name, index) */\ - X(VARINT, "varint", 0) X(64BIT, "64bit", 1) X(BYTES, "bytes", 2) \ - X(GSTART, "gstart", 3) X(GEND, "gend", 4) X(32BIT, "32bit", 5) \ - -#define PB_TYPES(X) /* X(name, type, index) */\ - X(double, double, 1) X(float, float, 2) \ - X(int64, int64_t, 3) X(uint64, uint64_t, 4) \ - X(int32, int32_t, 5) X(fixed64, uint64_t, 6) \ - X(fixed32, uint32_t, 7) X(bool, int, 8) \ - X(string, pb_Slice, 9) X(group, pb_Slice, 10) \ - X(message, pb_Slice, 11) X(bytes, pb_Slice, 12) \ - X(uint32, uint32_t, 13) X(enum, int32_t, 14) \ - X(sfixed32, int32_t, 15) X(sfixed64, int64_t, 16) \ - X(sint32, int32_t, 17) X(sint64, int64_t, 18) \ +#define PB_WIRETYPES(X) /* X(id, name, fmt) */\ + X(VARINT, "varint", 'v') X(64BIT, "64bit", 'q') X(BYTES, "bytes", 's') \ + X(GSTART, "gstart", '!') X(GEND, "gend", '!') X(32BIT, "32bit", 'd') \ + +#define PB_TYPES(X) /* X(name, type, fmt) */\ + X(double, double, 'F') X(float, float, 'f') \ + X(int64, int64_t, 'I') X(uint64, uint64_t, 'U') \ + X(int32, int32_t, 'i') X(fixed64, uint64_t, 'X') \ + X(fixed32, uint32_t, 'x') X(bool, int, 'b') \ + X(string, pb_Slice, 't') X(group, pb_Slice, 'g') \ + X(message, pb_Slice, 'S') X(bytes, pb_Slice, 's') \ + X(uint32, uint32_t, 'u') X(enum, int32_t, 'v') \ + X(sfixed32, int32_t, 'y') X(sfixed64, int64_t, 'Y') \ + X(sint32, int32_t, 'j') X(sint64, int64_t, 'J') \ typedef enum pb_WireType { -#define X(name, s, index) PB_T##name, +#define X(id,name,fmt) PB_T##id, PB_WIRETYPES(X) #undef X PB_TWIRECOUNT } pb_WireType; typedef enum pb_FieldType { -#define X(name, type, index) PB_T##name = index, + PB_TNONE, +#define X(name,type,fmt) PB_T##name, PB_TYPES(X) #undef X PB_TYPECOUNT @@ -102,7 +112,7 @@ typedef enum pb_FieldType { /* conversions */ -PB_API uint64_t pb_expandsig ( int32_t v); +PB_API uint64_t pb_expandsig (uint32_t v); PB_API uint32_t pb_encode_sint32 ( int32_t v); PB_API int32_t pb_decode_sint32 (uint32_t v); PB_API uint64_t pb_encode_sint64 ( int64_t v); @@ -115,19 +125,21 @@ PB_API double pb_decode_double (uint64_t v); /* decode */ -typedef struct pb_Slice { const char *p, *end; } pb_Slice; -#define pb_gettype(v) ((v) & 7) -#define pb_gettag(v) ((v) >> 3) -#define pb_pair(tag, type) ((tag) << 3 | ((type) & 7)) +typedef struct pb_Slice { const char *p, *start, *end; } pb_Slice; +#define pb_gettype(v) ((v) & 7) +#define pb_gettag(v) ((v) >> 3) +#define pb_pair(tag,type) ((tag) << 3 | ((type) & 7)) PB_API pb_Slice pb_slice (const char *p); PB_API pb_Slice pb_lslice (const char *p, size_t len); -PB_API size_t pb_len (pb_Slice s); + +PB_API size_t pb_pos (const pb_Slice s); +PB_API size_t pb_len (const pb_Slice s); PB_API size_t pb_readvarint32 (pb_Slice *s, uint32_t *pv); PB_API size_t pb_readvarint64 (pb_Slice *s, uint64_t *pv); -PB_API size_t pb_readfixed32 (pb_Slice *s, uint32_t *pv); -PB_API size_t pb_readfixed64 (pb_Slice *s, uint64_t *pv); +PB_API size_t pb_readfixed32 (pb_Slice *s, uint32_t *pv); +PB_API size_t pb_readfixed64 (pb_Slice *s, uint64_t *pv); PB_API size_t pb_readslice (pb_Slice *s, size_t len, pb_Slice *pv); PB_API size_t pb_readbytes (pb_Slice *s, pb_Slice *pv); @@ -148,28 +160,32 @@ PB_API int pb_wtypebytype (int type); /* encode */ -#define PB_BUFFERSIZE (1024) +#define PB_SSO_SIZE (sizeof(pb_HeapBuffer)) + +typedef struct pb_HeapBuffer { + unsigned capacity; + char *buff; +} pb_HeapBuffer; typedef struct pb_Buffer { - size_t size; - size_t capacity; - char *buff; - char init_buff[PB_BUFFERSIZE]; + unsigned size : sizeof(unsigned)*CHAR_BIT - 1; + unsigned heap : 1; + union { + char buff[PB_SSO_SIZE]; + pb_HeapBuffer h; + } u; } pb_Buffer; -#define pb_buffer(b) ((b)->buff) -#define pb_bufflen(b) ((b)->size) -#define pb_addsize(b, sz) ((b)->size += (sz)) -#define pb_addchar(b, ch) \ - ((void)((b)->size < (b)->capacity || pb_prepbuffsize((b), 1)), \ - ((b)->buff[(b)->size++] = (ch))) +#define pb_onheap(b) ((b)->heap) +#define pb_bufflen(b) ((b)->size) +#define pb_buffer(b) (pb_onheap(b) ? (b)->u.h.buff : (b)->u.buff) +#define pb_addsize(b,sz) ((void)((b)->size += (unsigned)(sz))) -PB_API void pb_initbuffer (pb_Buffer *b); -PB_API void pb_resetbuffer (pb_Buffer *b); -PB_API size_t pb_resizebuffer (pb_Buffer *b, size_t len); -PB_API void *pb_prepbuffsize (pb_Buffer *b, size_t len); +PB_API void pb_initbuffer (pb_Buffer *b); +PB_API void pb_resetbuffer (pb_Buffer *b); +PB_API char *pb_prepbuffsize (pb_Buffer *b, size_t len); -PB_API pb_Slice pb_result (pb_Buffer *b); +PB_API pb_Slice pb_result (const pb_Buffer *b); PB_API size_t pb_addvarint32 (pb_Buffer *b, uint32_t v); PB_API size_t pb_addvarint64 (pb_Buffer *b, uint64_t v); @@ -185,16 +201,17 @@ PB_API size_t pb_addlength (pb_Buffer *b, size_t len); typedef struct pb_State pb_State; typedef struct pb_Name pb_Name; +typedef struct pb_Cache pb_Cache; PB_API void pb_init (pb_State *S); PB_API void pb_free (pb_State *S); -PB_API pb_Name *pb_newname (pb_State *S, pb_Slice s); -PB_API void pb_delname (pb_State *S, pb_Name *name); -PB_API pb_Name *pb_name (pb_State *S, const char *name); - +PB_API pb_Name *pb_newname (pb_State *S, pb_Slice s, pb_Cache *cache); +PB_API void pb_delname (pb_State *S, pb_Name *name); PB_API pb_Name *pb_usename (pb_Name *name); +PB_API const pb_Name *pb_name (const pb_State *S, pb_Slice s, pb_Cache *cache); + /* type info */ @@ -212,12 +229,14 @@ PB_API void pb_deltype (pb_State *S, pb_Type *t); PB_API pb_Field *pb_newfield (pb_State *S, pb_Type *t, pb_Name *fname, int32_t number); PB_API void pb_delfield (pb_State *S, pb_Type *t, pb_Field *f); -PB_API pb_Type *pb_type (pb_State *S, pb_Name *tname); -PB_API pb_Field *pb_fname (pb_Type *t, pb_Name *tname); -PB_API pb_Field *pb_field (pb_Type *t, int32_t number); +PB_API const pb_Type *pb_type (const pb_State *S, const pb_Name *tname); +PB_API const pb_Field *pb_fname (const pb_Type *t, const pb_Name *tname); +PB_API const pb_Field *pb_field (const pb_Type *t, int32_t number); + +PB_API const pb_Name *pb_oneofname (const pb_Type *t, int oneof_index); -PB_API int pb_nexttype (pb_State *S, pb_Type **ptype); -PB_API int pb_nextfield (pb_Type *t, pb_Field **pfield); +PB_API int pb_nexttype (const pb_State *S, const pb_Type **ptype); +PB_API int pb_nextfield (const pb_Type *t, const pb_Field **pfield); /* util: memory pool */ @@ -247,13 +266,14 @@ PB_API void pb_freetable (pb_Table *t); PB_API size_t pb_resizetable (pb_Table *t, size_t size); -PB_API pb_Entry *pb_gettable (pb_Table *t, pb_Key key); +PB_API pb_Entry *pb_gettable (const pb_Table *t, pb_Key key); PB_API pb_Entry *pb_settable (pb_Table *t, pb_Key key); -PB_API int pb_nextentry (pb_Table *t, pb_Entry **pentry); + +PB_API int pb_nextentry (const pb_Table *t, const pb_Entry **pentry); struct pb_Table { - size_t size; - size_t lastfree; + unsigned size; + unsigned lastfree; unsigned entry_size : sizeof(unsigned)*CHAR_BIT - 1; unsigned has_zero : 1; pb_Entry *hash; @@ -267,11 +287,13 @@ struct pb_Entry { /* fields */ +#define PB_CACHE_SIZE (53) + typedef struct pb_NameEntry { struct pb_NameEntry *next; - unsigned hash : 32; - unsigned length : 16; - unsigned refcount : 16; + unsigned hash : 32; + unsigned length : 16; + unsigned refcount : 16; } pb_NameEntry; typedef struct pb_NameTable { @@ -280,9 +302,19 @@ typedef struct pb_NameTable { pb_NameEntry **hash; } pb_NameTable; +typedef struct pb_CacheSlot { + const char *name; + unsigned hash; +} pb_CacheSlot; + +struct pb_Cache { + pb_CacheSlot slots[PB_CACHE_SIZE][2]; + unsigned hash; +}; + struct pb_State { - pb_Table types; pb_NameTable nametable; + pb_Table types; pb_Pool typepool; pb_Pool fieldpool; }; @@ -292,21 +324,24 @@ struct pb_Field { pb_Type *type; pb_Name *default_value; int32_t number; - unsigned type_id : 29; /* PB_T* enum */ - unsigned repeated : 1; - unsigned packed : 1; - unsigned scalar : 1; + unsigned oneof_idx : 24; + unsigned type_id : 5; /* PB_T* enum */ + unsigned repeated : 1; + unsigned packed : 1; + unsigned scalar : 1; }; struct pb_Type { pb_Name *name; const char *basename; - pb_Table field_tags; - pb_Table field_names; - pb_Table oneof_index; - unsigned field_count : 30; - unsigned is_enum : 1; - unsigned is_map : 1; + pb_Table field_tags; + pb_Table field_names; + pb_Table oneof_index; + unsigned field_count : 28; + unsigned is_enum : 1; + unsigned is_map : 1; + unsigned is_proto3 : 1; + unsigned is_dead : 1; }; @@ -318,7 +353,8 @@ PB_NS_END #if defined(PB_IMPLEMENTATION) && !defined(pb_implemented) #define pb_implemented -#define PB_MAX_SIZET ((size_t)~0 - 100) +#define PB_MAX_SIZET ((unsigned)~0 - 100) +#define PB_MAX_HASHSIZE ((unsigned)~0 - 100) #define PB_MIN_STRTABLE_SIZE 16 #define PB_MIN_HASHTABLE_SIZE 8 #define PB_HASHLIMIT 5 @@ -333,19 +369,19 @@ PB_NS_BEGIN /* conversions */ PB_API uint32_t pb_encode_sint32(int32_t value) -{ return ((uint32_t)value << 1) ^ (value >> 31); } +{ return ((uint32_t)value << 1) ^ -(value < 0); } PB_API int32_t pb_decode_sint32(uint32_t value) { return (value >> 1) ^ -(int32_t)(value & 1); } PB_API uint64_t pb_encode_sint64(int64_t value) -{ return ((uint64_t)value << 1) ^ (value >> 63); } +{ return ((uint64_t)value << 1) ^ -(value < 0); } PB_API int64_t pb_decode_sint64(uint64_t value) { return (value >> 1) ^ -(int64_t)(value & 1); } -PB_API uint64_t pb_expandsig(int32_t value) -{ return (int64_t)value; } +PB_API uint64_t pb_expandsig(uint32_t value) +{ uint64_t m = (uint64_t)1U << 31; return (value ^ m) - m; } PB_API uint32_t pb_encode_float(float value) { union { uint32_t u32; float f; } u; u.f = value; return u.u32; } @@ -363,13 +399,10 @@ PB_API double pb_decode_double(uint64_t value) /* decode */ PB_API pb_Slice pb_slice(const char *s) -{ return pb_lslice(s, strlen(s)); } +{ return s ? pb_lslice(s, strlen(s)) : pb_lslice(NULL, 0); } -PB_API pb_Slice pb_lslice(const char *s, size_t len) -{ pb_Slice slice; slice.p = s; slice.end = s + len; return slice; } - -PB_API size_t pb_len(pb_Slice s) -{ return s.end - s.p; } +PB_API size_t pb_pos(const pb_Slice s) { return s.p - s.start; } +PB_API size_t pb_len(const pb_Slice s) { return s.end - s.p; } static size_t pb_readvarint_slow(pb_Slice *s, uint64_t *pv) { const char *p = s->p; @@ -428,6 +461,13 @@ static size_t pb_readvarint64_fallback(pb_Slice *s, uint64_t *pv) { return p - o; } +PB_API pb_Slice pb_lslice(const char *s, size_t len) { + pb_Slice slice; + slice.start = slice.p = s; + slice.end = s + len; + return slice; +} + PB_API size_t pb_readvarint32(pb_Slice *s, uint32_t *pv) { uint64_t u64; size_t ret; @@ -479,8 +519,9 @@ PB_API size_t pb_readfixed64(pb_Slice *s, uint64_t *pv) { PB_API size_t pb_readslice(pb_Slice *s, size_t len, pb_Slice *pv) { if (pb_len(*s) < len) return 0; - pv->p = s->p; - pv->end = s->p + len; + pv->start = s->start; + pv->p = s->p; + pv->end = s->p + len; s->p = pv->end; return len; } @@ -492,6 +533,7 @@ PB_API size_t pb_readbytes(pb_Slice *s, pb_Slice *pv) { s->p = p; return 0; } + pv->start = s->start; pv->p = s->p; pv->end = s->p + len; s->p = pv->end; @@ -507,6 +549,7 @@ PB_API size_t pb_readgroup(pb_Slice *s, uint32_t tag, pb_Slice *pv) { if (pb_gettype(newtag) == PB_TGEND) { if (pb_gettag(newtag) != pb_gettag(tag)) break; + pv->start = s->start; pv->p = p; pv->end = s->p - count; return s->p - p; @@ -584,7 +627,7 @@ PB_API int pb_wtypebytype(int type) { PB_API const char *pb_wtypename(int wiretype, const char *def) { switch (wiretype) { -#define X(id, name, v) case v: return name; +#define X(id,name,fmt) case PB_T##id: return name; PB_WIRETYPES(X) #undef X default: return def ? def : ""; @@ -593,7 +636,7 @@ PB_API const char *pb_wtypename(int wiretype, const char *def) { PB_API const char *pb_typename(int type, const char *def) { switch (type) { -#define X(name, t, v) case v: return #name; +#define X(name,type,fmt) case PB_T##name: return #name; PB_TYPES(X) #undef X default: return def ? def : ""; @@ -602,7 +645,7 @@ PB_API const char *pb_typename(int type, const char *def) { PB_API int pb_typebyname(const char *name, int def) { static struct entry { const char *name; int value; } names[] = { -#define X(name, t, v) { #name, v }, +#define X(name,type,fmt) { #name, PB_T##name }, PB_TYPES(X) #undef X { NULL, 0 } @@ -616,7 +659,7 @@ PB_API int pb_typebyname(const char *name, int def) { PB_API int pb_wtypebyname(const char *name, int def) { static struct entry { const char *name; int value; } names[] = { -#define X(id, name, v) { name, v }, +#define X(id,name,fmt) { name, PB_T##id }, PB_WIRETYPES(X) #undef X { NULL, 0 } @@ -631,14 +674,14 @@ PB_API int pb_wtypebyname(const char *name, int def) { /* encode */ -PB_API pb_Slice pb_result(pb_Buffer *b) -{ pb_Slice slice = pb_lslice(b->buff, b->size); return slice; } +PB_API pb_Slice pb_result(const pb_Buffer *b) +{ pb_Slice slice = pb_lslice(pb_buffer(b), b->size); return slice; } PB_API void pb_initbuffer(pb_Buffer *b) -{ b->buff = b->init_buff, b->capacity = PB_BUFFERSIZE, b->size = 0; } +{ memset(b, 0, sizeof(pb_Buffer)); } PB_API void pb_resetbuffer(pb_Buffer *b) -{ if (b->buff != b->init_buff) xfree(b->buff); pb_initbuffer(b); } +{ if (pb_onheap(b)) free(b->u.h.buff); pb_initbuffer(b); } static int pb_write32(char *buff, uint32_t n) { int p, c = 0; @@ -669,36 +712,31 @@ static int pb_write64(char *buff, uint64_t n) { return *buff++ = p, ++c; } -PB_API size_t pb_resizebuffer(pb_Buffer *b, size_t len) { - size_t newsize = PB_BUFFERSIZE; - while (newsize < PB_MAX_SIZET/2 && newsize < len) - newsize += newsize >> 1; - if (newsize > b->size) { - char *newbuff = b->buff == b->init_buff ? NULL : b->buff; - newbuff = (char*)realloc(newbuff, newsize); - if (newbuff == NULL) return 0; - if (b->buff == b->init_buff) - memcpy(newbuff, b->buff, b->size); - b->buff = newbuff; - b->capacity = newsize; +PB_API char *pb_prepbuffsize(pb_Buffer *b, size_t len) { + size_t capacity = pb_onheap(b) ? b->u.h.capacity : PB_SSO_SIZE; + if (b->size + len > capacity) { + char *newp, *oldp = pb_onheap(b) ? b->u.h.buff : NULL; + size_t expected = b->size + len; + size_t newsize = PB_SSO_SIZE; + while (newsize < PB_MAX_SIZET/2 && newsize < expected) + newsize += newsize >> 1; + if (newsize < expected) return NULL; + if ((newp = (char*)realloc(oldp, newsize)) == NULL) return NULL; + if (!pb_onheap(b)) memcpy(newp, pb_buffer(b), b->size); + b->heap = 1; + b->u.h.buff = newp; + b->u.h.capacity = (unsigned)newsize; } - return b->capacity; -} - -PB_API void* pb_prepbuffsize(pb_Buffer *b, size_t len) { - if (b->size + len > b->capacity) { - size_t oldsize = b->size; - if (pb_resizebuffer(b, oldsize + len) == 0) - return NULL; - } - return &b->buff[b->size]; + return &pb_buffer(b)[b->size]; } PB_API size_t pb_addslice(pb_Buffer *b, pb_Slice s) { size_t len = pb_len(s); - void *p = pb_prepbuffsize(b, len); - memcpy(p, s.p, len); - return pb_addsize(b, len); + char *buff = pb_prepbuffsize(b, len); + if (buff == NULL) return 0; + memcpy(buff, s.p, len); + pb_addsize(b, len); + return len; } PB_API size_t pb_addlength(pb_Buffer *b, size_t len) { @@ -707,8 +745,8 @@ PB_API size_t pb_addlength(pb_Buffer *b, size_t len) { if ((bl = pb_bufflen(b)) < len) return 0; ml = pb_write64(buff, bl - len); - if (pb_prepbuffsize(b, ml) == 0) return 0; - s = b->buff + len; + if (pb_prepbuffsize(b, ml) == NULL) return 0; + s = pb_buffer(b) + len; memmove(s+ml, s, bl - len); memcpy(s, buff, ml); pb_addsize(b, ml); @@ -723,29 +761,34 @@ PB_API size_t pb_addbytes(pb_Buffer *b, pb_Slice s) { } PB_API size_t pb_addvarint32(pb_Buffer *b, uint32_t n) { - char *buff = (char*)pb_prepbuffsize(b, 5); + char *buff = pb_prepbuffsize(b, 5); + size_t l; if (buff == NULL) return 0; - return pb_addsize(b, pb_write32(buff, n)); + pb_addsize(b, l = pb_write32(buff, n)); + return l; } PB_API size_t pb_addvarint64(pb_Buffer *b, uint64_t n) { - char *buff = (char*)pb_prepbuffsize(b, 10); + char *buff = pb_prepbuffsize(b, 10); + size_t l; if (buff == NULL) return 0; - return pb_addsize(b, pb_write64(buff, n)); + pb_addsize(b, l = pb_write64(buff, n)); + return l; } PB_API size_t pb_addfixed32(pb_Buffer *b, uint32_t n) { - char *ch = (char*)pb_prepbuffsize(b, 4); + char *ch = pb_prepbuffsize(b, 4); if (ch == NULL) return 0; *ch++ = n & 0xFF; n >>= 8; *ch++ = n & 0xFF; n >>= 8; *ch++ = n & 0xFF; n >>= 8; *ch = n & 0xFF; - return pb_addsize(b, 4); + pb_addsize(b, 4); + return 4; } PB_API size_t pb_addfixed64(pb_Buffer *b, uint64_t n) { - char *ch = (char*)pb_prepbuffsize(b, 8); + char *ch = pb_prepbuffsize(b, 8); if (ch == NULL) return 0; *ch++ = n & 0xFF; n >>= 8; *ch++ = n & 0xFF; n >>= 8; @@ -755,7 +798,8 @@ PB_API size_t pb_addfixed64(pb_Buffer *b, uint64_t n) { *ch++ = n & 0xFF; n >>= 8; *ch++ = n & 0xFF; n >>= 8; *ch = n & 0xFF; - return pb_addsize(b, 8); + pb_addsize(b, 8); + return 8; } @@ -771,7 +815,7 @@ PB_API void pb_freepool(pb_Pool *pool) { void *page = pool->pages; while (page) { void *next = *(void**)((char*)page + PB_POOLSIZE - sizeof(void*)); - xfree(page); + free(page); page = next; } pb_initpool(pool, pool->obj_size); @@ -802,43 +846,40 @@ PB_API void pb_poolfree(pb_Pool *pool, void *obj) /* hash table */ -#define pbT_offset(a, b) ((char*)(a) - (char*)(b)) -#define pbT_index(a, b) ((pb_Entry*)((char*)(a) + (b))) +#define pbT_offset(a,b) ((char*)(a) - (char*)(b)) +#define pbT_index(a,b) ((pb_Entry*)((char*)(a) + (b))) PB_API void pb_inittable(pb_Table *t, size_t entrysize) -{ memset(t, 0, sizeof(pb_Table)), t->entry_size = entrysize; } +{ memset(t, 0, sizeof(pb_Table)), t->entry_size = (unsigned)entrysize; } PB_API void pb_freetable(pb_Table *t) -{ xfree(t->hash); pb_inittable(t, t->entry_size); } +{ free(t->hash); pb_inittable(t, t->entry_size); } -static pb_Entry *pbT_hash(pb_Table *t, pb_Key key) { - size_t h = ((size_t)key*2654435761)&(t->size-1); +static pb_Entry *pbT_hash(const pb_Table *t, pb_Key key) { + size_t h = ((size_t)key*2654435761U)&(t->size-1); if (key && h == 0) h = 1; return pbT_index(t->hash, h*t->entry_size); } static pb_Entry *pbT_newkey(pb_Table *t, pb_Key key) { - pb_Entry *mp, *othern, *next, *f = NULL; - if (t->size == 0 && pb_resizetable(t, t->size*2) == 0) return NULL; + pb_Entry *mp, *on, *next, *f = NULL; + if (t->size == 0 && pb_resizetable(t, (size_t)t->size*2) == 0) return NULL; if (key == 0) { mp = t->hash; t->has_zero = 1; - } - else if ((mp = pbT_hash(t, key))->key != 0) { + } else if ((mp = pbT_hash(t, key))->key != 0) { while (t->lastfree > t->entry_size) { pb_Entry *cur = pbT_index(t->hash, t->lastfree -= t->entry_size); if (cur->key == 0 && cur->next == 0) { f = cur; break; } } - if (f == NULL) return pb_resizetable(t, t->size*2) ? + if (f == NULL) return pb_resizetable(t, (size_t)t->size*2u) ? pbT_newkey(t, key) : NULL; - if ((othern = pbT_hash(t, mp->key)) != mp) { - while ((next = pbT_index(othern, othern->next)) != mp) - othern = next; - othern->next = pbT_offset(f, othern); + if ((on = pbT_hash(t, mp->key)) != mp) { + while ((next = pbT_index(on, on->next)) != mp) on = next; + on->next = pbT_offset(f, on); memcpy(f, mp, t->entry_size); if (mp->next != 0) f->next += pbT_offset(mp, f), mp->next = 0; - } - else { + } else { if (mp->next != 0) f->next = pbT_offset(mp, f) + mp->next; else assert(f->next == 0); mp->next = pbT_offset(f, mp); @@ -853,9 +894,9 @@ static pb_Entry *pbT_newkey(pb_Table *t, pb_Key key) { PB_API size_t pb_resizetable(pb_Table *t, size_t size) { pb_Table nt = *t; - size_t i, rawsize = t->size*t->entry_size; - size_t newsize = PB_MIN_HASHTABLE_SIZE; - while (newsize < PB_MAX_SIZET/t->entry_size && newsize < size) + unsigned i, rawsize = t->size*t->entry_size; + unsigned newsize = PB_MIN_HASHTABLE_SIZE; + while (newsize < PB_MAX_HASHSIZE/t->entry_size && newsize < size) newsize <<= 1; if (newsize < size) return 0; nt.size = newsize; @@ -869,12 +910,12 @@ PB_API size_t pb_resizetable(pb_Table *t, size_t size) { if (nt.entry_size > sizeof(pb_Entry)) memcpy(newe+1, olde+1, nt.entry_size - sizeof(pb_Entry)); } - xfree(t->hash); + free(t->hash); *t = nt; return newsize; } -PB_API pb_Entry *pb_gettable(pb_Table *t, pb_Key key) { +PB_API pb_Entry *pb_gettable(const pb_Table *t, pb_Key key) { pb_Entry *entry; if (t == NULL || t->size == 0) return NULL; @@ -894,9 +935,9 @@ PB_API pb_Entry *pb_settable(pb_Table *t, pb_Key key) { return pbT_newkey(t, key); } -PB_API int pb_nextentry(pb_Table *t, pb_Entry **pentry) { +PB_API int pb_nextentry(const pb_Table *t, const pb_Entry **pentry) { size_t i = *pentry ? pbT_offset(*pentry, t->hash) : 0; - size_t size = t->size*t->entry_size; + size_t size = (size_t)t->size*t->entry_size; if (*pentry == NULL && t->has_zero) { *pentry = t->hash; return 1; @@ -918,6 +959,9 @@ PB_API int pb_nextentry(pb_Table *t, pb_Entry **pentry) { static void pbN_init(pb_State *S) { memset(&S->nametable, 0, sizeof(pb_NameTable)); } +PB_API pb_Name *pb_usename(pb_Name *name) +{ if (name != NULL) ++((pb_NameEntry*)name-1)->refcount; return name; } + static void pbN_free(pb_State *S) { pb_NameTable *nt = &S->nametable; size_t i; @@ -925,19 +969,20 @@ static void pbN_free(pb_State *S) { pb_NameEntry *ne = nt->hash[i]; while (ne != NULL) { pb_NameEntry *next = ne->next; - xfree(ne); + free(ne); ne = next; } } - xfree(nt->hash); + free(nt->hash); pbN_init(S); } -static unsigned pbN_calchash(const char *s, size_t len) { +static unsigned pbN_calchash(pb_Slice s) { + size_t len = pb_len(s); unsigned h = (unsigned)len; size_t step = (len >> PB_HASHLIMIT) + 1; for (; len >= step; len -= step) - h ^= ((h<<5) + (h>>2) + (unsigned char)(s[len - 1])); + h ^= ((h<<5) + (h>>2) + (unsigned char)(s.p[len - 1])); return h; } @@ -945,7 +990,7 @@ static size_t pbN_resize(pb_State *S, size_t size) { pb_NameTable *nt = &S->nametable; pb_NameEntry **hash; size_t i, newsize = PB_MIN_STRTABLE_SIZE; - while (newsize < PB_MAX_SIZET/sizeof(pb_NameEntry*) && newsize < size) + while (newsize < PB_MAX_HASHSIZE/sizeof(pb_NameEntry*) && newsize < size) newsize <<= 1; if (newsize < size) return 0; hash = (pb_NameEntry**)malloc(newsize * sizeof(pb_NameEntry*)); @@ -960,24 +1005,25 @@ static size_t pbN_resize(pb_State *S, size_t size) { entry = next; } } - xfree(nt->hash); + free(nt->hash); nt->hash = hash; nt->size = newsize; return newsize; } -static pb_NameEntry *pbN_newname(pb_State *S, const char *name, size_t len, unsigned hash) { +static pb_NameEntry *pbN_newname(pb_State *S, pb_Slice s, unsigned hash) { pb_NameTable *nt = &S->nametable; pb_NameEntry **list, *newobj; + size_t len = pb_len(s); if (nt->count >= nt->size && !pbN_resize(S, nt->size * 2)) return NULL; list = &nt->hash[hash & (nt->size - 1)]; newobj = (pb_NameEntry*)malloc(sizeof(pb_NameEntry) + len + 1); if (newobj == NULL) return NULL; - newobj->next = *list; - newobj->length = (unsigned)len; - newobj->refcount = 1; - newobj->hash = hash; - memcpy(newobj+1, name, len); + newobj->next = *list; + newobj->length = (unsigned)len; + newobj->refcount = 0; + newobj->hash = hash; + memcpy(newobj+1, s.p, len); ((char*)(newobj+1))[len] = '\0'; *list = newobj; ++nt->count; @@ -992,61 +1038,71 @@ static void pbN_delname(pb_State *S, pb_NameEntry *name) { list = &(*list)->next; else { *list = (*list)->next; - xfree(name); --nt->count; + free(name); break; } } } -static pb_NameEntry *pbN_getname(pb_State *S, const char *name, size_t len, unsigned hash) { - pb_NameTable *nt = &S->nametable; +static pb_NameEntry *pbN_getname(const pb_State *S, pb_Slice s, unsigned hash) { + const pb_NameTable *nt = &S->nametable; + size_t len = pb_len(s); if (nt->hash) { pb_NameEntry *entry = nt->hash[hash & (nt->size - 1)]; for (; entry != NULL; entry = entry->next) if (entry->hash == hash && entry->length == len - && memcmp(name, entry + 1, len) == 0) + && memcmp(s.p, entry + 1, len) == 0) return entry; } return NULL; } -PB_API pb_Name *pb_newname(pb_State *S, pb_Slice s) { - if (s.p != NULL) { - size_t size = pb_len(s); - const char *name = s.p; - unsigned hash = pbN_calchash(name, size); - pb_NameEntry *entry = pbN_getname(S, name, size, hash); - if (entry) return pb_usename((pb_Name*)(entry + 1)); - entry = pbN_newname(S, name, size, hash); - return entry ? (pb_Name*)(entry + 1) : NULL; - } - return NULL; -} - -PB_API pb_Name *pb_usename(pb_Name *name) { - if (name != NULL) - ++((pb_NameEntry*)name-1)->refcount; - return name; -} - PB_API void pb_delname(pb_State *S, pb_Name *name) { - pb_NameEntry *ne = (pb_NameEntry*)name - 1; if (name != NULL) { - if (ne->refcount <= 1) - { pbN_delname(S, ne); return; } + pb_NameEntry *ne = (pb_NameEntry*)name - 1; + if (ne->refcount <= 1) { pbN_delname(S, ne); return; } --ne->refcount; } } -PB_API pb_Name *pb_name(pb_State *S, const char *name) { - if (name != NULL) { - size_t size = strlen(name); - unsigned hash = pbN_calchash(name, size); - pb_NameEntry *entry = pbN_getname(S, name, size, hash); - return entry ? (pb_Name*)(entry + 1) : NULL; +PB_API pb_Name *pb_newname(pb_State *S, pb_Slice s, pb_Cache *cache) { + pb_NameEntry *entry; + if (s.p == NULL) return NULL; + (void)cache; + assert(cache == NULL); + /* if (cache == NULL) */{ + unsigned hash = pbN_calchash(s); + entry = pbN_getname(S, s, hash); + if (entry == NULL) entry = pbN_newname(S, s, hash); + }/* else { + pb_Name *name = (pb_Name*)pb_name(S, s, cache); + if (name) return pb_usename(name); + entry = pbN_newname(S, s, cache->hash); + }*/ + return entry ? pb_usename((pb_Name*)(entry + 1)) : NULL; +} + +PB_API const pb_Name *pb_name(const pb_State *S, pb_Slice s, pb_Cache *cache) { + pb_NameEntry *entry = NULL; + pb_CacheSlot *slot; + if (s.p == NULL) return NULL; + if (cache == NULL) + entry = pbN_getname(S, s, pbN_calchash(s)); + else { + slot = cache->slots[((uintptr_t)s.p*2654435761U)%PB_CACHE_SIZE]; + if (slot[0].name == s.p) + entry = pbN_getname(S, s, cache->hash = slot[0].hash); + else if (slot[1].name == s.p) + entry = pbN_getname(S, s, cache->hash = (++slot)[0].hash); + else + slot[1] = slot[0], slot[0].name = s.p; + if (entry == NULL) { + cache->hash = slot[0].hash = pbN_calchash(s); + entry = pbN_getname(S, s, slot[0].hash); + } } - return NULL; + return entry ? (pb_Name*)(entry + 1) : NULL; } @@ -1069,56 +1125,61 @@ PB_API void pb_init(pb_State *S) { } PB_API void pb_free(pb_State *S) { - if (S != NULL) { - pb_TypeEntry *te = NULL; - while (pb_nextentry(&S->types, (pb_Entry**)&te)) - if (te->value != NULL) pb_deltype(S, te->value); - pb_freetable(&S->types); - pb_freepool(&S->typepool); - pb_freepool(&S->fieldpool); - pbN_free(S); - } + const pb_TypeEntry *te = NULL; + if (S == NULL) return; + while (pb_nextentry(&S->types, (const pb_Entry**)&te)) + if (te->value != NULL) pb_deltype(S, te->value); + pb_freetable(&S->types); + pb_freepool(&S->typepool); + pb_freepool(&S->fieldpool); + pbN_free(S); } -PB_API pb_Type *pb_type(pb_State *S, pb_Name *tname) { +PB_API const pb_Type *pb_type(const pb_State *S, const pb_Name *tname) { pb_TypeEntry *te = NULL; if (S != NULL && tname != NULL) te = (pb_TypeEntry*)pb_gettable(&S->types, (pb_Key)tname); - return te ? te->value : NULL; + return te && !te->value->is_dead ? te->value : NULL; } -PB_API pb_Field *pb_fname(pb_Type *t, pb_Name *name) { +PB_API const pb_Field *pb_fname(const pb_Type *t, const pb_Name *name) { pb_FieldEntry *fe = NULL; if (t != NULL && name != NULL) fe = (pb_FieldEntry*)pb_gettable(&t->field_names, (pb_Key)name); return fe ? fe->value : NULL; } -PB_API pb_Field *pb_field(pb_Type *t, int32_t number) { +PB_API const pb_Field *pb_field(const pb_Type *t, int32_t number) { pb_FieldEntry *fe = NULL; if (t != NULL) fe = (pb_FieldEntry*)pb_gettable(&t->field_tags, number); return fe ? fe->value : NULL; } -PB_API int pb_nexttype(pb_State *S, pb_Type **ptype) { - pb_TypeEntry *e = NULL; +PB_API const pb_Name *pb_oneofname(const pb_Type *t, int idx) { + pb_OneofEntry *oe = NULL; + if (t != NULL) oe = (pb_OneofEntry*)pb_gettable(&t->oneof_index, idx); + return oe ? oe->name : NULL; +} + +PB_API int pb_nexttype(const pb_State *S, const pb_Type **ptype) { + const pb_TypeEntry *e = NULL; if (S != NULL) { if (*ptype != NULL) e = (pb_TypeEntry*)pb_gettable(&S->types, (pb_Key)(*ptype)->name); - while (pb_nextentry(&S->types, (pb_Entry**)&e)) - if ((*ptype = e->value) != NULL) + while (pb_nextentry(&S->types, (const pb_Entry**)&e)) + if ((*ptype = e->value) != NULL && !(*ptype)->is_dead) return 1; } *ptype = NULL; return 0; } -PB_API int pb_nextfield(pb_Type *t, pb_Field **pfield) { - pb_FieldEntry *e = NULL; +PB_API int pb_nextfield(const pb_Type *t, const pb_Field **pfield) { + const pb_FieldEntry *e = NULL; if (t != NULL) { if (*pfield != NULL) e = (pb_FieldEntry*)pb_gettable(&t->field_tags, (*pfield)->number); - while (pb_nextentry(&t->field_tags, (pb_Entry**)&e)) + while (pb_nextentry(&t->field_tags, (const pb_Entry**)&e)) if ((*pfield = e->value) != NULL) return 1; } @@ -1150,25 +1211,24 @@ static void pbT_freefield(pb_State *S, pb_Field *f) { } PB_API pb_Type *pb_newtype(pb_State *S, pb_Name *tname) { - if (tname != NULL) { - pb_TypeEntry *te = (pb_TypeEntry*)pb_settable( - &S->types, (pb_Key)tname); - pb_Type *t; - if (te == NULL) return NULL; - if ((t = te->value) != NULL) return t; - if (!(t = (pb_Type*)pb_poolalloc(&S->typepool))) return NULL; - pbT_inittype(t); - t->name = tname; - t->basename = pbT_basename((const char*)tname); - return te->value = t; - } - return NULL; + pb_TypeEntry *te; + pb_Type *t; + if (tname == NULL) return NULL; + te = (pb_TypeEntry*)pb_settable(&S->types, (pb_Key)tname); + if (te == NULL) return NULL; + if ((t = te->value) != NULL) { t->is_dead = 0; return t; } + if (!(t = (pb_Type*)pb_poolalloc(&S->typepool))) return NULL; + pbT_inittype(t); + t->name = tname; + t->basename = pbT_basename((const char*)tname); + return te->value = t; } PB_API void pb_deltype(pb_State *S, pb_Type *t) { pb_FieldEntry *nf = NULL; pb_OneofEntry *ne = NULL; - while (pb_nextentry(&t->field_names, (pb_Entry**)&nf)) { + if (S == NULL || t == NULL) return; + while (pb_nextentry(&t->field_names, (const pb_Entry**)&nf)) { if (nf->value != NULL) { pb_FieldEntry *of = (pb_FieldEntry*)pb_gettable( &t->field_tags, nf->value->number); @@ -1177,62 +1237,58 @@ PB_API void pb_deltype(pb_State *S, pb_Type *t) { pbT_freefield(S, nf->value); } } - while (pb_nextentry(&t->field_tags, (pb_Entry**)&nf)) + while (pb_nextentry(&t->field_tags, (const pb_Entry**)&nf)) if (nf->value != NULL) pbT_freefield(S, nf->value); - while (pb_nextentry(&t->oneof_index, (pb_Entry**)&ne)) + while (pb_nextentry(&t->oneof_index, (const pb_Entry**)&ne)) pb_delname(S, ne->name); pb_freetable(&t->field_tags); pb_freetable(&t->field_names); pb_freetable(&t->oneof_index); t->field_count = 0; + t->is_dead = 1; /*pb_delname(S, t->name); */ /*pb_poolfree(&S->typepool, t); */ } PB_API pb_Field *pb_newfield(pb_State *S, pb_Type *t, pb_Name *fname, int32_t number) { - if (fname != NULL) { - pb_FieldEntry *nf = (pb_FieldEntry*)pb_settable( - &t->field_names, (pb_Key)fname); - pb_FieldEntry *tf = (pb_FieldEntry*)pb_settable( - &t->field_tags, number); - pb_Field *f; - if (nf == NULL || tf == NULL) return NULL; - if ((f = nf->value) != NULL && tf->value == f) { - pb_delname(S, f->default_value); - f->default_value = NULL; - return f; - } - if (!(f = (pb_Field*)pb_poolalloc(&S->typepool))) return NULL; - memset(f, 0, sizeof(pb_Field)); - f->name = fname; - f->type = t; - f->number = number; - if (nf->value && pb_field(t, nf->value->number) != nf->value) - pbT_freefield(S, nf->value), --t->field_count; - if (tf->value && pb_fname(t, tf->value->name) != tf->value) - pbT_freefield(S, tf->value), --t->field_count; - ++t->field_count; - return nf->value = tf->value = f; + pb_FieldEntry *nf, *tf; + pb_Field *f; + if (fname == NULL) return NULL; + nf = (pb_FieldEntry*)pb_settable(&t->field_names, (pb_Key)fname); + tf = (pb_FieldEntry*)pb_settable(&t->field_tags, number); + if (nf == NULL || tf == NULL) return NULL; + if ((f = nf->value) != NULL && tf->value == f) { + pb_delname(S, f->default_value); + f->default_value = NULL; + return f; } - return NULL; + if (!(f = (pb_Field*)pb_poolalloc(&S->fieldpool))) return NULL; + memset(f, 0, sizeof(pb_Field)); + f->name = fname; + f->type = t; + f->number = number; + if (nf->value && pb_field(t, nf->value->number) != nf->value) + pbT_freefield(S, nf->value), --t->field_count; + if (tf->value && pb_fname(t, tf->value->name) != tf->value) + pbT_freefield(S, tf->value), --t->field_count; + ++t->field_count; + return nf->value = tf->value = f; } PB_API void pb_delfield(pb_State *S, pb_Type *t, pb_Field *f) { - pb_FieldEntry *nf = (pb_FieldEntry*)pb_gettable(&t->field_names, - (pb_Key)f->name); - pb_FieldEntry *tf = (pb_FieldEntry*)pb_gettable(&t->field_tags, - (pb_Key)f->number); + pb_FieldEntry *nf, *tf; int count = 0; + if (S == NULL || t == NULL || f == NULL) return; + nf = (pb_FieldEntry*)pb_gettable(&t->field_names, (pb_Key)f->name); + tf = (pb_FieldEntry*)pb_gettable(&t->field_tags, (pb_Key)f->number); if (nf && nf->value == f) nf->entry.key = 0, nf->value = NULL, ++count; - if (tf && tf->value == f) tf->entry.key = 0, nf->value = NULL, ++count; + if (tf && tf->value == f) tf->entry.key = 0, tf->value = NULL, ++count; if (count) pbT_freefield(S, f), --t->field_count; } /* .pb proto loader */ -#include - typedef struct pb_Loader pb_Loader; typedef struct pbL_FieldInfo pbL_FieldInfo; typedef struct pbL_EnumValueInfo pbL_EnumValueInfo; @@ -1240,16 +1296,22 @@ typedef struct pbL_EnumInfo pbL_EnumInfo; typedef struct pbL_TypeInfo pbL_TypeInfo; typedef struct pbL_FileInfo pbL_FileInfo; -#define pbL_rawh(A) ((size_t*)(A) - 2) -#define pbL_size(A) ((A) ? pbL_rawh(A)[0] : 0) -#define pbL_count(A) ((A) ? pbL_rawh(A)[1] : 0) -#define pbL_add(A) (pbL_grow(L, (void**)&(A), sizeof(*(A))), &(A)[pbL_rawh(A)[1]++]) -#define pbL_delete(A) ((A) ? (void)xfree(pbL_rawh(A)) : (void)0) +#define pbC(e) do { int r = (e); if (r != PB_OK) return r; } while (0) +#define pbCM(e) do { if ((e) == NULL) return PB_ENOMEM; } while (0) +#define pbCE(e) do { if ((e) == NULL) return PB_ERROR; } while (0) + +typedef struct pb_ArrayHeader { + unsigned count; + unsigned capacity; +} pb_ArrayHeader; -static void pbL_DescriptorProto (pb_Loader *L, pbL_TypeInfo *info); +#define pbL_rawh(A) ((pb_ArrayHeader*)(A) - 1) +#define pbL_delete(A) ((A) ? (void)free(pbL_rawh(A)) : (void)0) +#define pbL_count(A) ((A) ? pbL_rawh(A)->count : 0) +#define pbL_add(A) (pbL_grow((void**)&(A),sizeof(*(A)))==PB_OK ?\ + &(A)[pbL_rawh(A)->count++] : NULL) struct pb_Loader { - jmp_buf jbuf; pb_Slice s; pb_Buffer b; int is_proto3; @@ -1297,206 +1359,210 @@ struct pbL_FileInfo { pbL_FieldInfo *extension; }; -static void pbL_grow(pb_Loader *L, void **pp, size_t obj_size) { - enum { SIZE, COUNT, FIELDS }; - size_t *h = *pp ? pbL_rawh(*pp) : NULL; - if (h == NULL || h[SIZE] <= h[COUNT]) { - size_t newsize = (h ? h[SIZE] : 1) * 2; - size_t used = (h ? h[COUNT] : 0); - size_t *nh = (size_t*)realloc(h, sizeof(size_t)*2 + newsize*obj_size); - if (nh == NULL) longjmp(L->jbuf, PB_ENOMEM); - nh[SIZE] = newsize; - nh[COUNT] = used; - *pp = nh + FIELDS; - memset((char*)*pp + used*obj_size, 0, (newsize - used)*obj_size); +static int pbL_readbytes(pb_Loader *L, pb_Slice *pv) +{ return pb_readbytes(&L->s, pv) == 0 ? PB_ERROR : PB_OK; } + +static int pbL_beginmsg(pb_Loader *L, pb_Slice *pv) +{ pb_Slice v; pbC(pbL_readbytes(L, &v)); *pv = L->s, L->s = v; return PB_OK; } + +static void pbL_endmsg(pb_Loader *L, pb_Slice *pv) +{ L->s = *pv; } + +static int pbL_grow(void **pp, size_t objs) { + pb_ArrayHeader *nh, *h = *pp ? pbL_rawh(*pp) : NULL; + if (h == NULL || h->capacity <= h->count) { + size_t used = (h ? h->count : 0); + size_t size = used + 4, nsize = size + (size >> 1); + nh = nsize < size ? NULL : + (pb_ArrayHeader*)realloc(h, sizeof(pb_ArrayHeader)+nsize*objs); + if (nh == NULL) return PB_ENOMEM; + nh->count = (unsigned)used; + nh->capacity = (unsigned)nsize; + *pp = nh + 1; + memset((char*)*pp + used*objs, 0, (nsize - used)*objs); } + return PB_OK; } -static void pbL_readbytes(pb_Loader *L, pb_Slice *pv) { - if (pb_readbytes(&L->s, pv) == 0) - longjmp(L->jbuf, 1); -} - -static void pbL_readint32(pb_Loader *L, int32_t *pv) { +static int pbL_readint32(pb_Loader *L, int32_t *pv) { uint32_t v; - if (pb_readvarint32(&L->s, &v) == 0) - longjmp(L->jbuf, 1); + if (pb_readvarint32(&L->s, &v) == 0) return PB_ERROR; *pv = (int32_t)v; + return PB_OK; } -static void pbL_beginmsg(pb_Loader *L, pb_Slice *pv) { - pb_Slice v; - pbL_readbytes(L, &v); - *pv = L->s, L->s = v; -} - -static void pbL_endmsg(pb_Loader *L, pb_Slice *pv) { - L->s = *pv; -} - -static void pbL_FieldOptions(pb_Loader *L, pbL_FieldInfo *info) { +static int pbL_FieldOptions(pb_Loader *L, pbL_FieldInfo *info) { pb_Slice s; - uint32_t tag = 0; - pbL_beginmsg(L, &s); + uint32_t tag; + pbC(pbL_beginmsg(L, &s)); while (pb_readvarint32(&L->s, &tag)) { switch (tag) { case pb_pair(2, PB_TVARINT): /* bool packed */ - pbL_readint32(L, &info->packed); break; + pbC(pbL_readint32(L, &info->packed)); break; default: pb_skipvalue(&L->s, tag); } } pbL_endmsg(L, &s); + return PB_OK; } -static void pbL_FieldDescriptorProto(pb_Loader *L, pbL_FieldInfo *info) { +static int pbL_FieldDescriptorProto(pb_Loader *L, pbL_FieldInfo *info) { pb_Slice s; - uint32_t tag = 0; - pbL_beginmsg(L, &s); + uint32_t tag; + pbCM(info); pbC(pbL_beginmsg(L, &s)); info->packed = -1; while (pb_readvarint32(&L->s, &tag)) { switch (tag) { case pb_pair(1, PB_TBYTES): /* string name */ - pbL_readbytes(L, &info->name); break; + pbC(pbL_readbytes(L, &info->name)); break; case pb_pair(3, PB_TVARINT): /* int32 number */ - pbL_readint32(L, &info->number); break; + pbC(pbL_readint32(L, &info->number)); break; case pb_pair(4, PB_TVARINT): /* Label label */ - pbL_readint32(L, &info->label); break; + pbC(pbL_readint32(L, &info->label)); break; case pb_pair(5, PB_TVARINT): /* Type type */ - pbL_readint32(L, &info->type); break; + pbC(pbL_readint32(L, &info->type)); break; case pb_pair(6, PB_TBYTES): /* string type_name */ - pbL_readbytes(L, &info->type_name); break; + pbC(pbL_readbytes(L, &info->type_name)); break; case pb_pair(2, PB_TBYTES): /* string extendee */ - pbL_readbytes(L, &info->extendee); break; + pbC(pbL_readbytes(L, &info->extendee)); break; case pb_pair(7, PB_TBYTES): /* string default_value */ - pbL_readbytes(L, &info->default_value); break; + pbC(pbL_readbytes(L, &info->default_value)); break; case pb_pair(8, PB_TBYTES): /* FieldOptions options */ - pbL_FieldOptions(L, info); break; + pbC(pbL_FieldOptions(L, info)); break; case pb_pair(9, PB_TVARINT): /* int32 oneof_index */ - pbL_readint32(L, &info->oneof_index); + pbC(pbL_readint32(L, &info->oneof_index)); ++info->oneof_index; break; default: pb_skipvalue(&L->s, tag); } } pbL_endmsg(L, &s); + return PB_OK; } -static void pbL_EnumValueDescriptorProto(pb_Loader *L, pbL_EnumValueInfo *info) { +static int pbL_EnumValueDescriptorProto(pb_Loader *L, pbL_EnumValueInfo *info) { pb_Slice s; - uint32_t tag = 0; - pbL_beginmsg(L, &s); + uint32_t tag; + pbCM(info); pbC(pbL_beginmsg(L, &s)); while (pb_readvarint32(&L->s, &tag)) { switch (tag) { case pb_pair(1, PB_TBYTES): /* string name */ - pbL_readbytes(L, &info->name); break; + pbC(pbL_readbytes(L, &info->name)); break; case pb_pair(2, PB_TVARINT): /* int32 number */ - pbL_readint32(L, &info->number); break; + pbC(pbL_readint32(L, &info->number)); break; default: pb_skipvalue(&L->s, tag); } } pbL_endmsg(L, &s); + return PB_OK; } -static void pbL_EnumDescriptorProto(pb_Loader *L, pbL_EnumInfo *info) { +static int pbL_EnumDescriptorProto(pb_Loader *L, pbL_EnumInfo *info) { pb_Slice s; - uint32_t tag = 0; - pbL_beginmsg(L, &s); + uint32_t tag; + pbCM(info); pbC(pbL_beginmsg(L, &s)); while (pb_readvarint32(&L->s, &tag)) { switch (tag) { case pb_pair(1, PB_TBYTES): /* string name */ - pbL_readbytes(L, &info->name); break; + pbC(pbL_readbytes(L, &info->name)); break; case pb_pair(2, PB_TBYTES): /* EnumValueDescriptorProto value */ - pbL_EnumValueDescriptorProto(L, pbL_add(info->value)); break; + pbC(pbL_EnumValueDescriptorProto(L, pbL_add(info->value))); break; default: pb_skipvalue(&L->s, tag); } } pbL_endmsg(L, &s); + return PB_OK; } -static void pbL_MessageOptions(pb_Loader *L, pbL_TypeInfo *info) { +static int pbL_MessageOptions(pb_Loader *L, pbL_TypeInfo *info) { pb_Slice s; - uint32_t tag = 0; - pbL_beginmsg(L, &s); + uint32_t tag; + pbCM(info); pbC(pbL_beginmsg(L, &s)); while (pb_readvarint32(&L->s, &tag)) { switch (tag) { case pb_pair(7, PB_TVARINT): /* bool map_entry */ - pbL_readint32(L, &info->is_map); break; + pbC(pbL_readint32(L, &info->is_map)); break; default: pb_skipvalue(&L->s, tag); } } pbL_endmsg(L, &s); + return PB_OK; } -static void pbL_OneofDescriptorProto(pb_Loader *L, pbL_TypeInfo *info) { +static int pbL_OneofDescriptorProto(pb_Loader *L, pbL_TypeInfo *info) { pb_Slice s; - uint32_t tag = 0; - pbL_beginmsg(L, &s); + uint32_t tag; + pbCM(info); pbC(pbL_beginmsg(L, &s)); while (pb_readvarint32(&L->s, &tag)) { switch (tag) { case pb_pair(1, PB_TBYTES): /* string name */ - pbL_readbytes(L, pbL_add(info->oneof_decl)); break; + pbC(pbL_readbytes(L, pbL_add(info->oneof_decl))); break; default: pb_skipvalue(&L->s, tag); } } pbL_endmsg(L, &s); + return PB_OK; } -static void pbL_DescriptorProto(pb_Loader *L, pbL_TypeInfo *info) { +static int pbL_DescriptorProto(pb_Loader *L, pbL_TypeInfo *info) { pb_Slice s; - uint32_t tag = 0; - pbL_beginmsg(L, &s); + uint32_t tag; + pbCM(info); pbC(pbL_beginmsg(L, &s)); while (pb_readvarint32(&L->s, &tag)) { switch (tag) { case pb_pair(1, PB_TBYTES): /* string name */ - pbL_readbytes(L, &info->name); break; + pbC(pbL_readbytes(L, &info->name)); break; case pb_pair(2, PB_TBYTES): /* FieldDescriptorProto field */ - pbL_FieldDescriptorProto(L, pbL_add(info->field)); break; + pbC(pbL_FieldDescriptorProto(L, pbL_add(info->field))); break; case pb_pair(6, PB_TBYTES): /* FieldDescriptorProto extension */ - pbL_FieldDescriptorProto(L, pbL_add(info->extension)); break; + pbC(pbL_FieldDescriptorProto(L, pbL_add(info->extension))); break; case pb_pair(3, PB_TBYTES): /* DescriptorProto nested_type */ - pbL_DescriptorProto(L, pbL_add(info->nested_type)); break; + pbC(pbL_DescriptorProto(L, pbL_add(info->nested_type))); break; case pb_pair(4, PB_TBYTES): /* EnumDescriptorProto enum_type */ - pbL_EnumDescriptorProto(L, pbL_add(info->enum_type)); break; + pbC(pbL_EnumDescriptorProto(L, pbL_add(info->enum_type))); break; case pb_pair(8, PB_TBYTES): /* OneofDescriptorProto oneof_decl */ - pbL_OneofDescriptorProto(L, info); break; + pbC(pbL_OneofDescriptorProto(L, info)); break; case pb_pair(7, PB_TBYTES): /* MessageOptions options */ - pbL_MessageOptions(L, info); break; + pbC(pbL_MessageOptions(L, info)); break; default: pb_skipvalue(&L->s, tag); } } pbL_endmsg(L, &s); + return PB_OK; } -static void pbL_FileDescriptorProto(pb_Loader *L, pbL_FileInfo *info) { +static int pbL_FileDescriptorProto(pb_Loader *L, pbL_FileInfo *info) { pb_Slice s; - uint32_t tag = 0; - pbL_beginmsg(L, &s); + uint32_t tag; + pbCM(info); pbC(pbL_beginmsg(L, &s)); while (pb_readvarint32(&L->s, &tag)) { switch (tag) { case pb_pair(2, PB_TBYTES): /* string package */ - pbL_readbytes(L, &info->package); break; + pbC(pbL_readbytes(L, &info->package)); break; case pb_pair(4, PB_TBYTES): /* DescriptorProto message_type */ - pbL_DescriptorProto(L, pbL_add(info->message_type)); break; + pbC(pbL_DescriptorProto(L, pbL_add(info->message_type))); break; case pb_pair(5, PB_TBYTES): /* EnumDescriptorProto enum_type */ - pbL_EnumDescriptorProto(L, pbL_add(info->enum_type)); break; + pbC(pbL_EnumDescriptorProto(L, pbL_add(info->enum_type))); break; case pb_pair(7, PB_TBYTES): /* FieldDescriptorProto extension */ - pbL_FieldDescriptorProto(L, pbL_add(info->extension)); break; + pbC(pbL_FieldDescriptorProto(L, pbL_add(info->extension))); break; case pb_pair(12, PB_TBYTES): /* string syntax */ - pbL_readbytes(L, &info->syntax); break; + pbC(pbL_readbytes(L, &info->syntax)); break; default: pb_skipvalue(&L->s, tag); } } pbL_endmsg(L, &s); + return PB_OK; } -static void pbL_FileDescriptorSet(pb_Loader *L, pbL_FileInfo **pinfo) { - uint32_t tag = 0; +static int pbL_FileDescriptorSet(pb_Loader *L, pbL_FileInfo **pfiles) { + uint32_t tag; while (pb_readvarint32(&L->s, &tag)) { switch (tag) { case pb_pair(1, PB_TBYTES): /* FileDescriptorProto file */ - pbL_FileDescriptorProto(L, pbL_add(*pinfo)); break; + pbC(pbL_FileDescriptorProto(L, pbL_add(*pfiles))); break; default: pb_skipvalue(&L->s, tag); } } + return PB_OK; } /* loader */ @@ -1527,113 +1593,110 @@ static void pbL_delFileInfo(pbL_FileInfo *files) { pbL_delete(files); } -static pb_Slice pbL_prefixname(pb_Buffer *b, pb_Slice s, size_t *ps) { - *ps = b->size; - pb_addchar(b, '.'); - pb_addslice(b, s); - return pb_result(b); +static int pbL_prefixname(pb_State *S, pb_Slice s, size_t *ps, pb_Loader *L, pb_Name **out) { + char *buff; + *ps = pb_bufflen(&L->b); + pbCM(buff = pb_prepbuffsize(&L->b, pb_len(s) + 1)); + *buff = '.'; pb_addsize(&L->b, 1); + if (pb_addslice(&L->b, s) == 0) return PB_ENOMEM; + if (out) *out = pb_newname(S, pb_result(&L->b), NULL); + return PB_OK; } -static void pbL_loadEnum(pb_State *S, pbL_EnumInfo *info, pb_Loader *L) { +static int pbL_loadEnum(pb_State *S, pbL_EnumInfo *info, pb_Loader *L) { size_t i, count, curr; - pb_Type *t = pb_newtype(S, pb_newname(S, - pbL_prefixname(&L->b, info->name, &curr))); + pb_Name *name; + pb_Type *t; + pbC(pbL_prefixname(S, info->name, &curr, L, &name)); + pbCM(t = pb_newtype(S, name)); t->is_enum = 1; for (i = 0, count = pbL_count(info->value); i < count; ++i) { pbL_EnumValueInfo *ev = &info->value[i]; - pb_newfield(S, t, pb_newname(S, ev->name), ev->number); + pbCE(pb_newfield(S, t, pb_newname(S, ev->name, NULL), ev->number)); } - L->b.size = curr; -} - -static void pbL_loadField(pb_State *S, pbL_FieldInfo *info, pb_Loader *L, pb_Type *t) { - if (t != NULL || pb_len(info->extendee) != 0) { - pb_Type *ft = pb_newtype(S, pb_newname(S, info->type_name)); - pb_Field *f; - if (!ft && (info->type == PB_Tmessage || info->type == PB_Tenum)) - return; - if (t == NULL && !(t = pb_newtype(S, pb_newname(S, info->extendee)))) - return; - if (!(f = pb_newfield(S, t, pb_newname(S, info->name), info->number))) - return; - f->default_value = pb_newname(S, info->default_value); - f->type = ft; - f->type_id = info->type; - f->repeated = info->label == 3; /* repeated */ - f->packed = info->packed >= 0 ? info->packed : L->is_proto3; - if (f->type_id >= 9 && f->type_id <= 12) f->packed = 0; - f->scalar = f->type == NULL; - if (info->oneof_index != 0) { - pb_OneofEntry *e = (pb_OneofEntry*)pb_gettable(&t->oneof_index, - info->oneof_index), *fe; - if (e != NULL) { - fe = (pb_OneofEntry*)pb_settable(&t->oneof_index, (pb_Key)f); - fe->name = pb_usename(e->name); - fe->index = e->index; - } - } - } -} - -static void pbL_loadType(pb_State *S, pbL_TypeInfo *info, pb_Loader *L) { + L->b.size = (unsigned)curr; + return PB_OK; +} + +static int pbL_loadField(pb_State *S, pbL_FieldInfo *info, pb_Loader *L, pb_Type *t) { + pb_Type *ft = NULL; + pb_Field *f; + if (info->type == PB_Tmessage || info->type == PB_Tenum) + pbCE(ft = pb_newtype(S, pb_newname(S, info->type_name, NULL))); + if (t == NULL) + pbCE(t = pb_newtype(S, pb_newname(S, info->extendee, NULL))); + pbCE(f = pb_newfield(S, t, pb_newname(S, info->name, NULL), info->number)); + f->default_value = pb_newname(S, info->default_value, NULL); + f->type = ft; + f->oneof_idx = info->oneof_index; + f->type_id = info->type; + f->repeated = info->label == 3; /* repeated */ + f->packed = info->packed >= 0 ? info->packed : L->is_proto3 && f->repeated; + if (f->type_id >= 9 && f->type_id <= 12) f->packed = 0; + f->scalar = (f->type == NULL); + return PB_OK; +} + +static int pbL_loadType(pb_State *S, pbL_TypeInfo *info, pb_Loader *L) { size_t i, count, curr; - pb_Type *t = pb_newtype(S, pb_newname(S, - pbL_prefixname(&L->b, info->name, &curr))); - t->is_map = info->is_map; + pb_Name *name; + pb_Type *t; + pbC(pbL_prefixname(S, info->name, &curr, L, &name)); + pbCM(t = pb_newtype(S, name)); + t->is_map = info->is_map; + t->is_proto3 = L->is_proto3; for (i = 0, count = pbL_count(info->oneof_decl); i < count; ++i) { pb_OneofEntry *e = (pb_OneofEntry*)pb_settable(&t->oneof_index, i+1); - e->name = pb_newname(S, info->oneof_decl[i]); - e->index = i+1; + pbCM(e); pbCE(e->name = pb_newname(S, info->oneof_decl[i], NULL)); + e->index = (int)i+1; } for (i = 0, count = pbL_count(info->field); i < count; ++i) - pbL_loadField(S, &info->field[i], L, t); + pbC(pbL_loadField(S, &info->field[i], L, t)); for (i = 0, count = pbL_count(info->extension); i < count; ++i) - pbL_loadField(S, &info->extension[i], L, NULL); + pbC(pbL_loadField(S, &info->extension[i], L, NULL)); for (i = 0, count = pbL_count(info->enum_type); i < count; ++i) - pbL_loadEnum(S, &info->enum_type[i], L); + pbC(pbL_loadEnum(S, &info->enum_type[i], L)); for (i = 0, count = pbL_count(info->nested_type); i < count; ++i) - pbL_loadType(S, &info->nested_type[i], L); - L->b.size = curr; + pbC(pbL_loadType(S, &info->nested_type[i], L)); + L->b.size = (unsigned)curr; + return PB_OK; } -static void pbL_loadFile(pb_State *S, pbL_FileInfo *info, pb_Loader *L) { +static int pbL_loadFile(pb_State *S, pbL_FileInfo *info, pb_Loader *L) { size_t i, count, j, jcount, curr = 0; + pb_Name *syntax; + pbCM(syntax = pb_newname(S, pb_slice("proto3"), NULL)); for (i = 0, count = pbL_count(info); i < count; ++i) { - if (info[i].package.p) pbL_prefixname(&L->b, info[i].package, &curr); - if (pb_newname(S, info[i].syntax) == pb_newname(S, pb_slice("proto3"))) - L->is_proto3 = 1; + if (info[i].package.p) + pbC(pbL_prefixname(S, info[i].package, &curr, L, NULL)); + L->is_proto3 = (pb_name(S, info[i].syntax, NULL) == syntax); for (j = 0, jcount = pbL_count(info[i].enum_type); j < jcount; ++j) - pbL_loadEnum(S, &info[i].enum_type[j], L); + pbC(pbL_loadEnum(S, &info[i].enum_type[j], L)); for (j = 0, jcount = pbL_count(info[i].message_type); j < jcount; ++j) - pbL_loadType(S, &info[i].message_type[j], L); + pbC(pbL_loadType(S, &info[i].message_type[j], L)); for (j = 0, jcount = pbL_count(info[i].extension); j < jcount; ++j) - pbL_loadField(S, &info[i].extension[j], L, NULL); - L->b.size = curr; + pbC(pbL_loadField(S, &info[i].extension[j], L, NULL)); + L->b.size = (unsigned)curr; } + return PB_OK; } PB_API int pb_load(pb_State *S, pb_Slice *s) { pbL_FileInfo *files = NULL; pb_Loader L; - int ret; - if ((ret = setjmp(L.jbuf)) < 0) - return PB_ERROR; - else if (ret == 0) { - L.s = *s; - L.is_proto3 = 0; - pb_initbuffer(&L.b); - pbL_FileDescriptorSet(&L, &files); - pbL_loadFile(S, files, &L); - } + int r; + pb_initbuffer(&L.b); + L.s = *s; + L.is_proto3 = 0; + if ((r = pbL_FileDescriptorSet(&L, &files)) == PB_OK) + r = pbL_loadFile(S, files, &L); pbL_delFileInfo(files); pb_resetbuffer(&L.b); s->p = L.s.p; - return ret; + return r; } PB_NS_END -#endif /* PB_IMPLEMENTATION */ - -/* cc: flags+='-shared -DPB_IMPLEMENTATION -xc' output='pb.so' */ +#endif /* PB_IMPLEMENTATION */ \ No newline at end of file diff --git a/lualib/protobuf.lua b/lualib/protobuf/init.lua similarity index 100% rename from lualib/protobuf.lua rename to lualib/protobuf/init.lua diff --git a/lualib/protobuf/protoc.lua b/lualib/protobuf/protoc.lua new file mode 100644 index 00000000..935407f2 --- /dev/null +++ b/lualib/protobuf/protoc.lua @@ -0,0 +1,1116 @@ +local string = string +local tonumber = tonumber +local setmetatable = setmetatable +local error = error +local ipairs = ipairs +local io = io +local table = table +local math = math +local assert = assert +local tostring = tostring +local type = type +local insert_tab = table.insert + +local function meta(name, t) + t = t or {} + t.__name = name + t.__index = t + return t +end + +local function default(t, k, def) + local v = t[k] + if not v then + v = def or {} + t[k] = v + end + return v +end + +local Lexer = meta "Lexer" do + +local escape = { + a = "\a", b = "\b", f = "\f", n = "\n", + r = "\r", t = "\t", v = "\v" +} + +local function tohex(x) return string.byte(tonumber(x, 16)) end +local function todec(x) return string.byte(tonumber(x, 10)) end +local function toesc(x) return escape[x] or x end + +function Lexer.new(name, src) + local self = { + name = name, + src = src, + pos = 1 + } + return setmetatable(self, Lexer) +end + +function Lexer:__call(patt, pos) + return self.src:match(patt, pos or self.pos) +end + +function Lexer:test(patt) + self:whitespace() + local pos = self('^'..patt..'%s*()') + if not pos then return false end + self.pos = pos + return true +end + +function Lexer:expected(patt, name) + if not self:test(patt) then + return self:error((name or "'"..patt.."'").." expected") + end + return self +end + +function Lexer:pos2loc(pos) + local linenr = 1 + pos = pos or self.pos + for start, stop in self.src:gmatch "()[^\n]*()\n?" do + if start <= pos and pos <= stop then + return linenr, pos - start + 1 + end + linenr = linenr + 1 + end +end + +function Lexer:error(fmt, ...) + local ln, co = self:pos2loc() + return error(("%s:%d:%d: "..fmt):format(self.name, ln, co, ...)) +end + +function Lexer:opterror(opt, msg) + if not opt then return self:error(msg) end + return nil +end + +function Lexer:whitespace() + local pos, c = self "^%s*()(%/?)" + self.pos = pos + if c == '' then return self end + return self:comment() +end + +function Lexer:comment() + local pos = self "^%/%/[^\n]*\n?()" + if not pos then + if self "^%/%*" then + pos = self "^%/%*.-%*%/()" + if not pos then + self:error "unfinished comment" + end + end + end + if not pos then return self end + self.pos = pos + return self:whitespace() +end + +function Lexer:line_end(opt) + self:whitespace() + local pos = self '^[%s;]*%s*()' + if not pos then + return self:opterror(opt, "';' expected") + end + self.pos = pos + return pos +end + +function Lexer:eof() + self:whitespace() + return self.pos > #self.src +end + +function Lexer:keyword(kw, opt) + self:whitespace() + local ident, pos = self "^([%a_][%w_]*)%s*()" + if not ident or ident ~= kw then + return self:opterror(opt, "''"..kw..'" expected') + end + self.pos = pos + return kw +end + +function Lexer:ident(name, opt) + self:whitespace() + local b, ident, pos = self "^()([%a_][%w_]*)%s*()" + if not ident then + return self:opterror(opt, (name or 'name')..' expected') + end + self.pos = pos + return ident, b +end + +function Lexer:full_ident(name, opt) + self:whitespace() + local b, ident, pos = self "^()([%a_][%w_.]*)%s*()" + if not ident or ident:match "%.%.+" then + return self:opterror(opt, (name or 'name')..' expected') + end + self.pos = pos + return ident, b +end + +function Lexer:integer(opt) + self:whitespace() + local ns, oct, hex, s, pos = + self "^([+-]?)(0?)([xX]?)([0-9a-fA-F]+)%s*()" + local n + if oct == '0' and hex == '' then + n = tonumber(s, 8) + elseif oct == '' and hex == '' then + n = tonumber(s, 10) + elseif oct == '0' and hex ~= '' then + n = tonumber(s, 16) + end + if not n then + return self:opterror(opt, 'integer expected') + end + self.pos = pos + return ns == '-' and -n or n +end + +function Lexer:number(opt) + self:whitespace() + if self:test "nan%f[%A]" then + return 0.0/0.0 + elseif self:test "inf%f[%A]" then + return 1.0/0.0 + end + local ns, d1, s, d2, s2, pos = self "^([+-]?)(%.?)([0-9]+)(%.?)([0-9]*)()" + if not ns then + return self:opterror(opt, 'floating-point number expected') + end + local es, pos2 = self("(^[eE][+-]?[0-9]+)%s*()", pos) + if d1 == "." and d2 == "." then + return self:error "malformed floating-point number" + end + self.pos = pos2 or pos + local n = tonumber(d1..s..d2..s2..(es or "")) + return ns == '-' and -n or n +end + +function Lexer:quote(opt) + self:whitespace() + local q, start = self '^(["\'])()' + if not start then + return self:opterror(opt, 'string expected') + end + self.pos = start + local patt = '()(\\?'..q..')%s*()' + while true do + local stop, s, pos = self(patt) + if not stop then + self.pos = start-1 + return self:error "unfinished string" + end + self.pos = pos + if s == q then + return self.src:sub(start, stop-1) + :gsub("\\x(%x+)", tohex) + :gsub("\\(%d+)", todec) + :gsub("\\(.)", toesc) + end + end +end + +function Lexer:structure(opt) + self:whitespace() + if not self:test "{" then + return self:opterror(opt, 'opening curly brace expected') + end + local t = {} + while not self:test "}" do + local ident = self:full_ident "field name" -- TODO: full_ident? + self:test ":" + local value = self:constant() + self:test "," + self:line_end "opt" + t[ident] = value + end + return t +end + +function Lexer:array(opt) + self:whitespace() + if not self:test "%[" then + return self:opterror(opt, 'opening square bracket expected') + end + local t = {} + while not self:test "]" do + local value = self:constant() + self:test "," + t[#t + 1] = value + end + return t +end + +function Lexer:constant(opt) + local c = self:full_ident('constant', 'opt') or + self:number('opt') or + self:quote('opt') or + self:structure('opt') or + self:array('opt') + if not c and not opt then + return self:error "constant expected" + end + return c +end + +function Lexer:option_name() + local ident + if self:test "%(" then + ident = self:full_ident "option name" + self:expected "%)" + else + ident = self:ident "option name" + end + while self:test "%." do + ident = ident .. "." .. self:ident() + end + return ident +end + +function Lexer:type_name() + if self:test "%." then + local id, pos = self:full_ident "type name" + return "."..id, pos + else + return self:full_ident "type name" + end +end + +end + +local Parser = meta "Parser" do +Parser.typemap = {} +Parser.loaded = {} +Parser.paths = { "", "." } + +function Parser.new() + local self = {} + self.typemap = {} + self.loaded = {} + self.paths = { "", "." } + return setmetatable(self, Parser) +end + +function Parser:error(msg) + return self.lex:error(msg) +end + +function Parser:addpath(path) + insert_tab(self.paths, path) +end + +function Parser:parsefile(name) + local info = self.loaded[name] + if info then return info end + local errors = {} + for _, path in ipairs(self.paths) do + local fn = path ~= "" and path.."/"..name or name + local fh, err = io.open(fn) + if fh then + local content = fh:read "*a" + info = self:parse(content, name) + fh:close() + return info + end + insert_tab(errors, err or fn..": ".."unknown error") + end + if self.import_fallback then + info = self.import_fallback(name) + end + if not info then + error("module load error: "..name.."\n\t"..table.concat(errors, "\n\t")) + end + return info +end + +-- parser + +local labels = { optional = 1; required = 2; repeated = 3 } + +local key_types = { + int32 = 5; int64 = 3; uint32 = 13; + uint64 = 4; sint32 = 17; sint64 = 18; + fixed32 = 7; fixed64 = 6; sfixed32 = 15; + sfixed64 = 16; bool = 8; string = 9; +} + +local com_types = { + group = 10; message = 11; enum = 14; +} + +local types = { + double = 1; float = 2; int32 = 5; + int64 = 3; uint32 = 13; uint64 = 4; + sint32 = 17; sint64 = 18; fixed32 = 7; + fixed64 = 6; sfixed32 = 15; sfixed64 = 16; + bool = 8; string = 9; bytes = 12; + group = 10; message = 11; enum = 14; +} + +local function register_type(self, lex, tname, typ) + if not tname:match "%."then + tname = self.prefix..tname + end + if self.typemap[tname] then + return lex:error("type %s already defined", tname) + end + self.typemap[tname] = typ +end + +local function type_info(lex, tname) + local tenum = types[tname] + if com_types[tname] then + return lex:error("invalid type name: "..tname) + elseif tenum then + tname = nil + end + return tenum, tname +end + +local function map_info(lex) + local keyt = lex:ident "key type" + if not key_types[keyt] then + return lex:error("invalid key type: "..keyt) + end + local valt = lex:expected "," :type_name() + local name = lex:expected ">" :ident() + local ident = name:gsub("^%a", string.upper) + :gsub("_(%a)", string.upper).."Entry" + local kt, ktn = type_info(lex, keyt) + local vt, vtn = type_info(lex, valt) + return name, types.message, ident, { + name = ident, + field = { + { + name = "key", + number = 1; + label = labels.optional, + type = kt, + type_name = ktn + }, + { + name = "value", + number = 2; + label = labels.optional, + type = vt, + type_name = vtn + }, + }, + options = { map_entry = true } + } +end + +local function inline_option(lex, info) + if lex:test "%[" then + info = info or {} + while true do + local name = lex:option_name() + local value = lex:expected '=' :constant() + info[name] = value + if lex:test "%]" then + return info + end + lex:expected ',' + end + end +end + +local function field(self, lex, ident) + local name, typ, type_name, map_entry + if ident == "map" and lex:test "%<" then + name, typ, type_name, map_entry = map_info(lex) + self.locmap[map_entry.field[1]] = lex.pos + self.locmap[map_entry.field[2]] = lex.pos + register_type(self, lex, type_name, types.message) + else + typ, type_name = type_info(lex, ident) + name = lex:ident() + end + local info = { + name = name, + number = lex:expected "=":integer(), + label = ident == "map" and labels.repeated or labels.optional, + type = typ, + type_name = type_name + } + local options = inline_option(lex) + if options then + info.default_value, options.default = tostring(options.default), nil + info.json_name, options.json_name = options.json_name, nil + if options.packed and options.packed == "false" then + options.packed = false + end + end + info.options = options + if info.number <= 0 then + lex:error("invalid tag number: "..info.number) + end + return info, map_entry +end + +local function label_field(self, lex, ident) + local label = labels[ident] + local info, map_entry + if not label then + if self.syntax == "proto2" and ident ~= "map" then + return lex:error("proto2 disallow missing label") + end + return field(self, lex, ident) + end + if label == labels.optional and self.syntax == "proto3" then + return lex:error("proto3 disallow 'optional' label") + end + info, map_entry = field(self, lex, lex:type_name()) + info.label = label + return info, map_entry +end + +local toplevel = {} do + +function toplevel:package(lex, info) + local package = lex:full_ident 'package name' + lex:line_end() + info.package = package + self.prefix = "."..package.."." + return self +end + +function toplevel:import(lex, info) + local mode = lex:ident('"weak" or "public"', 'opt') or "public" + if mode ~= 'weak' and mode ~= 'public' then + return lex:error '"weak or "public" expected' + end + local name = lex:quote() + lex:line_end() + local result = self:parsefile(name) + if self.on_import then + self.on_import(result) + end + local dep = default(info, 'dependency') + local index = #dep + dep[index+1] = name + if mode == "public" then + local it = default(info, 'public_dependency') + insert_tab(it, index) + else + local it = default(info, 'weak_dependency') + insert_tab(it, index) + end +end + +local msg_body = {} do + +function msg_body:message(lex, info) + local nested_type = default(info, 'nested_type') + insert_tab(nested_type, toplevel.message(self, lex)) + return self +end + +function msg_body:enum(lex, info) + local nested_type = default(info, 'enum_type') + insert_tab(nested_type, toplevel.enum(self, lex)) + return self +end + +function msg_body:extend(lex, info) + local extension = default(info, 'extension') + local nested_type = default(info, 'nested_type') + local ft, mt = toplevel.extend(self, lex, {}) + for _, v in ipairs(ft) do + insert_tab(extension, v) + end + for _, v in ipairs(mt) do + insert_tab(nested_type, v) + end + return self +end + +function msg_body:extensions(lex, info) + local rt = default(info, 'extension_range') + repeat + local start = lex:integer "field number range" + local stop = math.floor(2^29) + lex:keyword 'to' + if not lex:keyword('max', 'opt') then + stop = lex:integer "field number range end or 'max'" + end + insert_tab(rt, { start = start, ['end'] = stop }) + until not lex:test ',' + lex:line_end() + return self +end + +function msg_body:reserved(lex, info) + lex:whitespace() + if not lex '^%d' then + local rt = default(info, 'reserved_name') + repeat + insert_tab(rt, (lex:quote())) + until not lex:test ',' + else + local rt = default(info, 'reserved_range') + local first = true + repeat + local start = lex:integer(first and 'field name or number range' + or 'field number range') + if lex:keyword('to', 'opt') then + local stop = lex:integer 'field number range end' + insert_tab(rt, { start = start, ['end'] = stop }) + else + insert_tab(rt, { start = start, ['end'] = start }) + end + first = false + until not lex:test ',' + end + lex:line_end() + return self +end + +function msg_body:oneof(lex, info) + local fs = default(info, "field") + local ts = default(info, "nested_type") + local ot = default(info, "oneof_decl") + local index = #ot + 1 + local oneof = { name = lex:ident() } + lex:expected "{" + while not lex:test "}" do + local ident = lex:type_name() + if ident == "option" then + toplevel.option(self, lex, oneof) + else + local f, t = field(self, lex, ident, "no_label") + self.locmap[f] = lex.pos + if t then insert_tab(ts, t) end + f.oneof_index = index - 1 + insert_tab(fs, f) + end + lex:line_end 'opt' + end + ot[index] = oneof +end + +function msg_body:option(lex, info) + toplevel.option(self, lex, default(info, 'options')) +end + +end + +function toplevel:message(lex, info) + local name = lex:ident 'message name' + local typ = { name = name } + register_type(self, lex, name, types.message) + local prefix = self.prefix + self.prefix = prefix..name.."." + lex:expected "{" + while not lex:test "}" do + local ident, pos = lex:type_name() + local body_parser = msg_body[ident] + if body_parser then + body_parser(self, lex, typ) + else + local fs = default(typ, 'field') + local f, t = label_field(self, lex, ident) + self.locmap[f] = pos + insert_tab(fs, f) + if t then + local ts = default(typ, 'nested_type') + insert_tab(ts, t) + end + end + lex:line_end 'opt' + end + lex:line_end 'opt' + if info then + info = default(info, 'message_type') + insert_tab(info, typ) + end + self.prefix = prefix + return typ +end + +function toplevel:enum(lex, info) + local name = lex:ident 'enum name' + local enum = { name = name } + register_type(self, lex, name, types.enum) + lex:expected "{" + while not lex:test "}" do + local ident = lex:ident 'enum constant name' + if ident == 'option' then + toplevel.option(self, lex, default(enum, 'options')) + else + local values = default(enum, 'value') + local number = lex:expected '=' :integer() + lex:line_end() + insert_tab(values, { + name = ident, + number = number, + options = inline_option(lex) + }) + end + lex:line_end 'opt' + end + lex:line_end 'opt' + if info then + info = default(info, 'enum_type') + insert_tab(info, enum) + end + return enum +end + +function toplevel:option(lex, info) + local ident = lex:option_name() + lex:expected "=" + local value = lex:constant() + lex:line_end() + local options = info and default(info, 'options') or {} + options[ident] = value + return options, self +end + +function toplevel:extend(lex, info) + local name = lex:type_name() + local ft = info and default(info, 'extension') or {} + local mt = info and default(info, 'message_type') or {} + lex:expected "{" + while not lex:test "}" do + local ident, pos = lex:type_name() + local f, t = label_field(self, lex, ident) + self.locmap[f] = pos + f.extendee = name + insert_tab(ft, f) + insert_tab(mt, t) + lex:line_end 'opt' + end + return ft, mt +end + +local svr_body = {} do + +function svr_body:rpc(lex, info) + local name, pos = lex:ident "rpc name" + local rpc = { name = name } + self.locmap[rpc] = pos + local _, tn + lex:expected "%(" + rpc.client_streaming = lex:keyword("stream", "opt") + _, tn = type_info(lex, lex:type_name()) + if not tn then return lex:error "rpc input type must by message" end + rpc.input_type = tn + lex:expected "%)" :expected "returns" :expected "%(" + rpc.server_streaming = lex:keyword("stream", "opt") + _, tn = type_info(lex, lex:type_name()) + if not tn then return lex:error "rpc output type must by message" end + rpc.output_type = tn + lex:expected "%)" + if lex:test "{" then + while not lex:test "}" do + lex:line_end "opt" + lex:keyword "option" + toplevel.option(self, lex, default(rpc, 'options')) + end + end + lex:line_end "opt" + local t = default(info, "method") + insert_tab(t, rpc) +end + +function svr_body:option(lex, info) + toplevel.option(self, lex, default(info, 'options')) -- TODO: should be deeper in the info? +end + +function svr_body.stream(_, lex) + lex:error "stream not implement yet" +end + +end + +function toplevel:service(lex, info) + local name = lex:ident 'service name' + local svr = { name = name } + lex:expected "{" + while not lex:test "}" do + local ident = lex:type_name() + local body_parser = svr_body[ident] + if body_parser then + body_parser(self, lex, svr) + else + return lex:error "expected 'rpc' or 'option' in service body" + end + lex:line_end 'opt' + end + lex:line_end 'opt' + if info then + info = default(info, 'service') + insert_tab(info, svr) + end + return svr +end + +end + +local function make_context(self, lex) + local ctx = { + syntax = "proto2"; + locmap = {}; + prefix = "."; + lex = lex; + parser = self; + } + ctx.loaded = self.loaded + ctx.typemap = self.typemap + ctx.paths = self.paths + + function ctx.import_fallback(import_name) + if self.unknown_import == true then + return true + elseif type(self.unknown_import) == 'string' then + return import_name:match(self.unknown_import) and true or nil + elseif self.unknown_import then + return self:unknown_import(import_name) + end + end + + function ctx.type_fallback(type_name) + if self.unknown_type == true then + return true + elseif type(self.unknown_type) == 'string' then + return type_name:match(self.unknown_type) and true + elseif self.unknown_type then + return self:unknown_type(type_name) + end + end + + function ctx.on_import(info) + if self.on_import then + return self.on_import(info) + end + end + + return setmetatable(ctx, Parser) +end + +function Parser:parse(src, name) + local loaded = self.loaded[name] + if loaded then + if loaded == true then + error("loop loaded: "..name) + end + return loaded + end + + name = name or "" + self.loaded[name] = true + local lex = Lexer.new(name, src) + local ctx = make_context(self, lex) + local info = { name = lex.name, syntax = ctx.syntax } + + local syntax = lex:keyword('syntax', 'opt') + if syntax then + info.syntax = lex:expected '=' :quote() + ctx.syntax = info.syntax + lex:line_end() + end + + while not lex:eof() do + local ident = lex:ident() + local top_parser = toplevel[ident] + if top_parser then + top_parser(ctx, lex, info) + else + lex:error("unknown keyword '"..ident.."'") + end + lex:line_end "opt" + end + self.loaded[name] = name ~= "" and info or nil + return ctx:resolve(lex, info) +end + +-- resolver + +local function empty() end + +local function iter(t, k) + local v = t[k] + if v then return ipairs(v) end + return empty +end + +local function check_dup(self, lex, typ, map, k, v) + local old = map[v[k]] + if old then + local ln, co = lex:pos2loc(self.locmap[old]) + lex:error("%s '%s' exists, previous at %d:%d", + typ, v[k], ln, co) + end + map[v[k]] = v +end + +local function check_type(self, lex, tname) + if tname:match "^%." then + local t = self.typemap[tname] + if not t then + return lex:error("unknown type '%s'", tname) + end + return t, tname + end + local prefix = self.prefix + for i = #prefix+1, 1, -1 do + local op = prefix[i] + prefix[i] = tname + local tn = table.concat(prefix, ".", 1, i) + prefix[i] = op + local t = self.typemap[tn] + if t then return t, tn end + end + local tn, t + if self.type_fallback then + tn, t = self.type_fallback(tname) + end + if tn then + t = types[t or "message"] + if tn == true then tn = "."..tname end + return t, tn + end + return lex:error("unknown type '%s'", tname) +end + +local function check_field(self, lex, info) + if info.extendee then + local t, tn = check_type(self, lex, info.extendee) + if t ~= types.message then + lex:error("message type expected in extension") + end + info.extendee = tn + end + if info.type_name then + local t, tn = check_type(self, lex, info.type_name) + info.type = t + info.type_name = tn + end +end + +local function check_enum(self, lex, info) + local names, numbers = {}, {} + for _, v in iter(info, 'value') do + lex.pos = self.locmap[v] + check_dup(self, lex, 'enum name', names, 'name', v) + if not (info.options + and info.options.options + and info.options.options.allow_alias) then + check_dup(self, lex, 'enum number', numbers, 'number', v) + end + end +end + +local function check_message(self, lex, info) + insert_tab(self.prefix, info.name) + local names, numbers = {}, {} + for _, v in iter(info, 'field') do + lex.pos = assert(self.locmap[v]) + check_dup(self, lex, 'field name', names, 'name', v) + check_dup(self, lex, 'field number', numbers, 'number', v) + check_field(self, lex, v) + end + for _, v in iter(info, 'nested_type') do + check_message(self, lex, v) + end + for _, v in iter(info, 'extension') do + lex.pos = assert(self.locmap[v]) + check_field(self, lex, v) + end + self.prefix[#self.prefix] = nil +end + +local function check_service(self, lex, info) + local names = {} + for _, v in iter(info, 'method') do + lex.pos = self.locmap[v] + check_dup(self, lex, 'rpc name', names, 'name', v) + local t, tn = check_type(self, lex, v.input_type) + v.input_type = tn + if t ~= types.message then + lex:error "message type expected in parameter" + end + t, tn = check_type(self, lex, v.output_type) + v.output_type = tn + if t ~= types.message then + lex:error "message type expected in return" + end + end +end + +function Parser:resolve(lex, info) + self.prefix = { "", info.package } + for _, v in iter(info, 'message_type') do + check_message(self, lex, v) + end + for _, v in iter(info, 'enum_type') do + check_enum(self, lex, v) + end + for _, v in iter(info, 'service') do + check_service(self, lex, v) + end + for _, v in iter(info, 'extension') do + lex.pos = assert(self.locmap[v]) + check_field(self, lex, v) + end + self.prefix = nil + return info +end + +end + +local has_pb, pb = pcall(require, "protobuf") do +if has_pb then + local descriptor_pb = + "\10\249#\10\16descriptor.proto\18\15google.protobuf\"G\10\17FileDescript".. + "orSet\0182\10\4file\24\1 \3(\0112$.google.protobuf.FileDescriptorProto\"".. + "\219\3\10\19FileDescriptorProto\18\12\10\4name\24\1 \1(\9\18\15\10\7pack".. + "age\24\2 \1(\9\18\18\10\10dependency\24\3 \3(\9\18\25\10\17public_depend".. + "ency\24\10 \3(\5\18\23\10\15weak_dependency\24\11 \3(\5\0186\10\12messag".. + "e_type\24\4 \3(\0112 .google.protobuf.DescriptorProto\0187\10\9enum_type".. + "\24\5 \3(\0112$.google.protobuf.EnumDescriptorProto\0188\10\7service\24".. + "\6 \3(\0112'.google.protobuf.ServiceDescriptorProto\0188\10\9extension".. + "\24\7 \3(\0112%.google.protobuf.FieldDescriptorProto\18-\10\7options\24".. + "\8 \1(\0112\28.google.protobuf.FileOptions\0189\10\16source_code_info\24".. + "\9 \1(\0112\31.google.protobuf.SourceCodeInfo\18\14\10\6syntax\24\12 \1(".. + "\9\"\228\3\10\15DescriptorProto\18\12\10\4name\24\1 \1(\9\0184\10\5field".. + "\24\2 \3(\0112%.google.protobuf.FieldDescriptorProto\0188\10\9extension".. + "\24\6 \3(\0112%.google.protobuf.FieldDescriptorProto\0185\10\11nested_ty".. + "pe\24\3 \3(\0112 .google.protobuf.DescriptorProto\0187\10\9enum_type\24".. + "\4 \3(\0112$.google.protobuf.EnumDescriptorProto\18H\10\15extension_rang".. + "e\24\5 \3(\0112/.google.protobuf.DescriptorProto.ExtensionRange\0189\10".. + "\10oneof_decl\24\8 \3(\0112%.google.protobuf.OneofDescriptorProto\0180".. + "\10\7options\24\7 \1(\0112\31.google.protobuf.MessageOptions\26,\10\14Ex".. + "tensionRange\18\13\10\5start\24\1 \1(\5\18\11\10\3end\24\2 \1(\5\"\169\5".. + "\10\20FieldDescriptorProto\18\12\10\4name\24\1 \1(\9\18\14\10\6number\24".. + "\3 \1(\5\18:\10\5label\24\4 \1(\0142+.google.protobuf.FieldDescriptorPro".. + "to.Label\0188\10\4type\24\5 \1(\0142*.google.protobuf.FieldDescriptorPro".. + "to.Type\18\17\10\9type_name\24\6 \1(\9\18\16\10\8extendee\24\2 \1(\9\18".. + "\21\10\13default_value\24\7 \1(\9\18\19\10\11oneof_index\24\9 \1(\5\18.".. + "\10\7options\24\8 \1(\0112\29.google.protobuf.FieldOptions\"\182\2\10\4T".. + "ype\18\15\10\11TYPE_DOUBLE\16\1\18\14\10\10TYPE_FLOAT\16\2\18\14\10\10TY".. + "PE_INT64\16\3\18\15\10\11TYPE_UINT64\16\4\18\14\10\10TYPE_INT32\16\5\18".. + "\16\10\12TYPE_FIXED64\16\6\18\16\10\12TYPE_FIXED32\16\7\18\13\10\9TYPE_B".. + "OOL\16\8\18\15\10\11TYPE_STRING\16\9\18\14\10\10TYPE_GROUP\16\10\18\16".. + "\10\12TYPE_MESSAGE\16\11\18\14\10\10TYPE_BYTES\16\12\18\15\10\11TYPE_UIN".. + "T32\16\13\18\13\10\9TYPE_ENUM\16\14\18\17\10\13TYPE_SFIXED32\16\15\18\17".. + "\10\13TYPE_SFIXED64\16\16\18\15\10\11TYPE_SINT32\16\17\18\15\10\11TYPE_S".. + "INT64\16\18\"C\10\5Label\18\18\10\14LABEL_OPTIONAL\16\1\18\18\10\14LABEL".. + "_REQUIRED\16\2\18\18\10\14LABEL_REPEATED\16\3\"$\10\20OneofDescriptorPro".. + "to\18\12\10\4name\24\1 \1(\9\"\140\1\10\19EnumDescriptorProto\18\12\10\4".. + "name\24\1 \1(\9\0188\10\5value\24\2 \3(\0112).google.protobuf.EnumValueD".. + "escriptorProto\18-\10\7options\24\3 \1(\0112\28.google.protobuf.EnumOpti".. + "ons\"l\10\24EnumValueDescriptorProto\18\12\10\4name\24\1 \1(\9\18\14\10".. + "\6number\24\2 \1(\5\0182\10\7options\24\3 \1(\0112!.google.protobuf.Enum".. + "ValueOptions\"\144\1\10\22ServiceDescriptorProto\18\12\10\4name\24\1 \1(".. + "\9\0186\10\6method\24\2 \3(\0112&.google.protobuf.MethodDescriptorProto".. + "\0180\10\7options\24\3 \1(\0112\31.google.protobuf.ServiceOptions\"\193".. + "\1\10\21MethodDescriptorProto\18\12\10\4name\24\1 \1(\9\18\18\10\10input".. + "_type\24\2 \1(\9\18\19\10\11output_type\24\3 \1(\9\18/\10\7options\24\4 ".. + "\1(\0112\30.google.protobuf.MethodOptions\18\31\10\16client_streaming\24".. + "\5 \1(\8:\5false\18\31\10\16server_streaming\24\6 \1(\8:\5false\"\231\4".. + "\10\11FileOptions\18\20\10\12java_package\24\1 \1(\9\18\28\10\20java_out".. + "er_classname\24\8 \1(\9\18\"\10\19java_multiple_files\24\10 \1(\8:\5fals".. + "e\18,\10\29java_generate_equals_and_hash\24\20 \1(\8:\5false\18%\10\22ja".. + "va_string_check_utf8\24\27 \1(\8:\5false\18F\10\12optimize_for\24\9 \1(".. + "\0142).google.protobuf.FileOptions.OptimizeMode:\5SPEED\18\18\10\10go_pa".. + "ckage\24\11 \1(\9\18\"\10\19cc_generic_services\24\16 \1(\8:\5false\18$".. + "\10\21java_generic_services\24\17 \1(\8:\5false\18\"\10\19py_generic_ser".. + "vices\24\18 \1(\8:\5false\18\25\10\10deprecated\24\23 \1(\8:\5false\18".. + "\31\10\16cc_enable_arenas\24\31 \1(\8:\5false\18\25\10\17objc_class_pref".. + "ix\24$ \1(\9\18C\10\20uninterpreted_option\24\231\7 \3(\0112$.google.pro".. + "tobuf.UninterpretedOption\":\10\12OptimizeMode\18\9\10\5SPEED\16\1\18\13".. + "\10\9CODE_SIZE\16\2\18\16\10\12LITE_RUNTIME\16\3*\9\8\232\7\16\128\128".. + "\128\128\2\"\230\1\10\14MessageOptions\18&\10\23message_set_wire_format".. + "\24\1 \1(\8:\5false\18.\10\31no_standard_descriptor_accessor\24\2 \1(\8:".. + "\5false\18\25\10\10deprecated\24\3 \1(\8:\5false\18\17\10\9map_entry\24".. + "\7 \1(\8\18C\10\20uninterpreted_option\24\231\7 \3(\0112$.google.protobu".. + "f.UninterpretedOption*\9\8\232\7\16\128\128\128\128\2\"\160\2\10\12Field".. + "Options\18:\10\5ctype\24\1 \1(\0142#.google.protobuf.FieldOptions.CType:".. + "\6STRING\18\14\10\6packed\24\2 \1(\8\18\19\10\4lazy\24\5 \1(\8:\5false".. + "\18\25\10\10deprecated\24\3 \1(\8:\5false\18\19\10\4weak\24\10 \1(\8:\5f".. + "alse\18C\10\20uninterpreted_option\24\231\7 \3(\0112$.google.protobuf.Un".. + "interpretedOption\"/\10\5CType\18\10\10\6STRING\16\0\18\8\10\4CORD\16\1".. + "\18\16\10\12STRING_PIECE\16\2*\9\8\232\7\16\128\128\128\128\2\"\141\1\10".. + "\11EnumOptions\18\19\10\11allow_alias\24\2 \1(\8\18\25\10\10deprecated".. + "\24\3 \1(\8:\5false\18C\10\20uninterpreted_option\24\231\7 \3(\0112$.goo".. + "gle.protobuf.UninterpretedOption*\9\8\232\7\16\128\128\128\128\2\"}\10".. + "\16EnumValueOptions\18\25\10\10deprecated\24\1 \1(\8:\5false\18C\10\20un".. + "interpreted_option\24\231\7 \3(\0112$.google.protobuf.UninterpretedOptio".. + "n*\9\8\232\7\16\128\128\128\128\2\"{\10\14ServiceOptions\18\25\10\10depr".. + "ecated\24! \1(\8:\5false\18C\10\20uninterpreted_option\24\231\7 \3(\0112".. + "$.google.protobuf.UninterpretedOption*\9\8\232\7\16\128\128\128\128\2\"z".. + "\10\13MethodOptions\18\25\10\10deprecated\24! \1(\8:\5false\18C\10\20uni".. + "nterpreted_option\24\231\7 \3(\0112$.google.protobuf.UninterpretedOption".. + "*\9\8\232\7\16\128\128\128\128\2\"\158\2\10\19UninterpretedOption\18;\10".. + "\4name\24\2 \3(\0112-.google.protobuf.UninterpretedOption.NamePart\18\24".. + "\10\16identifier_value\24\3 \1(\9\18\26\10\18positive_int_value\24\4 \1(".. + "\4\18\26\10\18negative_int_value\24\5 \1(\3\18\20\10\12double_value\24\6".. + "\32\1(\1\18\20\10\12string_value\24\7 \1(\12\18\23\10\15aggregate_value".. + "\24\8 \1(\9\0263\10\8NamePart\18\17\10\9name_part\24\1 \2(\9\18\20\10\12".. + "is_extension\24\2 \2(\8\"\213\1\10\14SourceCodeInfo\18:\10\8location\24".. + "\1 \3(\0112(.google.protobuf.SourceCodeInfo.Location\26\134\1\10\8Locati".. + "on\18\16\10\4path\24\1 \3(\5B\2\16\1\18\16\10\4span\24\2 \3(\5B\2\16\1".. + "\18\24\10\16leading_comments\24\3 \1(\9\18\25\10\17trailing_comments\24".. + "\4 \1(\9\18!\10\25leading_detached_comments\24\6 \3(\9B)\10\19com.google".. + ".protobufB\16DescriptorProtosH\1" + + function Parser.reload() + assert(pb.load(descriptor_pb), "load descriptor msg failed") + end + + local function do_compile(self, f, ...) + if self.include_imports then + local old = self.on_import + local infos = {} + function self.on_import(info) + insert_tab(infos, info) + end + local r = f(...) + insert_tab(infos, r) + self.on_import = old + return { file = infos } + end + return { file = { f(...) } } + end + + function Parser:compile(s, name) + if self == Parser then self = Parser.new() end + local set = do_compile(self, self.parse, self, s, name) + return pb.encode('.google.protobuf.FileDescriptorSet', set) + end + + function Parser:compilefile(fn) + if self == Parser then self = Parser.new() end + local set = do_compile(self, self.parsefile, self, fn) + return pb.encode('.google.protobuf.FileDescriptorSet', set) + end + + function Parser:load(s, name) + if self == Parser then self = Parser.new() end + local ret, pos = pb.load(self:compile(s, name)) + if ret then return ret, pos end + error("load failed at offset "..pos) + end + + function Parser:loadfile(fn) + if self == Parser then self = Parser.new() end + local ret, pos = pb.load(self:compilefile(fn)) + if ret then return ret, pos end + error("load failed at offset "..pos) + end + + Parser.reload() + + end +end + +return Parser \ No newline at end of file From e44b1e5f75a56f690b0e33857bfcc4bab2c64c32 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 19 Nov 2020 21:48:24 +0800 Subject: [PATCH 650/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0SSL=E7=9A=84ALPN?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 47 ++++++++++++++++++++++++++++++++++++++++- lualib/internal/TCP.lua | 26 +++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 0dbddd29..e5b02388 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -964,7 +964,7 @@ static int ssl_set_userdata_key(lua_State *L) { } // 验证证书与私钥是否有效 -int ssl_verify(lua_State *L) { +static int ssl_verify(lua_State *L) { SSL *ssl = (SSL*) lua_touserdata(L, 1); if (!ssl) @@ -982,6 +982,49 @@ int ssl_verify(lua_State *L) { return 1; } +static int ssl_set_alpn(lua_State *L) { + + SSL *ssl = (SSL*) lua_touserdata(L, 1); + if (!ssl) + return luaL_error(L, "Invalid SSL ssl."); + + SSL_CTX *ctx = (SSL_CTX*) lua_touserdata(L, 2); + if (!ctx) + return luaL_error(L, "Invalid SSL_CTX ctx."); + + size_t lsize = 0; + const char *str = luaL_checklstring(L, 3, &lsize); + if (!str || lsize < 1) + return 0; + +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + SSL_set_alpn_protos(ssl, (const unsigned char *)str, lsize); + SSL_CTX_set_alpn_protos(ctx, (const unsigned char *)str, lsize); +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + + return 1; +} + +static int ssl_get_alpn(lua_State *L) { + SSL *ssl = (SSL*) lua_touserdata(L, 1); + if (!ssl) + return luaL_error(L, "Invalid SSL ssl."); + + SSL_CTX *ctx = (SSL_CTX*) lua_touserdata(L, 2); + if (!ctx) + return luaL_error(L, "Invalid SSL_CTX ctx."); + + uint32_t len = 0; + const uint8_t *data = NULL; +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + SSL_get0_alpn_selected(ssl, (const uint8_t **)&data, (unsigned int *)&len); +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + if (!data || len < 1) + return 0; + lua_pushlstring(L, (const char *)data, len); + return 1; +} + static int ssl_new(lua_State *L){ SSL_CTX *ssl_ctx = SSL_CTX_new(SSLv23_method()); @@ -1121,6 +1164,8 @@ luaopen_tcp(lua_State *L){ {"new_unixsock_fd", new_unixsock_fd}, {"sendfile", tcp_sendfile}, {"ssl_verify", ssl_verify}, + {"ssl_set_alpn", ssl_set_alpn}, + {"ssl_get_alpn", ssl_get_alpn}, {"ssl_set_accept_mode", ssl_set_accept_mode}, {"ssl_set_connect_mode", ssl_set_connect_mode}, {"ssl_set_privatekey", ssl_set_privatekey}, diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index 63697f80..975e6d48 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -5,8 +5,10 @@ local new_tab = require("sys").new_tab local log = require "logging" local Log = log:new({ dump = true, path = 'internal-TCP' }) +local ipairs = ipairs local assert = assert local split = string.sub +local spack = string.pack local insert = table.insert local remove = table.remove @@ -50,6 +52,8 @@ local tcp_new_unixsock_fd = tcp.new_unixsock_fd local tcp_ssl_verify = tcp.ssl_verify local tcp_ssl_set_fd = tcp.ssl_set_fd +local tcp_ssl_set_alpn = tcp.ssl_set_alpn +local tcp_ssl_get_alpn = tcp.ssl_get_alpn local tcp_ssl_set_accept_mode = tcp.ssl_set_accept_mode local tcp_ssl_set_connect_mode = tcp.ssl_set_connect_mode local tcp_ssl_set_privatekey = tcp.ssl_set_privatekey @@ -128,6 +132,24 @@ function TCP:ssl_set_verify() return tcp_ssl_verify(self.ssl, self.ssl_ctx) end +-- 设置NPN/ALPN +function TCP:ssl_set_alpn(protocol) + if type(protocol) == 'string' and protocol ~= '' then + if not self.ssl or not self.ssl_ctx then + self.ssl, self.ssl_ctx = tcp_ssl_new() + end + self.alpn = protocol + end +end + +-- 获取NPN/ALPN +function TCP:ssl_get_alpn() + if not self.ssl or not self.ssl_ctx then + return + end + return tcp_ssl_get_alpn(self.ssl, self.ssl_ctx) +end + -- 设置私钥 function TCP:ssl_set_privatekey(privatekey_path) if not self.ssl or not self.ssl_ctx then @@ -666,6 +688,10 @@ local function event_wait(self, event) end function TCP:ssl_handshake() + -- 如果设置了NPN/ALPN, 则需要在握手协商中指定. + if self.alpn then + tcp_ssl_set_alpn(self.ssl, self.ssl_ctx, spack(">B", #self.alpn) .. self.alpn) + end -- 如果是服务端模式, 需要等待客户端先返送hello信息. -- 如果是客户端, 则需要先发送hello信息. if self.mode == "server" then From c6a3d7bb5b1e620bc593ffbd508e8dc479954732 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 19 Nov 2020 21:49:16 +0800 Subject: [PATCH 651/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0ua=E7=9A=84=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=BC=95=E7=94=A8=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/ua.lua | 32 +------------------------------- lualib/protocol/http/ua.lua | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 31 deletions(-) create mode 100644 lualib/protocol/http/ua.lua diff --git a/lualib/httpc/ua.lua b/lualib/httpc/ua.lua index cfe7d93c..45f8c6c3 100644 --- a/lualib/httpc/ua.lua +++ b/lualib/httpc/ua.lua @@ -1,31 +1 @@ -local random = math.random - -local ua = { - -- Windows - "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko", - "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0", - "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0", - "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36", - "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER", - "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3314.0 Safari/537.36 SE 2.X MetaSr 1.0", - "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3756.400 QQBrowser/10.5.4039.400", - "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 UBrowser/6.2.4098.3 Safari/537.36", - "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3947.100 Safari/537.36 2345Explorer/10.7.0.20186", - "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36 Maxthon/5.3.8.2000", - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36", - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36 OPR/68.0.3618.63", - -- Apple - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:76.0) Gecko/20100101 Firefox/76.0", - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:75.0) Gecko/20100101 Firefox/75.0", - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:74.0) Gecko/20100101 Firefox/74.0", - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Safari/605.1.15", - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36", - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36", - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1.2 Safari/605.1.15 QQBrowserLite/1.2.9", -} - -function ua.get_user_agent() - return ua[random(1, #ua)] -end - -return ua \ No newline at end of file +return require "protocol.http.ua" \ No newline at end of file diff --git a/lualib/protocol/http/ua.lua b/lualib/protocol/http/ua.lua new file mode 100644 index 00000000..cfe7d93c --- /dev/null +++ b/lualib/protocol/http/ua.lua @@ -0,0 +1,31 @@ +local random = math.random + +local ua = { + -- Windows + "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0", + "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36", + "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER", + "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3314.0 Safari/537.36 SE 2.X MetaSr 1.0", + "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3756.400 QQBrowser/10.5.4039.400", + "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 UBrowser/6.2.4098.3 Safari/537.36", + "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3947.100 Safari/537.36 2345Explorer/10.7.0.20186", + "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36 Maxthon/5.3.8.2000", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36 OPR/68.0.3618.63", + -- Apple + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:76.0) Gecko/20100101 Firefox/76.0", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:75.0) Gecko/20100101 Firefox/75.0", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:74.0) Gecko/20100101 Firefox/74.0", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Safari/605.1.15", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1.2 Safari/605.1.15 QQBrowserLite/1.2.9", +} + +function ua.get_user_agent() + return ua[random(1, #ua)] +end + +return ua \ No newline at end of file From 5b78d655da04fb4a1d46ae42804299eb22d433e3 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 19 Nov 2020 21:49:33 +0800 Subject: [PATCH 652/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0json=E7=9A=84?= =?UTF-8?q?=E5=AF=BC=E5=85=A5=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/{json.lua => json/init.lua} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lualib/{json.lua => json/init.lua} (100%) diff --git a/lualib/json.lua b/lualib/json/init.lua similarity index 100% rename from lualib/json.lua rename to lualib/json/init.lua From 088c4159fa7e69c718059cc2bd0747cfba69f621 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 26 Nov 2020 00:03:55 +0800 Subject: [PATCH 653/956] =?UTF-8?q?DB=E5=BA=93=E5=A2=9E=E5=8A=A0=E4=BA=8B?= =?UTF-8?q?=E5=8A=A1=E6=94=AF=E6=8C=81=E5=B9=B6=E4=BF=AE=E5=A4=8Dmssql?= =?UTF-8?q?=E4=B8=8Epgsql=E7=9A=84=E6=9F=A5=E8=AF=A2=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/DB/mssql.lua | 96 ++++++++++++++++++++++++++++++++++++++- lualib/DB/mysql.lua | 96 ++++++++++++++++++++++++++++++++++++++- lualib/DB/pgsql.lua | 95 +++++++++++++++++++++++++++++++++++++- lualib/protocol/mssql.lua | 6 +-- lualib/protocol/mysql.lua | 2 +- lualib/protocol/pgsql.lua | 31 +++++++------ 6 files changed, 303 insertions(+), 23 deletions(-) diff --git a/lualib/DB/mssql.lua b/lualib/DB/mssql.lua index 498d7de6..c715cd62 100644 --- a/lualib/DB/mssql.lua +++ b/lualib/DB/mssql.lua @@ -12,9 +12,12 @@ local hashkey = crypt.hashkey local co = require "internal.Co" local co_self = co.self local co_wait = co.wait +local co_spwan = co.spwan local co_wakeup = co.wakeup local type = type +local error = error +local xpcall = xpcall local ipairs = ipairs local assert = assert local tostring = tostring @@ -92,9 +95,9 @@ local function run_query(self, query) local co = pop_wait(self) if co then co_wakeup(co, db) - return ret, err + else + add_db(self, db) end - add_db(self, db) return ret, err end @@ -124,6 +127,95 @@ function DB:connect () return self.INITIALIZATION end +local function traceback(msg) + return fmt("[%s] %s", os.date("%Y/%m/%d %H:%M:%S"), debug.traceback(co_self(), msg, 3)) +end + +function DB:transaction(f) + assert(self.INITIALIZATION, "DB needs to be initialized first.") + assert(type(f) == 'function', "A function must be passed to describe the execution of the transaction.") + local db, ret, err + while 1 do + db = pop_db(self) + if db then + ret, err = db:query("BEGIN TRAN;") + if db.state then + break + end + db:close() + self.current = self.current - 1 + end + db, ret, err = nil, nil, nil + end + -- 每个事务都有独立的session + local session = { nil, nil, nil } + session.query = function ( sql ) + if session.over then + return nil, "Please use `return session.rollback()` or `return session.commit()` after the transaction process is over." + end + assert(db.state, "MSSQL transaction session closed. 1") + local ret, err = db:query(sql) + assert(db.state, "MSSQL transaction session closed. 2") + return ret, err + end + session.rollback = function ( ... ) + assert(db.state, "MSSQL transaction session closed. 3") + db:query("ROLLBACK TRAN;") + assert(db.state, "MSSQL transaction session closed. 4") + session.over = true + return { state = "rollback" } + end + session.commit = function ( ... ) + assert(db.state, "MSSQL transaction session closed. 5") + db:query("COMMIT TRAN;") + assert(db.state, "MSSQL transaction session closed. 6") + session.over = true + return { state = "successed" } + end + + local ok, info = xpcall(f, traceback, session) + if not ok then + -- 如果在自定义事务流程的内部发生了错误 + if not db.state then + self.current = self.current - 1 + db:close() + local co = pop_wait(self) + if co then + co_spwan(function ( ... ) + co_wakeup(co, pop_db(self)) + end) + end + return nil, info + end + db:query("ROLLBACK TRAN;") + local co = pop_wait(self) + if co then + co_wakeup(co, db) + else + add_db(self, db) + end + return nil, info + end + -- 如果定义的事务没有以`commit`或者`rollback`结尾. + if type(info) ~= 'table' or (info.state ~= "successed" and info.state ~= 'rollback') then + db:query("ROLLBACK TRAN;") + local co = pop_wait(self) + if co then + co_wakeup(co, db) + else + add_db(self, db) + end + error("Must return after transaction ends`session.commit()`or`session.rollback()`.") + end + local co = pop_wait(self) + if co then + co_wakeup(co, db) + else + add_db(self, db) + end + return info.state == "successed" and true or false +end + -- 原始查询语句 function DB:query(query) assert(self.INITIALIZATION, "DB needs to be initialized first.") diff --git a/lualib/DB/mysql.lua b/lualib/DB/mysql.lua index c2da7346..ffd9085b 100644 --- a/lualib/DB/mysql.lua +++ b/lualib/DB/mysql.lua @@ -12,9 +12,12 @@ local hashkey = crypt.hashkey local co = require "internal.Co" local co_self = co.self local co_wait = co.wait +local co_spwan = co.spwan local co_wakeup = co.wakeup local type = type +local error = error +local xpcall = xpcall local ipairs = ipairs local assert = assert local tostring = tostring @@ -92,9 +95,9 @@ local function run_query(self, query) local co = pop_wait(self) if co then co_wakeup(co, db) - return ret, err + else + add_db(self, db) end - add_db(self, db) return ret, err end @@ -124,6 +127,95 @@ function DB:connect () return self.INITIALIZATION end +local function traceback(msg) + return fmt("[%s] %s", os.date("%Y/%m/%d %H:%M:%S"), debug.traceback(co_self(), msg, 3)) +end + +function DB:transaction(f) + assert(self.INITIALIZATION, "DB needs to be initialized first.") + assert(type(f) == 'function', "A function must be passed to describe the execution of the transaction.") + local db, ret, err + while 1 do + db = pop_db(self) + if db then + ret, err = db:query("set autocommit=0;") + if db.state then + break + end + db:close() + self.current = self.current - 1 + end + db, ret, err = nil, nil, nil + end + -- 每个事务都有独立的session + local session = { nil, nil, nil } + session.query = function ( sql ) + if session.over then + return nil, "Please use `return session.rollback()` or `return session.commit()` after the transaction process is over." + end + assert(db.state, "MySQL transaction session closed. 1") + local ret, err = db:query(sql) + assert(db.state, "MySQL transaction session closed. 2") + return ret, err + end + session.rollback = function ( ... ) + assert(db.state, "MySQL transaction session closed. 3") + db:query("rollback; set autocommit=1;") + assert(db.state, "MySQL transaction session closed. 4") + session.over = true + return { state = "rollback" } + end + session.commit = function ( ... ) + assert(db.state, "MySQL transaction session closed. 5") + db:query("set autocommit=1;") + assert(db.state, "MySQL transaction session closed. 6") + session.over = true + return { state = "successed" } + end + + local ok, info = xpcall(f, traceback, session) + if not ok then + -- 如果在自定义事务流程的内部发生了错误 + if not db.state then + self.current = self.current - 1 + db:close() + local co = pop_wait(self) + if co then + co_spwan(function ( ... ) + co_wakeup(co, pop_db(self)) + end) + end + return nil, info + end + db:query("rollback; set autocommit=1;") + local co = pop_wait(self) + if co then + co_wakeup(co, db) + else + add_db(self, db) + end + return nil, info + end + -- 如果定义的事务没有以`commit`或者`rollback`结尾. + if type(info) ~= 'table' or (info.state ~= "successed" and info.state ~= 'rollback') then + db:query("rollback; set autocommit=1;") + local co = pop_wait(self) + if co then + co_wakeup(co, db) + else + add_db(self, db) + end + error("Must return after transaction ends`session.commit()`or`session.rollback()`.") + end + local co = pop_wait(self) + if co then + co_wakeup(co, db) + else + add_db(self, db) + end + return info.state == "successed" and true or false +end + -- 原始查询语句 function DB:query(query) assert(self.INITIALIZATION, "DB needs to be initialized first.") diff --git a/lualib/DB/pgsql.lua b/lualib/DB/pgsql.lua index da5fb976..f97f4637 100644 --- a/lualib/DB/pgsql.lua +++ b/lualib/DB/pgsql.lua @@ -12,9 +12,12 @@ local hashkey = crypt.hashkey local co = require "internal.Co" local co_self = co.self local co_wait = co.wait +local co_spwan = co.spwan local co_wakeup = co.wakeup local type = type +local error = error +local xpcall = xpcall local ipairs = ipairs local assert = assert local tostring = tostring @@ -92,9 +95,9 @@ local function run_query(self, query) local co = pop_wait(self) if co then co_wakeup(co, db) - return ret, err + else + add_db(self, db) end - add_db(self, db) return ret, err end @@ -124,6 +127,94 @@ function DB:connect () return self.INITIALIZATION end +local function traceback(msg) + return fmt("[%s] %s", os.date("%Y/%m/%d %H:%M:%S"), debug.traceback(co_self(), msg, 3)) +end + +function DB:transaction(f) + assert(self.INITIALIZATION, "DB needs to be initialized first.") + assert(type(f) == 'function', "A function must be passed to describe the execution of the transaction.") + local db, ret, err + while 1 do + db = pop_db(self) + if db then + ret, err = db:query("BEGIN;") + if db.state then + break + end + db:close() + self.current = self.current - 1 + end + db, ret, err = nil, nil, nil + end + -- 每个事务都有独立的session + local session = { nil, nil, nil } + session.query = function ( sql ) + if session.over then + return nil, "Please use `return session.rollback()` or `return session.commit()` after the transaction process is over or Process error." + end + assert(db.state, "PGSQL transaction session closed. 1") + local ret, err = db:query(sql) + assert(db.state, "PGSQL transaction session closed. 2") + return ret, err + end + session.rollback = function ( ... ) + assert(db.state, "PGSQL transaction session closed. 3") + db:query("ROLLBACK;") + assert(db.state, "PGSQL transaction session closed. 4") + session.over = true + return { state = "rollback" } + end + session.commit = function ( ... ) + assert(db.state, "PGSQL transaction session closed. 5") + db:query("COMMIT;") + assert(db.state, "PGSQL transaction session closed. 6") + session.over = true + return { state = "successed" } + end + local ok, info = xpcall(f, traceback, session) + if not ok then + -- 如果在自定义事务流程的内部发生了错误 + if not db.state then + self.current = self.current - 1 + db:close() + local co = pop_wait(self) + if co then + co_spwan(function ( ... ) + co_wakeup(co, pop_db(self)) + end) + end + return nil, info + end + db:query("ROLLBACK;") + local co = pop_wait(self) + if co then + co_wakeup(co, db) + else + add_db(self, db) + end + return nil, info + end + -- 如果定义的事务没有以`commit`或者`rollback`结尾. + if type(info) ~= 'table' or (info.state ~= "successed" and info.state ~= 'rollback') then + db:query("ROLLBACK;") + local co = pop_wait(self) + if co then + co_wakeup(co, db) + else + add_db(self, db) + end + error("Must return after transaction ends`session.commit()`or`session.rollback()`.") + end + local co = pop_wait(self) + if co then + co_wakeup(co, db) + else + add_db(self, db) + end + return info.state == "successed" and true or false +end + -- 原始查询语句 function DB:query(query) assert(self.INITIALIZATION, "DB needs to be initialized first.") diff --git a/lualib/protocol/mssql.lua b/lualib/protocol/mssql.lua index 7c044a80..6867055e 100644 --- a/lualib/protocol/mssql.lua +++ b/lualib/protocol/mssql.lua @@ -743,18 +743,18 @@ end local function tds_read_response(self, before_packets) local packet = tds_read_head(self) if not packet then - self.connected = nil + self.state = nil return nil, "The server disconnected before receiving the response header." end local OPCODE, STATUS, LENGTH, CHANNEL, PACKNO, WINDOW = tds_unpack_header(packet) if OPCODE ~= PTYPE_RESPONSE then - self.connected = nil + self.state = nil return nil, "A protocol type not supported by TDS-7.0 was received." end local packet = tds_read_body(self, LENGTH) if not packet then - self.connected = nil + self.state = nil return nil, "The server disconnected before receiving the response data." end diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua index 369a0724..89805996 100644 --- a/lualib/protocol/mysql.lua +++ b/lualib/protocol/mysql.lua @@ -651,4 +651,4 @@ function mysql:close() return sock:close() end -return mysql \ No newline at end of file +return mysql diff --git a/lualib/protocol/pgsql.lua b/lualib/protocol/pgsql.lua index 8133d960..785d03b8 100644 --- a/lualib/protocol/pgsql.lua +++ b/lualib/protocol/pgsql.lua @@ -199,7 +199,7 @@ local function get_error_message_tab(msg) end local function read_opcode_and_len (self) - local opstr = self.sock:recv(1) + local opstr = self:read(1) if not opstr then return nil, "client read: server close this session." end @@ -227,7 +227,7 @@ local function get_query_error_msg (self, data) if opcode ~= RESP_READY then return nil, len end - local _ = self.sock:recv(len - 4) + local _ = self:read(len - 4) return nil, msg end @@ -301,7 +301,7 @@ local function read_response (self) while 1 do local opcode, len = read_opcode_and_len(self) if not opcode then - self.status = "closed" + self.state = "closed" return nil, "1. server close this session." end if opcode == RESP_ERROR then @@ -311,7 +311,7 @@ local function read_response (self) if opcode == RESP_STATUS then local kv = self:read(len - 4) if not kv then - self.status = "closed" + self.state = "closed" return nil, "2. server close this session." end local k, v = strunpack("zz", kv) @@ -326,7 +326,7 @@ local function read_response (self) results[#results + 1] = result local opcode, len = read_opcode_and_len(self) if not opcode then - self.status = "closed" + self.state = "closed" return nil, "3. server close this session." end if opcode == RESP_CMD_COMPLETION then @@ -336,7 +336,7 @@ local function read_response (self) local tab = new_tab(3, 0) local content = self:read(len - 4) if not content then - self.status = "closed" + self.state = "closed" return nil, "4. server close this session." end for v in strgmatch(content, "[^ \x00]+") do @@ -362,14 +362,19 @@ local function read_response (self) elseif opcode == RESP_READY then local v = self:read(len - 4) if not v then - self.status = "closed" + self.state = "closed" return nil, "5. server close this session." end + if v == "T" then + results[#results].transaction = true + else + results[#results].transaction = false + end break elseif opcode == RESP_COLUMN then local columns, err = read_column_data(self, len - 4) if not columns then - self.status = "closed" + self.state = "closed" return nil, err end -- var_dump(columns) @@ -382,7 +387,7 @@ local function read_response (self) elseif type(row) == 'number' then break else - self.status = "closed" + self.state = "closed" return nil, "6. server close this session." end end @@ -399,7 +404,7 @@ local function read_response (self) if row == RESP_CMD_COMPLETION then local v = self:read(len - 4) if not v then - self.status = "closed" + self.state = "closed" return nil, "7. server close this session." end end @@ -521,7 +526,7 @@ function pgsql:connect() end end - self.status = "connected" + self.state = "connected" self.server = server return server end @@ -557,8 +562,8 @@ function pgsql:set_timeout(timeout) end function pgsql:close() - if self.status == "connected" then - self.status = "closed" + if self.state == "connected" then + self.state = "closed" self:write(strpack(">BI4", OP_TERMINATE, 4)) end if self.sock then From bdb8be19d4d0777ba6d68be493fbbb2880f91669 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 26 Nov 2020 23:40:59 +0800 Subject: [PATCH 654/956] =?UTF-8?q?=E5=BF=BD=E7=95=A5PGSQL=E7=9A=84?= =?UTF-8?q?=E6=A0=87=E5=BF=97=E4=BD=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/pgsql.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lualib/protocol/pgsql.lua b/lualib/protocol/pgsql.lua index 785d03b8..78c1ab30 100644 --- a/lualib/protocol/pgsql.lua +++ b/lualib/protocol/pgsql.lua @@ -365,11 +365,11 @@ local function read_response (self) self.state = "closed" return nil, "5. server close this session." end - if v == "T" then - results[#results].transaction = true - else - results[#results].transaction = false - end + -- if v == "T" then + -- results[#results].transaction = true + -- else + -- results[#results].transaction = false + -- end break elseif opcode == RESP_COLUMN then local columns, err = read_column_data(self, len - 4) From 9f5306a27d5e60054a93e163a1c295b0d80fd818 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 26 Nov 2020 23:41:31 +0800 Subject: [PATCH 655/956] =?UTF-8?q?=E5=AE=8C=E5=96=84DB=E5=BA=93=E7=9A=84?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93=E4=BA=8B=E5=8A=A1=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/DB/mssql.lua | 19 +++++++++++-------- lualib/DB/mysql.lua | 19 +++++++++++-------- lualib/DB/pgsql.lua | 19 +++++++++++-------- 3 files changed, 33 insertions(+), 24 deletions(-) diff --git a/lualib/DB/mssql.lua b/lualib/DB/mssql.lua index c715cd62..0c6aafb2 100644 --- a/lualib/DB/mssql.lua +++ b/lualib/DB/mssql.lua @@ -149,27 +149,30 @@ function DB:transaction(f) end -- 每个事务都有独立的session local session = { nil, nil, nil } - session.query = function ( sql ) - if session.over then - return nil, "Please use `return session.rollback()` or `return session.commit()` after the transaction process is over." + session.query = function (self, sql) + assert(self and self == session, "Must use the syntax of `session:query()`") + if self.over then + return nil, "Please use `return session:rollback()` or `return session:commit()` after the transaction process is over or Process error." end assert(db.state, "MSSQL transaction session closed. 1") local ret, err = db:query(sql) assert(db.state, "MSSQL transaction session closed. 2") return ret, err end - session.rollback = function ( ... ) + session.rollback = function ( self ) + assert(self and self == session, "Must use the syntax of `session:rollback()`") assert(db.state, "MSSQL transaction session closed. 3") db:query("ROLLBACK TRAN;") assert(db.state, "MSSQL transaction session closed. 4") - session.over = true + self.over = true return { state = "rollback" } end - session.commit = function ( ... ) + session.commit = function ( self ) + assert(self and self == session, "Must use the syntax of `session:commit()`") assert(db.state, "MSSQL transaction session closed. 5") db:query("COMMIT TRAN;") assert(db.state, "MSSQL transaction session closed. 6") - session.over = true + self.over = true return { state = "successed" } end @@ -205,7 +208,7 @@ function DB:transaction(f) else add_db(self, db) end - error("Must return after transaction ends`session.commit()`or`session.rollback()`.") + error("Must return after transaction ends`session:commit()`or`session:rollback()`.") end local co = pop_wait(self) if co then diff --git a/lualib/DB/mysql.lua b/lualib/DB/mysql.lua index ffd9085b..98d4ec11 100644 --- a/lualib/DB/mysql.lua +++ b/lualib/DB/mysql.lua @@ -149,27 +149,30 @@ function DB:transaction(f) end -- 每个事务都有独立的session local session = { nil, nil, nil } - session.query = function ( sql ) - if session.over then - return nil, "Please use `return session.rollback()` or `return session.commit()` after the transaction process is over." + session.query = function (self, sql) + assert(self and self == session, "Must use the syntax of `session:query()`") + if self.over then + return nil, "Please use `return session:rollback()` or `return session:commit()` after the transaction process is over or Process error." end assert(db.state, "MySQL transaction session closed. 1") local ret, err = db:query(sql) assert(db.state, "MySQL transaction session closed. 2") return ret, err end - session.rollback = function ( ... ) + session.rollback = function ( self ) + assert(self and self == session, "Must use the syntax of `session:rollback()`") assert(db.state, "MySQL transaction session closed. 3") db:query("rollback; set autocommit=1;") assert(db.state, "MySQL transaction session closed. 4") - session.over = true + self.over = true return { state = "rollback" } end - session.commit = function ( ... ) + session.commit = function ( self ) + assert(self and self == session, "Must use the syntax of `session:commit()`") assert(db.state, "MySQL transaction session closed. 5") db:query("set autocommit=1;") assert(db.state, "MySQL transaction session closed. 6") - session.over = true + self.over = true return { state = "successed" } end @@ -205,7 +208,7 @@ function DB:transaction(f) else add_db(self, db) end - error("Must return after transaction ends`session.commit()`or`session.rollback()`.") + error("Must return after transaction ends`session:commit()`or`session:rollback()`.") end local co = pop_wait(self) if co then diff --git a/lualib/DB/pgsql.lua b/lualib/DB/pgsql.lua index f97f4637..d53469c3 100644 --- a/lualib/DB/pgsql.lua +++ b/lualib/DB/pgsql.lua @@ -149,27 +149,30 @@ function DB:transaction(f) end -- 每个事务都有独立的session local session = { nil, nil, nil } - session.query = function ( sql ) - if session.over then - return nil, "Please use `return session.rollback()` or `return session.commit()` after the transaction process is over or Process error." + session.query = function (self, sql) + assert(self and self == session, "Must use the syntax of `session:query()`") + if self.over then + return nil, "Please use `return session:rollback()` or `return session:commit()` after the transaction process is over or Process error." end assert(db.state, "PGSQL transaction session closed. 1") local ret, err = db:query(sql) assert(db.state, "PGSQL transaction session closed. 2") return ret, err end - session.rollback = function ( ... ) + session.rollback = function ( self ) + assert(self and self == session, "Must use the syntax of `session:rollback()`") assert(db.state, "PGSQL transaction session closed. 3") db:query("ROLLBACK;") assert(db.state, "PGSQL transaction session closed. 4") - session.over = true + self.over = true return { state = "rollback" } end - session.commit = function ( ... ) + session.commit = function ( self ) + assert(self and self == session, "Must use the syntax of `session:commit()`") assert(db.state, "PGSQL transaction session closed. 5") db:query("COMMIT;") assert(db.state, "PGSQL transaction session closed. 6") - session.over = true + self.over = true return { state = "successed" } end local ok, info = xpcall(f, traceback, session) @@ -204,7 +207,7 @@ function DB:transaction(f) else add_db(self, db) end - error("Must return after transaction ends`session.commit()`or`session.rollback()`.") + error("Must return after transaction ends`session:commit()`or`session:rollback()`.") end local co = pop_wait(self) if co then From 91c2bfe490c859ea21b2feccecfd9c8b948b3adb Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 3 Dec 2020 20:50:41 +0800 Subject: [PATCH 656/956] =?UTF-8?q?crypt=E5=BA=93=E5=A2=9E=E5=8A=A0SM2/SM3?= =?UTF-8?q?/SM4=E7=AE=97=E6=B3=95=E6=94=AF=E6=8C=81(openssl=20>=201.1.1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/Makefile | 2 +- luaclib/src/lcrypt/lcrypt.c | 34 +++- luaclib/src/lcrypt/lcrypt.h | 22 ++- luaclib/src/lcrypt/sm.c | 369 ++++++++++++++++++++++++++++++++++++ lualib/crypt/init.lua | 124 ++++++++++++ 5 files changed, 541 insertions(+), 10 deletions(-) create mode 100644 luaclib/src/lcrypt/sm.c diff --git a/luaclib/src/lcrypt/Makefile b/luaclib/src/lcrypt/Makefile index a029a79d..742e04fa 100644 --- a/luaclib/src/lcrypt/Makefile +++ b/luaclib/src/lcrypt/Makefile @@ -16,5 +16,5 @@ INCLUDES = -I../../../ -I/usr/local/include LIBS = -L../ -L../../../ -L/usr/local/lib build: - @$(CC) -o lcrypt.so lcrypt.c aes.c des.c dh.c rsa.c sha.c hmac.c hmac_ex.c b64.c crc.c url.c hex.c uuid.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) + @$(CC) -o lcrypt.so lcrypt.c aes.c des.c dh.c rsa.c sha.c hmac.c hmac_ex.c b64.c crc.c url.c hex.c uuid.c sm.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) @mv *.so ../../ diff --git a/luaclib/src/lcrypt/lcrypt.c b/luaclib/src/lcrypt/lcrypt.c index 2e8a5f37..7d702fc8 100644 --- a/luaclib/src/lcrypt/lcrypt.c +++ b/luaclib/src/lcrypt/lcrypt.c @@ -43,19 +43,24 @@ static int lrandomkey(lua_State *L) { /* -- xor_str -- */ -#define lua_set_key_value(L, key, value) ({ lua_pushstring((L), (key)); lua_pushinteger((L), (value)); lua_rawset((L), -3); }) +#define lua_set_key_INT(L, key, value) ({ lua_pushstring((L), (key)); lua_pushinteger((L), (value)); lua_rawset((L), -3); }) +#define lua_set_key_STR(L, key, value) ({ lua_pushstring((L), (key)); lua_pushstring((L), (value)); lua_rawset((L), -3); }) static int crypt_set_key_value(lua_State *L) { + /* OPENSSL VERSION NUMBER */ + lua_set_key_INT(L, "OPENSSL_VERSION_NUMBER", OPENSSL_VERSION_NUMBER); + lua_set_key_STR(L, "OPENSSL_VERSION_TEXT", OPENSSL_VERSION_TEXT); + /* 增加rsa填充方式常量 */ - lua_set_key_value(L, "RSA_NO_PADDING", RSA_NO_PADDING); - lua_set_key_value(L, "RSA_PKCS1_PADDING", RSA_PKCS1_PADDING); - lua_set_key_value(L, "RSA_PKCS1_OAEP_PADDING", RSA_PKCS1_OAEP_PADDING); + lua_set_key_INT(L, "RSA_NO_PADDING", RSA_NO_PADDING); + lua_set_key_INT(L, "RSA_PKCS1_PADDING", RSA_PKCS1_PADDING); + lua_set_key_INT(L, "RSA_PKCS1_OAEP_PADDING", RSA_PKCS1_OAEP_PADDING); /* 增加rsa_sign/rsa_verify算法常量*/ - lua_set_key_value(L, "nid_md5", NID_md5); - lua_set_key_value(L, "nid_sha1", NID_sha1); - lua_set_key_value(L, "nid_sha256", NID_sha256); - lua_set_key_value(L, "nid_sha512", NID_sha512); + lua_set_key_INT(L, "nid_md5", NID_md5); + lua_set_key_INT(L, "nid_sha1", NID_sha1); + lua_set_key_INT(L, "nid_sha256", NID_sha256); + lua_set_key_INT(L, "nid_sha512", NID_sha512); return 1; } @@ -121,6 +126,19 @@ LUAMOD_API int luaopen_lcrypt(lua_State *L) { // {"aes_cfb_decrypt", laes_cfb_decrypt}, // {"aes_ofb_decrypt", laes_ofb_decrypt}, // {"aes_ctr_decrypt", laes_ctr_decrypt}, + {"sm3", lsm3}, + {"hmac_sm3", lhmac_sm3}, + {"sm2keygen", lsm2keygen}, + {"sm2sign", lsm2sign}, + {"sm2verify", lsm2verify}, + {"sm4_cbc_encrypt", lsm4_cbc_encrypt}, + {"sm4_cbc_decrypt", lsm4_cbc_decrypt}, + {"sm4_ecb_encrypt", lsm4_ecb_encrypt}, + {"sm4_ecb_decrypt", lsm4_ecb_decrypt}, + {"sm4_ofb_encrypt", lsm4_ofb_encrypt}, + {"sm4_ofb_decrypt", lsm4_ofb_decrypt}, + {"sm4_ctr_encrypt", lsm4_ctr_encrypt}, + {"sm4_ctr_decrypt", lsm4_ctr_decrypt}, { NULL, NULL }, }; luaL_newlib(L, lcrypt); diff --git a/luaclib/src/lcrypt/lcrypt.h b/luaclib/src/lcrypt/lcrypt.h index ff7d8aef..a914c930 100644 --- a/luaclib/src/lcrypt/lcrypt.h +++ b/luaclib/src/lcrypt/lcrypt.h @@ -71,4 +71,24 @@ int lrsa_public_key_decode(lua_State *L); int lrsa_sign(lua_State *L); -int lrsa_verify(lua_State *L); \ No newline at end of file +int lrsa_verify(lua_State *L); + +int lsm3(lua_State *L); +int lhmac_sm3(lua_State *L); + +int lsm2keygen(lua_State *L); + +int lsm2sign(lua_State *L); +int lsm2verify(lua_State *L); + +int lsm4_cbc_encrypt(lua_State *L); +int lsm4_cbc_decrypt(lua_State *L); + +int lsm4_ecb_encrypt(lua_State *L); +int lsm4_ecb_decrypt(lua_State *L); + +int lsm4_ofb_encrypt(lua_State *L); +int lsm4_ofb_decrypt(lua_State *L); + +int lsm4_ctr_encrypt(lua_State *L); +int lsm4_ctr_decrypt(lua_State *L); \ No newline at end of file diff --git a/luaclib/src/lcrypt/sm.c b/luaclib/src/lcrypt/sm.c new file mode 100644 index 00000000..b1522820 --- /dev/null +++ b/luaclib/src/lcrypt/sm.c @@ -0,0 +1,369 @@ +#include "lcrypt.h" + +#if OPENSSL_VERSION_NUMBER < 0x10101000L || defined(OPENSSL_NO_SM2) || defined(OPENSSL_NO_SM3) || defined(OPENSSL_NO_SM4) + +/* 不支持的情况下使用需要抛出异常. */ +#define SM_THROW(L) luaL_error(L, "The current environment does not support the SM2/SM3/SM4 algorithm.") + +int lsm3(lua_State *L) { return SM_THROW(L); } +int lhmac_sm3(lua_State *L) { return SM_THROW(L); } + +int lsm4_cbc_encrypt(lua_State *L) { return SM_THROW(L); } +int lsm4_cbc_decrypt(lua_State *L) { return SM_THROW(L); } + +int lsm4_ecb_encrypt(lua_State *L) { return SM_THROW(L); } +int lsm4_ecb_decrypt(lua_State *L) { return SM_THROW(L); } + +int lsm4_ofb_encrypt(lua_State *L) { return SM_THROW(L); } +int lsm4_ofb_decrypt(lua_State *L) { return SM_THROW(L); } + +int lsm4_ctr_encrypt(lua_State *L) { return SM_THROW(L); } +int lsm4_ctr_decrypt(lua_State *L) { return SM_THROW(L); } + +int lsm2keygen(lua_State *L){ return SM_THROW(L); } + +int lsm2sign(lua_State *L) { return SM_THROW(L); } +int lsm2verify(lua_State *L) { return SM_THROW(L); } + +#else + +#ifndef SM3_BLOCK_SIZE + #define SM3_BLOCK_SIZE (32) +#endif + +int lsm3(lua_State *L) { + size_t textsize = 0; + const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 1, &textsize); + EVP_MD_CTX *md_ctx = EVP_MD_CTX_new(); + EVP_DigestInit_ex(md_ctx, EVP_sm3(), NULL); + EVP_DigestUpdate(md_ctx, text, textsize); + uint32_t result_size = SM3_BLOCK_SIZE; + uint8_t result[result_size]; + EVP_DigestFinal_ex(md_ctx, result, &result_size); + EVP_MD_CTX_free(md_ctx); + lua_pushlstring(L, (const char *)result, SM3_BLOCK_SIZE); + return 1; +} + +int lhmac_sm3(lua_State *L) { + size_t key_sz = 0; + size_t text_sz = 0; + const char * key = luaL_checklstring(L, 1, &key_sz); + if (!key || key_sz <= 0) + return luaL_error(L, "Invalid key value."); + + const char * text = luaL_checklstring(L, 2, &text_sz); + if (!text || text_sz <= 0) + return luaL_error(L, "Invalid text value."); + + uint32_t result_len = SM3_BLOCK_SIZE; + uint8_t result[result_len]; + memset(result, 0x0, result_len); + HMAC(EVP_sm3(), (const unsigned char*)key, key_sz, (const unsigned char*)text, text_sz, result, &result_len); + lua_pushlstring(L, (const char *)result, result_len); + return 1; +} + +/* 加密函数 */ +static inline int sm4_encrypt(lua_State *L, const EVP_CIPHER *evp_md, const uint8_t *iv, const uint8_t *key, const uint8_t *text, size_t tsize) { + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + if (!ctx) + return luaL_error(L, "allocate EVP failed."); + + if (1 != EVP_EncryptInit_ex(ctx, evp_md, NULL, key, iv)){ + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return luaL_error(L, "SM4_ENCRYPT_INIT failed."); + } + + EVP_CIPHER_CTX_set_padding(ctx, 1); + // printf("key_len = %d\n", EVP_CIPHER_CTX_key_length(ctx)); + // printf("iv_len = %d\n", EVP_CIPHER_CTX_iv_length(ctx)); + // printf("block_size = %d\n", EVP_CIPHER_CTX_block_size(ctx)); + + int out_size = tsize + EVP_MAX_BLOCK_LENGTH; + uint8_t *out = lua_newuserdata(L, out_size); + + + int update_len = out_size; + if (1 != EVP_EncryptUpdate(ctx, out, &update_len, text, tsize)){ + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return luaL_error(L, "SM4_ENCRYPT_UPDATE failed."); + } + + int final_len = out_size; + if (1 != EVP_EncryptFinal(ctx, out + update_len, &final_len)){ + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return luaL_error(L, "SM4_ENCRYPT_FINAL failed."); + } + + lua_pushlstring(L, (const char*)out, update_len + final_len); + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return 1; +} + +/* 解密函数 */ +static inline int sm4_decrypt(lua_State *L, const EVP_CIPHER *evp_md, const uint8_t *iv, const uint8_t *key, const uint8_t *cipher, size_t csize) { + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + if (!ctx) + return luaL_error(L, "allocate EVP failed."); + + if (1 != EVP_DecryptInit_ex(ctx, evp_md, NULL, key, iv)){ + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return luaL_error(L, "SM4_DECRYPT_INIT failed."); + } + + EVP_CIPHER_CTX_set_padding(ctx, 1); + // printf("key_len = %d\n", EVP_CIPHER_CTX_key_length(ctx)); + // printf("iv_len = %d\n", EVP_CIPHER_CTX_iv_length(ctx)); + // printf("block_size = %d\n", EVP_CIPHER_CTX_block_size(ctx)); + + int out_size = csize + EVP_MAX_BLOCK_LENGTH; + uint8_t *out = lua_newuserdata(L, out_size); + + int update_len = out_size; + if (1 != EVP_DecryptUpdate(ctx, out, &update_len, cipher, csize)){ + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return luaL_error(L, "SM4_DECRYPT_UPDATE failed."); + } + + int final_len = out_size; + if (1 != EVP_DecryptFinal_ex(ctx, out + update_len, &final_len)){ + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return luaL_error(L, "SM4_DECRYPT_FINAL failed."); + } + + lua_pushlstring(L, (const char*)out, update_len + final_len); + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return 1; +} + +static inline const EVP_CIPHER* get_cipher(lua_State *L, int mode) { + switch(mode){ + case 1: + return EVP_sm4_cbc(); + case 2: + return EVP_sm4_ecb(); + case 3: + return EVP_sm4_ofb(); + case 4: + return EVP_sm4_ctr(); + } + return luaL_error(L, "Invalid SM4 CIPHER."), NULL; +} + +static inline int lua_getarg(lua_State *L, const char **iv, const char **key, const char **text, size_t *tsize) { + *key = luaL_checkstring(L, 1); + if (!key) + return luaL_error(L, "Invalid key"); + + size_t size = 0; + *text = luaL_checklstring(L, 2, &size); + if (!text) + return luaL_error(L, "Invalid text"); + *tsize = size; + + *iv = luaL_checkstring(L, 3); + if (!iv) + return luaL_error(L, "Invalid iv"); + return 1; +} + + +/* SM4加密函数的分组类型封装 */ +int lsm4_cbc_encrypt(lua_State *L) { + const char *iv; const char *key; const char *text; size_t tsize; + // lua_getarg(L, &iv, &key, &text, &tsize); + // return 1; + return lua_getarg(L, &iv, &key, &text, &tsize) && sm4_encrypt(L, get_cipher(L, 1), (const uint8_t *)iv, (const uint8_t *)key, (const uint8_t *)text, tsize); +} + +int lsm4_ecb_encrypt(lua_State *L) { + const char *iv; const char *key; const char *text; size_t tsize; + // lua_getarg(L, &iv, &key, &text, &tsize); + // return 1; + return lua_getarg(L, &iv, &key, &text, &tsize) && sm4_encrypt(L, get_cipher(L, 2), (const uint8_t *)iv, (const uint8_t *)key, (const uint8_t *)text, tsize); +} + +int lsm4_ofb_encrypt(lua_State *L) { + const char *iv; const char *key; const char *text; size_t tsize; + // lua_getarg(L, &iv, &key, &text, &tsize); + // return 1; + return lua_getarg(L, &iv, &key, &text, &tsize) && sm4_encrypt(L, get_cipher(L, 3), (const uint8_t *)iv, (const uint8_t *)key, (const uint8_t *)text, tsize); +} + +int lsm4_ctr_encrypt(lua_State *L) { + const char *iv; const char *key; const char *text; size_t tsize; + // lua_getarg(L, &iv, &key, &text, &tsize); + // return 1; + return lua_getarg(L, &iv, &key, &text, &tsize) && sm4_encrypt(L, get_cipher(L, 4), (const uint8_t *)iv, (const uint8_t *)key, (const uint8_t *)text, tsize); +} + + +/* SM4解密函数的分组类型封装 */ +int lsm4_cbc_decrypt(lua_State *L) { + const char *iv; const char *key; const char *cipher; size_t csize; + // lua_getarg(L, &iv, &key, &cipher, &csize); + // return 1; + return lua_getarg(L, &iv, &key, &cipher, &csize) && sm4_decrypt(L, get_cipher(L, 1), (const uint8_t *)iv, (const uint8_t *)key, (const uint8_t *)cipher, csize); +} + +int lsm4_ecb_decrypt(lua_State *L) { + const char *iv; const char *key; const char *cipher; size_t csize; + // lua_getarg(L, &iv, &key, &cipher, &csize); + // return 1; + return lua_getarg(L, &iv, &key, &cipher, &csize) && sm4_decrypt(L, get_cipher(L, 2), (const uint8_t *)iv, (const uint8_t *)key, (const uint8_t *)cipher, csize); +} + +int lsm4_ofb_decrypt(lua_State *L) { + const char *iv; const char *key; const char *cipher; size_t csize; + // lua_getarg(L, &iv, &key, &cipher, &csize); + // return 1; + return lua_getarg(L, &iv, &key, &cipher, &csize) && sm4_decrypt(L, get_cipher(L, 3), (const uint8_t *)iv, (const uint8_t *)key, (const uint8_t *)cipher, csize); +} + +int lsm4_ctr_decrypt(lua_State *L) { + const char *iv; const char *key; const char *cipher; size_t csize; + // lua_getarg(L, &iv, &key, &cipher, &csize); + // return 1; + return lua_getarg(L, &iv, &key, &cipher, &csize) && sm4_decrypt(L, get_cipher(L, 4), (const uint8_t *)iv, (const uint8_t *)key, (const uint8_t *)cipher, csize); +} + +// 读取私钥 +static inline EVP_PKEY* load_sm2prikey(lua_State *L) { + const char* private_keyname = luaL_checkstring(L, 1); + FILE *fp = fopen(private_keyname, "rb"); + if (!fp) + return luaL_error(L, "Can't find `SM`2 privatekey in [%s] file.", private_keyname), NULL; + + EVP_PKEY *sm2key = PEM_read_PrivateKey(fp, NULL, NULL, NULL); + if (!sm2key) { + fclose(fp); + return luaL_error(L, "Invalid `SM2` private key in [%s] file.", private_keyname), NULL; + } + fclose(fp); + return sm2key; +} + +// 读取公钥 +static inline EVP_PKEY* load_sm2pubkey(lua_State *L) { + const char* public_keyname = luaL_checkstring(L, 1); + FILE *fp = fopen(public_keyname, "rb"); + if (!fp) + return luaL_error(L, "Can't find `SM`2 publickey in [%s] file.", public_keyname), NULL; + + EVP_PKEY *sm2key = PEM_read_PUBKEY(fp, NULL, NULL, NULL); + if (!sm2key) { + fclose(fp); + return luaL_error(L, "Invalid `SM2` publickey key in [%s] file.", public_keyname), NULL; + } + fclose(fp); + return sm2key; +} + +/* 生成 SM2 `私钥`与`公钥` */ +static inline int sm2keygen(lua_State *L) { + const char* private_keyname = luaL_checkstring(L, 1); + const char* public_keyname = luaL_checkstring(L, 2); + + EVP_PKEY *sm2key = EVP_PKEY_new(); + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); + EVP_PKEY_keygen_init(pctx); + + if (!EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_sm2) || !EVP_PKEY_CTX_set_ec_param_enc(pctx, OPENSSL_EC_NAMED_CURVE) || EVP_PKEY_keygen(pctx, &sm2key) <= 0 ){ + EVP_PKEY_free(sm2key); + EVP_PKEY_CTX_free(pctx); + return luaL_error(L, "Generate SM2 key error."); + } + + FILE *private_fp = fopen(private_keyname, "wb"); + FILE *public_fp = fopen(public_keyname, "wb"); + if (!private_fp || !public_fp) { + if (private_fp) + fclose(public_fp); + if (public_fp) + fclose(private_fp); + EVP_PKEY_free(sm2key); + EVP_PKEY_CTX_free(pctx); + return luaL_error(L, "Write file failed after generate SM2 key."); + } + + if (1 != PEM_write_PrivateKey(private_fp, sm2key, NULL, NULL, 0, NULL, NULL) || 1 != PEM_write_PUBKEY(public_fp, sm2key)) { + fclose(private_fp); + fclose(public_fp); + EVP_PKEY_free(sm2key); + EVP_PKEY_CTX_free(pctx); + return luaL_error(L, "`SM2` privatekey/publickey write file failed."); + } + + fclose(private_fp); + fclose(public_fp); + // 回收内存 + EVP_PKEY_free(sm2key); + EVP_PKEY_CTX_free(pctx); + return 0; +} + +int lsm2keygen(lua_State *L){ + return sm2keygen(L); +} + +int lsm2sign(lua_State *L){ + size_t tsize = 0; + const char* text = luaL_checklstring(L, 2, &tsize); + + EVP_PKEY *sm2key = load_sm2prikey(L); + EVP_PKEY_set_alias_type(sm2key, EVP_PKEY_SM2); + + size_t osize = EVP_PKEY_size(sm2key); + const char *out = lua_newuserdata(L, osize); + + EVP_MD_CTX *md_ctx = EVP_MD_CTX_new(); + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(sm2key, NULL); + EVP_MD_CTX_set_pkey_ctx(md_ctx, pctx); + EVP_DigestSignInit(md_ctx, NULL, EVP_sm3(), NULL, sm2key); + + EVP_DigestSign(md_ctx, (uint8_t*)out, &osize, (uint8_t*)text, tsize); + + lua_pushlstring(L, out, osize); + + EVP_PKEY_free(sm2key); + EVP_PKEY_CTX_free(pctx); + EVP_MD_CTX_free(md_ctx); + return 1; +} + +int lsm2verify(lua_State *L){ + size_t tsize = 0; + const char* text = luaL_checklstring(L, 2, &tsize); + + size_t csize = 0; + const char* cipher = luaL_checklstring(L, 3, &csize); + + EVP_PKEY *sm2key = load_sm2pubkey(L); + EVP_PKEY_set_alias_type(sm2key, EVP_PKEY_SM2); + + EVP_MD_CTX *md_ctx = EVP_MD_CTX_new(); + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(sm2key, NULL); + EVP_MD_CTX_set_pkey_ctx(md_ctx, pctx); + EVP_DigestVerifyInit(md_ctx, NULL, EVP_sm3(), NULL, sm2key); + + if (1 == EVP_DigestVerify(md_ctx, (uint8_t*)cipher, csize, (uint8_t*)text, tsize)) + lua_pushboolean(L, 1); + else + lua_pushboolean(L, 0); + + EVP_PKEY_free(sm2key); + EVP_PKEY_CTX_free(pctx); + EVP_MD_CTX_free(md_ctx); + return 1; +} + +#endif \ No newline at end of file diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index 5c97a2b6..612aace1 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -47,6 +47,25 @@ local dhexchange = CRYPT.dhexchange local urlencode = CRYPT.urlencode local urldecode = CRYPT.urldecode +local sm3 = CRYPT.sm3 +local hmac_sm3 = CRYPT.hmac_sm3 +local sm3 = CRYPT.sm3 +local sm2keygen = CRYPT.sm2keygen +local sm2sign = CRYPT.sm2sign +local sm2verify = CRYPT.sm2verify + +local sm4_cbc_encrypt = CRYPT.sm4_cbc_encrypt +local sm4_cbc_decrypt = CRYPT.sm4_cbc_decrypt + +local sm4_ecb_encrypt = CRYPT.sm4_ecb_encrypt +local sm4_ecb_decrypt = CRYPT.sm4_ecb_decrypt + +local sm4_ofb_encrypt = CRYPT.sm4_ofb_encrypt +local sm4_ofb_decrypt = CRYPT.sm4_ofb_decrypt + +local sm4_ctr_encrypt = CRYPT.sm4_ctr_encrypt +local sm4_ctr_decrypt = CRYPT.sm4_ctr_decrypt + local aes_ecb_encrypt = CRYPT.aes_ecb_encrypt local aes_ecb_decrypt = CRYPT.aes_ecb_decrypt @@ -89,6 +108,14 @@ function crypt.guid(host) return guid(host or hostname(), hi, lo * 1e4 // 1) end +function crypt.sm3(str, hex) + local hash = sm3(str) + if hash and hex then + return hexencode(hash) + end + return hash +end + function crypt.md5(str, hex) local hash = md5(str) if hash and hex then @@ -147,6 +174,13 @@ function crypt.sha512 (str, hex) return hash end +function crypt.hmac_sm3 (key, text, hex) + local hash = hmac_sm3(key, text) + if hash and hex then + return hexencode(hash) + end + return hash +end function crypt.hmac_sha1 (key, text, hex) local hash = hmac_sha1(key, text) @@ -453,4 +487,94 @@ function crypt.rsa_verify(text, public_key_path, sign, algorithm, hex) return rsa_verify(text, public_key_path, sign, rsa_algorithms[(algorithm or ""):lower()] or rsa_algorithms["md5"]) end +-- 生成SM2私钥、公钥 +function crypt.sm2keygen(pri_path, pub_path) + return sm2keygen(pri_path, pub_path) +end + +-- SM3WithSM2签名 +function crypt.sm2sign(pri_path, text, b64) + local sign = sm2sign(pri_path, text) + if b64 then + sign = base64encode(sign) + end + return sign +end + +-- SM3WithSM2验签 +function crypt.sm2verify(pub_path, text, sign, b64) + if b64 then + sign = base64decode(sign) + end + return sm2verify(pub_path, text, sign) +end + +-- SM4分组加密算法之CBC +function crypt.sm4_cbc_encrypt(key, text, iv, b64) + local cipher = sm4_cbc_encrypt(key, text, iv) + if b64 then + cipher = base64encode(cipher) + end + return cipher +end + +-- SM4分组加密算法之CBC +function crypt.sm4_cbc_decrypt(key, cipher, iv, b64) + if b64 then + cipher = base64decode(cipher) + end + return sm4_cbc_decrypt(key, cipher, iv) +end + +-- SM4分组加密算法之ECB +function crypt.sm4_ecb_encrypt(key, text, iv, b64) + local cipher = sm4_ecb_encrypt(key, text, iv) + if b64 then + cipher = base64encode(cipher) + end + return cipher +end + +-- SM4分组解密算法之ECB +function crypt.sm4_ecb_decrypt(key, cipher, iv, b64) + if b64 then + cipher = base64decode(cipher) + end + return sm4_ecb_decrypt(key, cipher, iv) +end + +-- SM4分组加密算法之OFB +function crypt.sm4_ofb_encrypt(key, text, iv, b64) + local cipher = sm4_ofb_encrypt(key, text, iv) + if b64 then + cipher = base64encode(cipher) + end + return cipher +end + +-- SM4分组解密算法之OFB +function crypt.sm4_ofb_decrypt(key, cipher, iv, b64) + if b64 then + cipher = base64decode(cipher) + end + return sm4_ofb_decrypt(key, cipher, iv) +end + +-- SM4分组加密算法之CTR +function crypt.sm4_ctr_encrypt(key, text, iv, b64) + local cipher = sm4_ctr_encrypt(key, text, iv) + if b64 then + cipher = base64encode(cipher) + end + return cipher +end + +-- SM4分组解密算法之CTR +function crypt.sm4_ctr_decrypt(key, cipher, iv, b64) + if b64 then + cipher = base64decode(cipher) + end + return sm4_ctr_decrypt(key, cipher, iv) +end + return crypt \ No newline at end of file From f6fdf6fec0ae8cd835df99cc80596fd0eb344f4c Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 6 Dec 2020 15:12:26 +0800 Subject: [PATCH 657/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=B9=B6=E5=A2=9E=E5=8A=A0DES=E9=AB=98=E7=BA=A7=E7=AE=97?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/aes.c | 110 ++++----- luaclib/src/lcrypt/des.c | 154 +++++++++++++ luaclib/src/lcrypt/hmac.c | 62 +++--- luaclib/src/lcrypt/lcrypt.c | 52 +++-- luaclib/src/lcrypt/lcrypt.h | 21 +- lualib/crypt/init.lua | 428 +++++++++++++++++++++++++++++++++--- 6 files changed, 678 insertions(+), 149 deletions(-) diff --git a/luaclib/src/lcrypt/aes.c b/luaclib/src/lcrypt/aes.c index 7870e05f..0078bdbc 100644 --- a/luaclib/src/lcrypt/aes.c +++ b/luaclib/src/lcrypt/aes.c @@ -5,6 +5,7 @@ #define AES_CFB_MODE (2) #define AES_OFB_MODE (4) #define AES_CTR_MODE (8) +#define AES_GCM_MODE (16) #define aes_bit_to_ecb_evp(bit) (bit == 16 ? EVP_aes_128_ecb() : bit == 24 ? EVP_aes_192_ecb() : EVP_aes_256_ecb()) @@ -16,7 +17,9 @@ #define aes_bit_to_ctr_evp(bit) (bit == 16 ? EVP_aes_128_ctr() : bit == 24 ? EVP_aes_192_ctr() : EVP_aes_256_ctr()) -static inline const EVP_CIPHER * get_cipher(size_t mode, size_t bit) { +#define aes_bit_to_gcm_evp(bit) (bit == 16 ? EVP_aes_128_gcm() : bit == 24 ? EVP_aes_192_gcm() : EVP_aes_256_gcm()) + +static inline const EVP_CIPHER * aes_get_cipher(size_t mode, size_t bit) { switch(mode){ case AES_ECB_MODE: return aes_bit_to_ecb_evp(bit); @@ -28,6 +31,8 @@ static inline const EVP_CIPHER * get_cipher(size_t mode, size_t bit) { return aes_bit_to_ofb_evp(bit); case AES_CTR_MODE: return aes_bit_to_ctr_evp(bit); + case AES_GCM_MODE: + return aes_bit_to_gcm_evp(bit); } return NULL; } @@ -39,7 +44,11 @@ static inline int do_aes_encrypt(lua_State *L, int bit, const uint8_t *key, cons if (!ctx) return luaL_error(L, "allocate EVP failed."); - if (1 != EVP_EncryptInit_ex(ctx, get_cipher(aes_mode, bit), NULL, key, iv)){ + EVP_CIPHER_CTX_set_padding(ctx, 0); + + // EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, bit, NULL); + + if (1 != EVP_EncryptInit_ex(ctx, aes_get_cipher(aes_mode, bit), NULL, key, iv)){ EVP_CIPHER_CTX_cleanup(ctx); EVP_CIPHER_CTX_free(ctx); lua_pushnil(L); @@ -85,7 +94,11 @@ static inline int do_aes_decrypt(lua_State *L, int bit, const uint8_t *key, cons if (!ctx) return luaL_error(L, "allocate EVP failed."); - if (1 != EVP_DecryptInit_ex(ctx, get_cipher(aes_mode, bit), NULL, key, iv)){ + EVP_CIPHER_CTX_set_padding(ctx, 0); + + // EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, bit, NULL); + + if (1 != EVP_DecryptInit_ex(ctx, aes_get_cipher(aes_mode, bit), NULL, key, iv)){ EVP_CIPHER_CTX_cleanup(ctx); EVP_CIPHER_CTX_free(ctx); lua_pushnil(L); @@ -147,97 +160,84 @@ static inline int lua_getargs(lua_State *L, lua_Integer *bit, uint8_t **text, si return 1; } -/* 加密封装 */ - -int laes_ecb_encrypt(lua_State *L) { +static inline int AES_ENCRYPT(lua_State *L, int mode) { lua_Integer bit = 0; size_t text_sz = 0; uint8_t* iv = NULL; uint8_t* key = NULL; uint8_t* text = NULL; - return lua_getargs(L, &bit, &text, &text_sz, &iv, &key) && do_aes_encrypt(L, bit, (const uint8_t*)key, (const uint8_t*)iv, (const uint8_t*)text, text_sz, AES_ECB_MODE); + return lua_getargs(L, &bit, &text, &text_sz, &iv, &key) && do_aes_encrypt(L, bit, (const uint8_t*)key, (const uint8_t*)iv, (const uint8_t*)text, text_sz, mode); } -int laes_cbc_encrypt(lua_State *L) { +static inline int AES_DECRYPT(lua_State *L, int mode) { - lua_Integer bit = 0; size_t text_sz = 0; + lua_Integer bit = 0; size_t cipher_sz = 0; - uint8_t* iv = NULL; uint8_t* key = NULL; uint8_t* text = NULL; + uint8_t* iv = NULL; uint8_t* key = NULL; uint8_t* cipher = NULL; - return lua_getargs(L, &bit, &text, &text_sz, &iv, &key) && do_aes_encrypt(L, bit, (const uint8_t*)key, (const uint8_t*)iv, (const uint8_t*)text, text_sz, AES_CBC_MODE); + return lua_getargs(L, &bit, &cipher, &cipher_sz, &iv, &key) && do_aes_decrypt(L, bit, (const uint8_t*)key, (const uint8_t*)iv, (const uint8_t*)cipher, cipher_sz, mode); } -// int laes_cfb_encrypt(lua_State *L) { +/* 加密封装 */ -// lua_Integer bit = 0; size_t text_sz = 0; +int laes_ecb_encrypt(lua_State *L) { -// uint8_t* iv; uint8_t* key; uint8_t* text; + return AES_ENCRYPT(L, AES_ECB_MODE); +} -// return lua_getargs(L, &bit, &text, &text_sz, &iv, &key) && do_aes_encrypt(L, bit, (const uint8_t*)key, (const uint8_t*)iv, (const uint8_t*)text, text_sz, AES_CFB_MODE); -// } +int laes_cbc_encrypt(lua_State *L) { -// int laes_ofb_encrypt(lua_State *L) { + return AES_ENCRYPT(L, AES_CBC_MODE); +} -// lua_Integer bit = 0; size_t text_sz = 0; +int laes_cfb_encrypt(lua_State *L) { -// uint8_t* iv; uint8_t* key; uint8_t* text; + return AES_ENCRYPT(L, AES_CFB_MODE); +} -// return lua_getargs(L, &bit, &text, &text_sz, &iv, &key) && do_aes_encrypt(L, bit, (const uint8_t*)key, (const uint8_t*)iv, (const uint8_t*)text, text_sz, AES_OFB_MODE); -// } +int laes_ofb_encrypt(lua_State *L) { -// int laes_ctr_encrypt(lua_State *L) { + return AES_ENCRYPT(L, AES_OFB_MODE); +} -// lua_Integer bit = 0; size_t text_sz = 0; +int laes_ctr_encrypt(lua_State *L) { -// uint8_t* iv; uint8_t* key; uint8_t* text; + return AES_ENCRYPT(L, AES_CTR_MODE); +} -// return lua_getargs(L, &bit, &text, &text_sz, &iv, &key) && do_aes_encrypt(L, bit, (const uint8_t*)key, (const uint8_t*)iv, (const uint8_t*)text, text_sz, AES_CTR_MODE); -// } +int laes_gcm_encrypt(lua_State *L) { + return AES_ENCRYPT(L, AES_GCM_MODE); +} /* 解密封装 */ int laes_ecb_decrypt(lua_State *L) { - lua_Integer bit = 0; size_t cipher_sz = 0; - - uint8_t* iv = NULL; uint8_t* key = NULL; uint8_t* cipher = NULL; - - return lua_getargs(L, &bit, &cipher, &cipher_sz, &iv, &key) && do_aes_decrypt(L, bit, (const uint8_t*)key, (const uint8_t*)iv, (const uint8_t*)cipher, cipher_sz, AES_ECB_MODE); + return AES_DECRYPT(L, AES_ECB_MODE); } int laes_cbc_decrypt(lua_State *L) { - lua_Integer bit = 0; size_t cipher_sz = 0; - - uint8_t* iv = NULL; uint8_t* key = NULL; uint8_t* cipher = NULL; - - return lua_getargs(L, &bit, &cipher, &cipher_sz, &iv, &key) && do_aes_decrypt(L, bit, (const uint8_t*)key, (const uint8_t*)iv, (const uint8_t*)cipher, cipher_sz, AES_CBC_MODE); + return AES_DECRYPT(L, AES_CBC_MODE); } -// int laes_cfb_decrypt(lua_State *L) { - -// lua_Integer bit = 0; size_t cipher_sz = 0; - -// uint8_t* iv; uint8_t* key; uint8_t* cipher; - -// return lua_getargs(L, &bit, &cipher, &cipher_sz, &iv, &key) && do_aes_decrypt(L, bit, (const uint8_t*)key, (const uint8_t*)iv, (const uint8_t*)cipher, cipher_sz, AES_CFB_MODE); -// } +int laes_cfb_decrypt(lua_State *L) { -// int laes_ofb_decrypt(lua_State *L) { - -// lua_Integer bit = 0; size_t cipher_sz = 0; + return AES_DECRYPT(L, AES_CFB_MODE); +} -// uint8_t* iv; uint8_t* key; uint8_t* cipher; +int laes_ofb_decrypt(lua_State *L) { -// return lua_getargs(L, &bit, &cipher, &cipher_sz, &iv, &key) && do_aes_decrypt(L, bit, (const uint8_t*)key, (const uint8_t*)iv, (const uint8_t*)cipher, cipher_sz, AES_OFB_MODE); -// } + return AES_DECRYPT(L, AES_OFB_MODE); +} -// int laes_ctr_decrypt(lua_State *L) { +int laes_ctr_decrypt(lua_State *L) { -// lua_Integer bit = 0; size_t cipher_sz = 0; + return AES_DECRYPT(L, AES_CTR_MODE); +} -// uint8_t* iv; uint8_t* key; uint8_t* cipher; +int laes_gcm_decrypt(lua_State *L) { -// return lua_getargs(L, &bit, &cipher, &cipher_sz, &iv, &key) && do_aes_decrypt(L, bit, (const uint8_t*)key, (const uint8_t*)iv, (const uint8_t*)cipher, cipher_sz, AES_CTR_MODE); -// } \ No newline at end of file + return AES_DECRYPT(L, AES_GCM_MODE); +} \ No newline at end of file diff --git a/luaclib/src/lcrypt/des.c b/luaclib/src/lcrypt/des.c index 8d82c519..5a45d174 100644 --- a/luaclib/src/lcrypt/des.c +++ b/luaclib/src/lcrypt/des.c @@ -405,3 +405,157 @@ int ldesdecode(lua_State *L) { lua_pushlstring(L, (const char *)buffer, textsz - padding); return 1; } + +static inline const EVP_CIPHER * des_get_cipher(size_t mode) { + switch(mode){ + case 0: + return EVP_desx_cbc(); + case 1: + return EVP_des_cbc(); + case 2: + return EVP_des_ecb(); + case 3: + return EVP_des_cfb(); + case 4: + return EVP_des_ofb(); + case 5: + return EVP_des_ede(); + case 6: + return EVP_des_ede3(); + case 7: + return EVP_des_ede_ecb(); + case 8: + return EVP_des_ede3_ecb(); + } + return NULL; +} + +// 加密函数 +static inline int do_des_encrypt(lua_State *L, size_t des_mode, const uint8_t *key, const uint8_t *iv, const uint8_t *text, size_t tsize) { + + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + if (!ctx) + return luaL_error(L, "allocate EVP failed."); + + EVP_CIPHER_CTX_set_padding(ctx, 0); + + if (1 != EVP_EncryptInit_ex(ctx, des_get_cipher(des_mode), NULL, key, iv)){ + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + lua_pushnil(L); + lua_pushstring(L, "des_encrypt_init failed."); + return 2; + } + + EVP_CIPHER_CTX_set_key_length(ctx, EVP_MAX_KEY_LENGTH); + + int out_size = tsize + EVP_MAX_BLOCK_LENGTH; + uint8_t *out = lua_newuserdata(L, out_size); + + int update_len = out_size; + if (0 == EVP_EncryptUpdate(ctx, out, &update_len, text, tsize)){ + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + lua_pushnil(L); + lua_pushstring(L, "des_encrypt_update failed."); + return 2; + } + + int final_len = out_size; + if (0 == EVP_EncryptFinal(ctx, out + update_len, &final_len)){ + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + lua_pushnil(L); + lua_pushstring(L, "des_encrypt_final failed."); + return 2; + } + + lua_pushlstring(L, (const char*)out, update_len + final_len); + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return 1; +} + +// 解密函数 +static inline int do_des_decrypt(lua_State *L, size_t des_mode, const uint8_t *key, const uint8_t *iv, const uint8_t *cipher, size_t csize) { + + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + if (!ctx) + return luaL_error(L, "allocate EVP failed."); + + EVP_CIPHER_CTX_set_padding(ctx, 0); + + if (1 != EVP_DecryptInit_ex(ctx, des_get_cipher(des_mode), NULL, key, iv)){ + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + lua_pushnil(L); + lua_pushstring(L, "des_decrypt_init failed."); + return 2; + } + + EVP_CIPHER_CTX_set_key_length(ctx, EVP_MAX_KEY_LENGTH); + + int out_size = csize + EVP_MAX_BLOCK_LENGTH; + uint8_t *out = lua_newuserdata(L, out_size); + + int update_len = out_size; + if (1 != EVP_DecryptUpdate(ctx, out, &update_len, cipher, csize)){ + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + lua_pushnil(L); + lua_pushstring(L, "des_decrypt_update failed."); + return 2; + } + + int final_len = out_size; + if (1 != EVP_DecryptFinal_ex(ctx, out + update_len, &final_len)){ + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + lua_pushnil(L); + lua_pushstring(L, "des_decrypt_final failed."); + return 2; + } + + lua_pushlstring(L, (const char*)out, update_len + final_len); + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + return 1; +} + +static inline int lua_getargs(lua_State *L, size_t *des_mode, uint8_t **text, size_t *tsize, uint8_t **iv, uint8_t **key) { + *des_mode = luaL_checkinteger(L, 1); + if (*des_mode > 8) + return luaL_error(L, "Invalid des_mode"); + + *key = (uint8_t *)luaL_checkstring(L, 2); + if (!key) + return luaL_error(L, "Invalid key"); + + size_t size = 0; + *text = (uint8_t *)luaL_checklstring(L, 3, &size); + if (!text) + return luaL_error(L, "Invalid text"); + *tsize = size; + + *iv = (uint8_t *)luaL_checkstring(L, 4); + if (!iv) + return luaL_error(L, "Invalid iv"); + + return 1; +} + +int ldes_encrypt(lua_State *L) { + size_t text_sz = 0; size_t mode = 0; + + uint8_t* iv = NULL; uint8_t* key = NULL; uint8_t* text = NULL; + + return lua_getargs(L, &mode, &text, &text_sz, &iv, &key) && do_des_encrypt(L, mode, (const uint8_t*)key, (const uint8_t*)iv, (const uint8_t*)text, text_sz); +} + +int ldes_decrypt(lua_State *L) { + size_t cipher_sz = 0; size_t mode = 0; + + uint8_t* iv = NULL; uint8_t* key = NULL; uint8_t* cipher = NULL; + + return lua_getargs(L, &mode, &cipher, &cipher_sz, &iv, &key) && do_des_decrypt(L, mode, (const uint8_t*)key, (const uint8_t*)iv, (const uint8_t*)cipher, cipher_sz); +} \ No newline at end of file diff --git a/luaclib/src/lcrypt/hmac.c b/luaclib/src/lcrypt/hmac.c index 1d71f30b..7eeec369 100644 --- a/luaclib/src/lcrypt/hmac.c +++ b/luaclib/src/lcrypt/hmac.c @@ -51,26 +51,26 @@ int lhmac_sha128(lua_State *L) { return 1; }; -// int lhmac_sha224(lua_State *L) { -// size_t key_sz = 0; -// size_t text_sz = 0; +int lhmac_sha224(lua_State *L) { + size_t key_sz = 0; + size_t text_sz = 0; -// const char * key = luaL_checklstring(L, 1, &key_sz); -// if (!key || key_sz <= 0) -// return luaL_error(L, "Invalid key value."); + const char * key = luaL_checklstring(L, 1, &key_sz); + if (!key || key_sz <= 0) + return luaL_error(L, "Invalid key value."); -// const char * text = luaL_checklstring(L, 2, &text_sz); -// if (!text || text_sz <= 0) -// return luaL_error(L, "Invalid text value."); + const char * text = luaL_checklstring(L, 2, &text_sz); + if (!text || text_sz <= 0) + return luaL_error(L, "Invalid text value."); -// uint32_t result_len = SHA224_DIGEST_LENGTH; -// unsigned char result[result_len]; + uint32_t result_len = SHA224_DIGEST_LENGTH; + unsigned char result[result_len]; -// HMAC(EVP_sha224(), key, key_sz, (const unsigned char*)text, text_sz, result, &result_len); + HMAC(EVP_sha224(), key, key_sz, (const unsigned char*)text, text_sz, result, &result_len); -// lua_pushlstring(L, (const char *)result, result_len); -// return 1; -// }; + lua_pushlstring(L, (const char *)result, result_len); + return 1; +}; int lhmac_sha256(lua_State *L) { size_t key_sz = 0; @@ -91,24 +91,24 @@ int lhmac_sha256(lua_State *L) { return 1; }; -// int lhmac_sha384(lua_State *L) { -// size_t key_sz = 0; -// size_t text_sz = 0; +int lhmac_sha384(lua_State *L) { + size_t key_sz = 0; + size_t text_sz = 0; -// const char * key = luaL_checklstring(L, 1, &key_sz); -// if (!key || key_sz <= 0) -// return luaL_error(L, "Invalid key value."); + const char * key = luaL_checklstring(L, 1, &key_sz); + if (!key || key_sz <= 0) + return luaL_error(L, "Invalid key value."); -// const char * text = luaL_checklstring(L, 2, &text_sz); -// if (!text || text_sz <= 0) -// return luaL_error(L, "Invalid text value."); + const char * text = luaL_checklstring(L, 2, &text_sz); + if (!text || text_sz <= 0) + return luaL_error(L, "Invalid text value."); -// uint32_t result_len = SHA384_DIGEST_LENGTH; -// unsigned char result[result_len]; -// HMAC(EVP_sha384(), key, key_sz, (const unsigned char*)text, text_sz, result, &result_len); -// lua_pushlstring(L, (const char *)result, result_len); -// return 1; -// }; + uint32_t result_len = SHA384_DIGEST_LENGTH; + unsigned char result[result_len]; + HMAC(EVP_sha384(), key, key_sz, (const unsigned char*)text, text_sz, result, &result_len); + lua_pushlstring(L, (const char *)result, result_len); + return 1; +}; int lhmac_sha512(lua_State *L) { size_t key_sz = 0; @@ -122,7 +122,7 @@ int lhmac_sha512(lua_State *L) { if (!text || text_sz <= 0) return luaL_error(L, "Invalid text value."); - uint32_t result_len = SHA384_DIGEST_LENGTH; + uint32_t result_len = SHA512_DIGEST_LENGTH; unsigned char result[result_len]; HMAC(EVP_sha512(), key, key_sz, (const unsigned char*)text, text_sz, result, &result_len); lua_pushlstring(L, (const char *)result, result_len); diff --git a/luaclib/src/lcrypt/lcrypt.c b/luaclib/src/lcrypt/lcrypt.c index 7d702fc8..b07e18e4 100644 --- a/luaclib/src/lcrypt/lcrypt.c +++ b/luaclib/src/lcrypt/lcrypt.c @@ -73,8 +73,6 @@ LUAMOD_API int luaopen_lcrypt(lua_State *L) { { "guid", lguid }, { "hashkey", lhashkey }, { "randomkey", lrandomkey }, - { "desencode", ldesencode }, - { "desdecode", ldesdecode }, { "hexencode", ltohex }, { "hexdecode", lfromhex }, { "hmac64", lhmac64 }, @@ -99,9 +97,9 @@ LUAMOD_API int luaopen_lcrypt(lua_State *L) { { "hmac_md5", lhmac_md5 }, { "hmac_sha1", lhmac_sha128 }, { "hmac_sha128", lhmac_sha128 }, - // { "hmac_sha224", lhmac_sha224 }, + { "hmac_sha224", lhmac_sha224 }, { "hmac_sha256", lhmac_sha256 }, - // { "hmac_sha384", lhmac_sha384 }, + { "hmac_sha384", lhmac_sha384 }, { "hmac_sha512", lhmac_sha512 }, { "hmac_hash", lhmac_hash }, { "xor_str", lxor_str }, @@ -117,28 +115,36 @@ LUAMOD_API int luaopen_lcrypt(lua_State *L) { // aes 加密 { "aes_ecb_encrypt", laes_ecb_encrypt }, { "aes_cbc_encrypt", laes_cbc_encrypt }, - // {"aes_cfb_encrypt", laes_cfb_encrypt}, - // {"aes_ofb_encrypt", laes_ofb_encrypt}, - // {"aes_ctr_encrypt", laes_ctr_encrypt}, + { "aes_cfb_encrypt", laes_cfb_encrypt }, + { "aes_ofb_encrypt", laes_ofb_encrypt }, + { "aes_ctr_encrypt", laes_ctr_encrypt }, + { "aes_gcm_encrypt", laes_gcm_encrypt }, // aes 解密 { "aes_ecb_decrypt", laes_ecb_decrypt }, { "aes_cbc_decrypt", laes_cbc_decrypt }, - // {"aes_cfb_decrypt", laes_cfb_decrypt}, - // {"aes_ofb_decrypt", laes_ofb_decrypt}, - // {"aes_ctr_decrypt", laes_ctr_decrypt}, - {"sm3", lsm3}, - {"hmac_sm3", lhmac_sm3}, - {"sm2keygen", lsm2keygen}, - {"sm2sign", lsm2sign}, - {"sm2verify", lsm2verify}, - {"sm4_cbc_encrypt", lsm4_cbc_encrypt}, - {"sm4_cbc_decrypt", lsm4_cbc_decrypt}, - {"sm4_ecb_encrypt", lsm4_ecb_encrypt}, - {"sm4_ecb_decrypt", lsm4_ecb_decrypt}, - {"sm4_ofb_encrypt", lsm4_ofb_encrypt}, - {"sm4_ofb_decrypt", lsm4_ofb_decrypt}, - {"sm4_ctr_encrypt", lsm4_ctr_encrypt}, - {"sm4_ctr_decrypt", lsm4_ctr_decrypt}, + { "aes_cfb_decrypt", laes_cfb_decrypt }, + { "aes_ofb_decrypt", laes_ofb_decrypt }, + { "aes_ctr_decrypt", laes_ctr_decrypt }, + { "aes_gcm_decrypt", laes_gcm_decrypt }, + // DES加密/解密 + { "desencode", ldesencode }, + { "desdecode", ldesdecode }, + { "des_encrypt", ldes_encrypt }, + { "des_decrypt", ldes_decrypt }, + // SM2/SM3/SM4 国密 + { "sm3", lsm3 }, + { "hmac_sm3", lhmac_sm3 }, + { "sm2keygen", lsm2keygen }, + { "sm2sign", lsm2sign }, + { "sm2verify", lsm2verify }, + { "sm4_cbc_encrypt", lsm4_cbc_encrypt }, + { "sm4_cbc_decrypt", lsm4_cbc_decrypt }, + { "sm4_ecb_encrypt", lsm4_ecb_encrypt }, + { "sm4_ecb_decrypt", lsm4_ecb_decrypt }, + { "sm4_ofb_encrypt", lsm4_ofb_encrypt }, + { "sm4_ofb_decrypt", lsm4_ofb_decrypt }, + { "sm4_ctr_encrypt", lsm4_ctr_encrypt }, + { "sm4_ctr_decrypt", lsm4_ctr_decrypt }, { NULL, NULL }, }; luaL_newlib(L, lcrypt); diff --git a/luaclib/src/lcrypt/lcrypt.h b/luaclib/src/lcrypt/lcrypt.h index a914c930..0870ac7b 100644 --- a/luaclib/src/lcrypt/lcrypt.h +++ b/luaclib/src/lcrypt/lcrypt.h @@ -28,6 +28,9 @@ int lurldecode(lua_State *L); int ldesencode(lua_State *L); int ldesdecode(lua_State *L); +int ldes_encrypt(lua_State *L); +int ldes_decrypt(lua_State *L); + int ldhsecret(lua_State *L); int ldhexchange(lua_State *L); @@ -46,22 +49,24 @@ int lsha512(lua_State *L); int lhmac_md5(lua_State *L); int lhmac_sha128(lua_State *L); -// int lhmac_sha224(lua_State *L); +int lhmac_sha224(lua_State *L); int lhmac_sha256(lua_State *L); -// int lhmac_sha384(lua_State *L); +int lhmac_sha384(lua_State *L); int lhmac_sha512(lua_State *L); int laes_ecb_encrypt(lua_State *L); int laes_cbc_encrypt(lua_State *L); -// int laes_cfb_encrypt(lua_State *L); -// int laes_ofb_encrypt(lua_State *L); -// int laes_ctr_encrypt(lua_State *L); +int laes_cfb_encrypt(lua_State *L); +int laes_ofb_encrypt(lua_State *L); +int laes_ctr_encrypt(lua_State *L); +int laes_gcm_encrypt(lua_State *L); int laes_ecb_decrypt(lua_State *L); int laes_cbc_decrypt(lua_State *L); -// int laes_cfb_decrypt(lua_State *L); -// int laes_ofb_decrypt(lua_State *L); -// int laes_ctr_decrypt(lua_State *L); +int laes_cfb_decrypt(lua_State *L); +int laes_ofb_decrypt(lua_State *L); +int laes_ctr_decrypt(lua_State *L); +int laes_gcm_decrypt(lua_State *L); int lrsa_public_key_encode(lua_State *L); int lrsa_private_key_decode(lua_State *L); diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index 612aace1..0232dad3 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -20,7 +20,9 @@ local sha384 = CRYPT.sha384 local sha512 = CRYPT.sha512 local hmac_sha1 = CRYPT.hmac_sha1 +local hmac_sha224 = CRYPT.hmac_sha224 local hmac_sha256 = CRYPT.hmac_sha256 +local hmac_sha384 = CRYPT.hmac_sha384 local hmac_sha512 = CRYPT.hmac_sha512 local crc32 = CRYPT.crc32 @@ -41,6 +43,9 @@ local hexdecode = CRYPT.hexdecode local desencode = CRYPT.desencode local desdecode = CRYPT.desdecode +local des_encrypt = CRYPT.des_encrypt +local des_decrypt = CRYPT.des_decrypt + local dhsecret = CRYPT.dhsecret local dhexchange = CRYPT.dhexchange @@ -72,6 +77,18 @@ local aes_ecb_decrypt = CRYPT.aes_ecb_decrypt local aes_cbc_encrypt = CRYPT.aes_cbc_encrypt local aes_cbc_decrypt = CRYPT.aes_cbc_decrypt +local aes_cfb_encrypt = CRYPT.aes_cfb_encrypt +local aes_cfb_decrypt = CRYPT.aes_cfb_decrypt + +local aes_ofb_encrypt = CRYPT.aes_ofb_encrypt +local aes_ofb_decrypt = CRYPT.aes_ofb_decrypt + +local aes_ctr_encrypt = CRYPT.aes_ctr_encrypt +local aes_ctr_decrypt = CRYPT.aes_ctr_decrypt + +local aes_gcm_encrypt = CRYPT.aes_gcm_encrypt +local aes_gcm_decrypt = CRYPT.aes_gcm_decrypt + -- 填充方式 local RSA_NO_PADDING = CRYPT.RSA_NO_PADDING local RSA_PKCS1_PADDING = CRYPT.RSA_PKCS1_PADDING @@ -124,14 +141,6 @@ function crypt.md5(str, hex) return hash end -function crypt.hmac_md5 (key, text, hex) - local hash = hmac_md5(key, text) - if hash and hex then - return hexencode(hash) - end - return hash -end - function crypt.sha1(str, hex) local hash = sha1(str) if hash and hex then @@ -174,6 +183,16 @@ function crypt.sha512 (str, hex) return hash end +-- 哈希信息摘要码方法(常见) + +function crypt.hmac_md5 (key, text, hex) + local hash = hmac_md5(key, text) + if hash and hex then + return hexencode(hash) + end + return hash +end + function crypt.hmac_sm3 (key, text, hex) local hash = hmac_sm3(key, text) if hash and hex then @@ -192,6 +211,14 @@ end crypt.hmac_sha128 = crypt.hmac_sha1 +function crypt.hmac_sha224(key, text, hex) + local hash = hmac_sha224(key, text) + if hex then + hash = hexencode(hash) + end + return hash +end + function crypt.hmac_sha256 (key, text, hex) local hash = hmac_sha256(key, text) if hash and hex then @@ -200,6 +227,14 @@ function crypt.hmac_sha256 (key, text, hex) return hash end +function crypt.hmac_sha384(key, text, hex) + local hash = hmac_sha384(key, text) + if hex then + hash = hexencode(hash) + end + return hash +end + function crypt.hmac_sha512 (key, text, hex) local hash = hmac_sha512(key, text) if hash and hex then @@ -240,6 +275,8 @@ function crypt.hashkey (key, hex) return hash end +-- 哈希信息摘要码方法(特殊) + function crypt.hmac_hash (key, text, hex) local hash = hmac_hash(key, text) if hash and hex then @@ -264,8 +301,10 @@ function crypt.hmac64_md5 (key, text, hex) return hash end +-- 高级对称分组解密方法 + function crypt.aes_128_cbc_encrypt(key, text, iv, hex) - local hash = aes_cbc_encrypt(16, #key == 16 and key or nil, text, iv) + local hash = aes_cbc_encrypt(16, key, text, iv) if hash and hex then return hexencode(hash) end @@ -273,7 +312,39 @@ function crypt.aes_128_cbc_encrypt(key, text, iv, hex) end function crypt.aes_128_ecb_encrypt(key, text, iv, hex) - local hash = aes_ecb_encrypt(16, #key == 16 and key or nil, text, iv) + local hash = aes_ecb_encrypt(16, key, text, iv) + if hash and hex then + return hexencode(hash) + end + return hash +end + +function crypt.aes_128_cfb_encrypt(key, text, iv, hex) + local hash = aes_cfb_encrypt(16, key, text, iv) + if hash and hex then + return hexencode(hash) + end + return hash +end + +function crypt.aes_128_ofb_encrypt(key, text, iv, hex) + local hash = aes_ofb_encrypt(16, key, text, iv) + if hash and hex then + return hexencode(hash) + end + return hash +end + +function crypt.aes_128_ctr_encrypt(key, text, iv, hex) + local hash = aes_ctr_encrypt(16, key, text, iv) + if hash and hex then + return hexencode(hash) + end + return hash +end + +function crypt.aes_128_gcm_encrypt(key, text, iv, hex) + local hash = aes_gcm_encrypt(16, key, text, iv) if hash and hex then return hexencode(hash) end @@ -281,7 +352,7 @@ function crypt.aes_128_ecb_encrypt(key, text, iv, hex) end function crypt.aes_192_cbc_encrypt(key, text, iv, hex) - local hash = aes_cbc_encrypt(24, #key == 24 and key or nil, text, iv) + local hash = aes_cbc_encrypt(24, key, text, iv) if hash and hex then return hexencode(hash) end @@ -289,7 +360,39 @@ function crypt.aes_192_cbc_encrypt(key, text, iv, hex) end function crypt.aes_192_ecb_encrypt(key, text, iv, hex) - local hash = aes_ecb_encrypt(24, #key == 24 and key or nil, text, iv) + local hash = aes_ecb_encrypt(24, key, text, iv) + if hash and hex then + return hexencode(hash) + end + return hash +end + +function crypt.aes_192_cfb_encrypt(key, text, iv, hex) + local hash = aes_cfb_encrypt(24, key, text, iv) + if hash and hex then + return hexencode(hash) + end + return hash +end + +function crypt.aes_192_ofb_encrypt(key, text, iv, hex) + local hash = aes_ofb_encrypt(24, key, text, iv) + if hash and hex then + return hexencode(hash) + end + return hash +end + +function crypt.aes_192_ctr_encrypt(key, text, iv, hex) + local hash = aes_ctr_encrypt(24, key, text, iv) + if hash and hex then + return hexencode(hash) + end + return hash +end + +function crypt.aes_192_gcm_encrypt(key, text, iv, hex) + local hash = aes_gcm_encrypt(24, key, text, iv) if hash and hex then return hexencode(hash) end @@ -297,7 +400,7 @@ function crypt.aes_192_ecb_encrypt(key, text, iv, hex) end function crypt.aes_256_cbc_encrypt(key, text, iv, hex) - local hash = aes_cbc_encrypt(32, #key == 32 and key or nil, text, iv) + local hash = aes_cbc_encrypt(32, key, text, iv) if hash and hex then return hexencode(hash) end @@ -305,53 +408,171 @@ function crypt.aes_256_cbc_encrypt(key, text, iv, hex) end function crypt.aes_256_ecb_encrypt(key, text, iv, hex) - local hash = aes_ecb_encrypt(32, #key == 32 and key or nil, text, iv) + local hash = aes_ecb_encrypt(32, key, text, iv) + if hash and hex then + return hexencode(hash) + end + return hash +end + +function crypt.aes_256_cfb_encrypt(key, text, iv, hex) + local hash = aes_cfb_encrypt(32, key, text, iv) + if hash and hex then + return hexencode(hash) + end + return hash +end + +function crypt.aes_256_ofb_encrypt(key, text, iv, hex) + local hash = aes_ofb_encrypt(32, key, text, iv) + if hash and hex then + return hexencode(hash) + end + return hash +end + +function crypt.aes_256_ctr_encrypt(key, text, iv, hex) + local hash = aes_ctr_encrypt(32, key, text, iv) + if hash and hex then + return hexencode(hash) + end + return hash +end + +function crypt.aes_256_gcm_encrypt(key, text, iv, hex) + local hash = aes_gcm_encrypt(32, key, text, iv) if hash and hex then return hexencode(hash) end return hash end -function crypt.aes_128_cbc_decrypt(key, text, iv, hex) +-- 高级对称分组解密方法 + +function crypt.aes_128_cbc_decrypt(key, cipher, iv, hex) if hex then - text = hexdecode(text) + cipher = hexdecode(cipher) end - return aes_cbc_decrypt(16, #key == 16 and key or nil, text, iv) + return aes_cbc_decrypt(16, key, cipher, iv) end -function crypt.aes_128_ecb_decrypt(key, text, iv, hex) +function crypt.aes_128_ecb_decrypt(key, cipher, iv, hex) if hex then - text = hexdecode(text) + cipher = hexdecode(cipher) end - return aes_ecb_decrypt(16, #key == 16 and key or nil, text, iv) + return aes_ecb_decrypt(16, key, cipher, iv) end -function crypt.aes_192_cbc_decrypt(key, text, iv, hex) +function crypt.aes_128_cfb_decrypt(key, cipher, iv, hex) if hex then - text = hexdecode(text) + cipher = hexdecode(cipher) end - return aes_cbc_decrypt(24, #key == 24 and key or nil, text, iv) + return aes_cfb_decrypt(16, key, cipher, iv) end -function crypt.aes_192_ecb_decrypt(key, text, iv, hex) +function crypt.aes_128_ofb_decrypt(key, cipher, iv, hex) if hex then - text = hexdecode(text) + cipher = hexdecode(cipher) end - return aes_ecb_decrypt(24, #key == 24 and key or nil, text, iv) + return aes_ofb_decrypt(16, key, cipher, iv) end -function crypt.aes_256_cbc_decrypt(key, text, iv, hex) +function crypt.aes_128_ctr_decrypt(key, cipher, iv, hex) if hex then - text = hexdecode(text) + cipher = hexdecode(cipher) end - return aes_cbc_decrypt(32, #key == 32 and key or nil, text, iv) + return aes_ctr_decrypt(16, key, cipher, iv) end -function crypt.aes_256_ecb_decrypt(key, text, iv, hex) +function crypt.aes_128_gcm_decrypt(key, cipher, iv, hex) if hex then - text = hexdecode(text) + cipher = hexdecode(cipher) + end + return aes_gcm_decrypt(16, key, cipher, iv) +end + +function crypt.aes_192_cbc_decrypt(key, cipher, iv, hex) + if hex then + cipher = hexdecode(cipher) + end + return aes_cbc_decrypt(24, key, cipher, iv) +end + +function crypt.aes_192_ecb_decrypt(key, cipher, iv, hex) + if hex then + cipher = hexdecode(cipher) + end + return aes_ecb_decrypt(24, key, cipher, iv) +end + +function crypt.aes_192_cfb_decrypt(key, cipher, iv, hex) + if hex then + cipher = hexdecode(cipher) end - return aes_ecb_decrypt(32, #key == 32 and key or nil, text, iv) + return aes_cfb_decrypt(24, key, cipher, iv) +end + +function crypt.aes_192_ofb_decrypt(key, cipher, iv, hex) + if hex then + cipher = hexdecode(cipher) + end + return aes_ofb_decrypt(24, key, cipher, iv) +end + +function crypt.aes_192_ctr_decrypt(key, cipher, iv, hex) + if hex then + cipher = hexdecode(cipher) + end + return aes_ctr_decrypt(24, key, cipher, iv) +end + +function crypt.aes_192_gcm_decrypt(key, cipher, iv, hex) + if hex then + cipher = hexdecode(cipher) + end + return aes_gcm_decrypt(24, key, cipher, iv) +end + +function crypt.aes_256_cbc_decrypt(key, cipher, iv, hex) + if hex then + cipher = hexdecode(cipher) + end + return aes_cbc_decrypt(32, key, cipher, iv) +end + +function crypt.aes_256_ecb_decrypt(key, cipher, iv, hex) + if hex then + cipher = hexdecode(cipher) + end + return aes_ecb_decrypt(32, key, cipher, iv) +end + +function crypt.aes_256_cfb_decrypt(key, cipher, iv, hex) + if hex then + cipher = hexdecode(cipher) + end + return aes_cfb_decrypt(32, key, cipher, iv) +end + +function crypt.aes_256_ofb_decrypt(key, cipher, iv, hex) + if hex then + cipher = hexdecode(cipher) + end + return aes_ofb_decrypt(32, key, cipher, iv) +end + +function crypt.aes_256_ctr_decrypt(key, cipher, iv, hex) + if hex then + cipher = hexdecode(cipher) + end + return aes_ctr_decrypt(32, key, cipher, iv) +end + +function crypt.aes_256_gcm_decrypt(key, cipher, iv, hex) + if hex then + cipher = hexdecode(cipher) + end + return aes_gcm_decrypt(32, key, cipher, iv) end function crypt.base64urlencode(data) @@ -393,6 +614,149 @@ function crypt.desdecode (key, text, hex) return desdecode(key, text) end +function crypt.desx_encrypt(key, text, iv, b64) + local hash = des_encrypt(0, key, text, iv) + if b64 then + hash = base64encode(hash) + end + return hash +end + +function crypt.desx_decrypt(key, cipher, iv, b64) + if b64 then + cipher = base64decode(cipher) + end + return des_decrypt(0, key, cipher, iv) +end + +function crypt.desx_cbc_encrypt(key, text, iv, b64) + return crypt.desx_encrypt(key, text, iv, b64) +end + +function crypt.desx_cbc_decrypt(key, cipher, iv, b64) + return crypt.desx_decrypt(key, text, iv, b64) +end + +function crypt.des_cbc_encrypt(key, text, iv, b64) + local hash = des_encrypt(1, key, text, iv) + if b64 then + hash = base64encode(hash) + end + return hash +end + +function crypt.des_cbc_decrypt(key, cipher, iv, b64) + if b64 then + cipher = base64encode(cipher) + end + return des_decrypt(1, key, cipher, iv) +end + +function crypt.des_ecb_encrypt(key, text, iv, b64) + local hash = des_encrypt(2, key, text, iv) + if b64 then + hash = base64encode(hash) + end + return hash +end + +function crypt.des_ecb_decrypt(key, cipher, iv, b64) + if b64 then + cipher = base64encode(cipher) + end + return des_decrypt(2, key, cipher, iv) +end + +function crypt.des_cfb_encrypt(key, text, iv, b64) + local hash = des_encrypt(3, key, text, iv) + if b64 then + hash = base64encode(hash) + end + return hash +end + +function crypt.des_cfb_decrypt(key, cipher, iv, b64) + if b64 then + cipher = base64encode(cipher) + end + return des_decrypt(3, key, cipher, iv) +end + +function crypt.des_ofb_encrypt(key, text, iv, b64) + local hash = des_encrypt(4, key, text, iv) + if b64 then + hash = base64encode(hash) + end + return hash +end + +function crypt.des_ofb_decrypt(key, cipher, iv, b64) + if b64 then + cipher = base64encode(cipher) + end + return des_decrypt(4, key, cipher, iv) +end + +function crypt.des_ede_encrypt(key, text, iv, b64) + local hash = des_encrypt(5, key, text, iv) + if b64 then + hash = base64encode(hash) + end + return hash +end + +function crypt.des_ede_decrypt(key, cipher, iv, b64) + if b64 then + cipher = base64encode(cipher) + end + return des_decrypt(5, key, cipher, iv) +end + +function crypt.des_ede3_encrypt(key, text, iv, b64) + local hash = des_encrypt(6, key, text, iv) + if b64 then + hash = base64encode(hash) + end + return hash +end + +function crypt.des_ede3_decrypt(key, cipher, iv, b64) + if b64 then + cipher = base64encode(cipher) + end + return des_decrypt(6, key, cipher, iv) +end + +function crypt.des_ede_ecb_encrypt(key, text, iv, b64) + local hash = des_encrypt(7, key, text, iv) + if b64 then + hash = base64encode(hash) + end + return hash +end + +function crypt.des_ede_ecb_decrypt(key, cipher, iv, b64) + if b64 then + cipher = base64encode(cipher) + end + return des_decrypt(7, key, cipher, iv) +end + +function crypt.des_ede3_ecb_encrypt(key, text, iv, b64) + local hash = des_encrypt(8, key, text, iv) + if b64 then + hash = base64encode(hash) + end + return hash +end + +function crypt.des_ede3_ecb_decrypt(key, cipher, iv, b64) + if b64 then + cipher = base64encode(cipher) + end + return des_decrypt(8, key, cipher, iv) +end + function crypt.dhsecret (...) return dhsecret(...) end From 6262f00feb8aa320681107d23b63c5e6dac57910 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 6 Dec 2020 15:13:01 +0800 Subject: [PATCH 658/956] =?UTF-8?q?=E4=B8=BAsys=E5=BA=93=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?interfact=E6=96=B9=E6=B3=95=E8=8E=B7=E5=8F=96=E7=BD=91=E7=BB=9C?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lsys.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/luaclib/src/lsys.c b/luaclib/src/lsys.c index 0b894e26..32eea5b9 100644 --- a/luaclib/src/lsys.c +++ b/luaclib/src/lsys.c @@ -1,6 +1,7 @@ #define LUA_LIB #include +#include #define MAX_IPV4 (4294967295L) @@ -103,6 +104,49 @@ static int lnew_tab(lua_State *L){ return 1; } +static int linterface(lua_State *L){ + struct ifaddrs *ifc, *ifc1; + if(getifaddrs(&ifc)) + return 0; + lua_createtable(L, 32, 0); + ifc1 = ifc; + int index = 1; + for(; NULL != ifc; ifc = (*ifc).ifa_next) { + if ((*ifc).ifa_addr && (*ifc).ifa_netmask && (*ifc).ifa_name) { + char ip[64] = {0}; + char mask[64] = {0}; + // IPv4 + if ((*ifc).ifa_addr->sa_family == AF_INET && (*ifc).ifa_netmask->sa_family == AF_INET) { + inet_ntop(AF_INET, &(((struct sockaddr_in*)((*ifc).ifa_addr))->sin_addr), ip, 64); + inet_ntop(AF_INET, &(((struct sockaddr_in*)((*ifc).ifa_netmask))->sin_addr), mask, 64); + if (0 != strncmp("0.0.0.0", ip, strlen(ip)) && 0 != strncmp("0.0.0.0", mask, strlen(mask))){ + lua_createtable(L, 0, 4); + lua_pushliteral(L, "Interface"); lua_pushstring(L, (*ifc).ifa_name); lua_rawset(L, -3); + lua_pushliteral(L, "IP"); lua_pushstring(L, ip); lua_rawset(L, -3); + lua_pushliteral(L, "Mask"); lua_pushstring(L, mask); lua_rawset(L, -3); + lua_pushliteral(L, "Version"); lua_pushliteral(L, "IPv4"); lua_rawset(L, -3); + lua_rawseti(L, -2, index++); + } + } + // IPv6 + if ((*ifc).ifa_addr->sa_family == AF_INET6 && (*ifc).ifa_netmask->sa_family == AF_INET6) { + inet_ntop(AF_INET6, &(((struct sockaddr_in6*)((*ifc).ifa_addr))->sin6_addr), ip, 64); + inet_ntop(AF_INET6, &(((struct sockaddr_in6*)((*ifc).ifa_netmask))->sin6_addr), mask, 64); + if (0 != strncmp("::", ip, strlen(ip)) && 0 != strncmp(ip, "fe80", 4)) { + lua_createtable(L, 0, 4); + lua_pushliteral(L, "Interface"); lua_pushstring(L, (*ifc).ifa_name); lua_rawset(L, -3); + lua_pushliteral(L, "IP"); lua_pushstring(L, ip); lua_rawset(L, -3); + lua_pushliteral(L, "Mask"); lua_pushstring(L, mask); lua_rawset(L, -3); + lua_pushliteral(L, "Version"); lua_pushliteral(L, "IPv6"); lua_rawset(L, -3); + lua_rawseti(L, -2, index++); + } + } + } + } + freeifaddrs(ifc1); + return 1; +} + LUAMOD_API int luaopen_sys(lua_State *L){ luaL_checkversion(L); @@ -115,6 +159,7 @@ luaopen_sys(lua_State *L){ {"str2ip", lstr2ip}, {"ip2str", lip2str}, {"hostname", lhostname}, + {"interface", linterface}, {"new_tab", lnew_tab}, {NULL, NULL} }; From 3c712f9d56569e9953bd2c5d29dab1e040963e38 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 7 Dec 2020 21:34:41 +0800 Subject: [PATCH 659/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0csv=E5=BA=93=E7=9A=84?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/csv.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lualib/csv.lua b/lualib/csv.lua index 3a9441b8..c53f5a69 100644 --- a/lualib/csv.lua +++ b/lualib/csv.lua @@ -60,4 +60,20 @@ function csv.writefile (path, t) return true end +function csv.loadstring (str) + if type(str) ~= 'string' or str == '' then + return nil, "invalid args." + end + local tab = {} + local index = 1 + for line in str:gmatch("([^\r\n]-)\r\n") do + local items = {} + for s in line:gmatch("([^, \r\n]+)") do + items[#items+1] = s + end + tab[#tab+1] = items + end + return tab +end + return csv From 6d87d18baa30b355193315ced8a26e7173f74538 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 7 Dec 2020 21:35:05 +0800 Subject: [PATCH 660/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BB=A3=E7=90=86?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=99=A8=E5=8F=AF=E8=83=BD=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E7=9A=84header=E5=88=A4=E6=96=AD=E5=A4=B1=E8=B4=A5=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index dde8bb87..7e98c851 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -175,9 +175,9 @@ local function PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEA local XML_ENCODE_2 = 'application/xml' local JSON_ENCODE = 'application/json' local URL_ENCODE = 'application/x-www-form-urlencoded' - local format = match(HEADER['Content-type'] or HEADER['Content-Type'] or HEADER['content-type'] or '', '(.-/[^;]*)') + local format = match(HEADER['Content-Type'] or HEADER['content-type'] or '', '(.-/[^;]*)') if format == FILE_ENCODE then - local BOUNDARY = match(HEADER['Content-Type'], '^.+=[%-]*(.+)') + local BOUNDARY = match(HEADER['Content-Type'] or HEADER['content-type'] or '', '^.+=[%-]*(.+)') if BOUNDARY and BOUNDARY ~= '' then local typ, body = form_multipart(BODY, BOUNDARY) if typ == FILE_TYPE then From 2b8fc9616fe15c973e8b24d856842439d4d709bc Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 12 Dec 2020 10:17:36 +0800 Subject: [PATCH 661/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=97=A5=E6=9C=9F?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E6=A0=BC=E5=BC=8F=E5=8C=96=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/datetime/init.lua | 133 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 lualib/datetime/init.lua diff --git a/lualib/datetime/init.lua b/lualib/datetime/init.lua new file mode 100644 index 00000000..523a9e45 --- /dev/null +++ b/lualib/datetime/init.lua @@ -0,0 +1,133 @@ +local type = type +local assert = assert + +local sys = require "sys" +local os_now = sys.now +local os_time = os.time +local os_date = os.date +local difftime = os.difftime + +local match = string.match +local fmt = string.format + +local abs = math.abs +local toint = math.tointeger + +local datetime = {} + +--[[ +时间日期格式化符号: +%y 两位数的年份表示(00-99) +%Y 四位数的年份表示(000-9999) +%m 月份(01-12) +%d 月内中的一天(0-31) +%H 24小时制小时数(0-23) +%I 12小时制小时数(01-12) +%M 分钟数(00=59) +%S 秒(00-59) + +%a 本地简化星期名称 +%A 本地完整星期名称 +%b 本地简化的月份名称 +%B 本地完整的月份名称 +%c 本地相应的日期表示和时间表示 +%j 年内的一天(001-366) +%p 本地A.M.或P.M.的等价符 +%U 一年中的星期数(00-53)星期天为星期的开始 +%w 星期(0-6),星期天为星期的开始 +%W 一年中的星期数(00-53)星期一为星期的开始 +%x 本地相应的日期表示 +%X 本地相应的时间表示 +%Z 当前时区的名称 +%% %号本身 + +例如:  "Tue May 31 17:46:55 +0800 2011" 对应 "%a %b %d %H:%M:%S %Z %Y" +0800为中国的时区代码(东八区) +]] + +-- DATETIME 时间格式 +function datetime.datetime(timestamp) + return os_date("%F %X", timestamp or os_time()) +end + +-- ISO 8601表示法 - 1 +function datetime.iso8601( timestamp ) + return os_date("%FT%X", timestamp or os_time()) .. datetime.timezone2() +end + +-- ISO 8601表示法 - 2 +function datetime.iso8601_2( timestamp ) + return os_date("%Y%m%dT%H%M%S", timestamp or os_time()) .. datetime.timezone2() +end + +-- 格林威治时间 +function datetime.greenwich( timestamp ) + return os_date("%a, %d %b %Y %X GMT", timestamp or os_time()) +end + +-- 本地时间表示法 +function datetime.localtime( timestamp ) + return os_date("%c", timestamp or os_time()) +end + +-- 当前所在时区 +function datetime.timezone() + local time = difftime(os_time(), os_time(os_date("!*t"))) + return toint(time // 3600) +end + +-- 当前所在时区 +function datetime.timezone2() + local time = difftime(os_time(), os_time(os_date("!*t"))) + if abs(time) ~= time then + return fmt("-%02d:00", abs(time // 3600)) + end + return fmt("+%02d:00", toint(time // 3600)) +end + +-- 凌晨 - 秒级时间戳 +function datetime.dawn(timestamp) + local time = os_date("*t", timestamp) + return os_time { year = time.year, month = time.month, day = time.day, hour = 0, min = 0, sec = 0 } +end + +-- 午夜 - 秒级时间戳 +function datetime.midnight(timestamp) + local time = os_date("*t", timestamp) + return os_time { year = time.year, month = time.month, day = time.day, hour = 23, min = 59, sec = 59 } +end + +-- 毫秒级时间戳 +function datetime.mtime() + return toint((os_now() * 1e3) // 1) +end + +-- 秒级时间戳 +function datetime.time() + return os_time() +end + +-- DATETIME 转换为秒级时间戳 +function datetime.from_timestamp(dt) + assert(type(dt) == 'string' and dt ~= '', "Invalide datetime format.") + local year, month, day, hour, min, sec = match(dt, '([%d]-)[%-/]?([%d]-)[%-/]?([%d]-)[T ]+([%d]+):([%d]+):([%d]+)') + return os_time { year = year, month = month, day = day, hour = hour, min = min, sec = sec } +end + +-- DATETIME 转换为毫秒级时间戳 +function datetime.from_timestamp2(dt) + assert(type(dt) == 'string' and dt ~= '', "Invalide datetime format.") + local year, month, day, hour, min, sec, ms = match(dt, '([%d]-)[%-/]?([%d]-)[%-/]?([%d]-)[T ]+([%d]+):([%d]+):([%d]+)[%., ]+([%d]+)') + local ms = ms:sub(1, 3) + if #ms < 3 then + local ms = toint(ms) + if ms < 100 then + if ms < 10 then + ms = ms * 10 + end + ms = ms * 10 + end + end + return toint(os_time { year = year, month = month, day = day, hour = hour, min = min, sec = sec } * 1e3 + ms) +end + +return datetime \ No newline at end of file From 8e7cc9b088689af6522fbdc34ff6e0915161b142 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 12 Dec 2020 10:18:14 +0800 Subject: [PATCH 662/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0LUA=E7=9A=84PATH?= =?UTF-8?q?=E4=B8=8ECPATH=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/core.c b/src/core.c index e72a3e6c..f0bf175e 100644 --- a/src/core.c +++ b/src/core.c @@ -1,20 +1,22 @@ #include "core.h" -#ifdef __MSYS__ - #ifndef LUALIBS_PATH - #define LUALIBS_PATH "lualib/?.lua;lualib/?/init.lua;./?.lua;./?/init.lua;script/?.lua;script/?/init.lua;" - #endif - #ifndef LUACLIBS_PATH - #define LUACLIBS_PATH "luaclib/?.so;luaclib/lib?.so;./?.so;luaclib/msys-?.dll;luaclib/?.dll;./msys-?.dll;./?.dll;" - #endif -#else - #ifndef LUALIBS_PATH - #define LUALIBS_PATH "lualib/?.lua;lualib/?/init.lua;./?.lua;./?/init.lua;script/?.lua;script/?/init.lua;" - #endif - #ifndef LUACLIBS_PATH - #define LUACLIBS_PATH "luaclib/?.so;./?.so;luaclib/lib?.so;./lib?.so;luaclib/?.dylib;./?.dylib;luaclib/lib?.dylib;./lib?.dylib;" - #endif -#endif +#define LUALIBS_PATH \ + "lualib/?.lua;;lualib/?/init.lua;;" \ + "3rd/?.lua;;3rd/?/init.lua;;" \ + "script/?.lua;;script/?/init.lua;;" + +#define LUACLIBS_PATH \ + "luaclib/?.so;;luaclib/lib?.so;;" \ + "luaclib/?.dylib;;luaclib/lib?.dylib;;" \ + "luaclib/?.dll;;luaclib/msys-?.dll;;" \ + \ + "./?.so;;./lib?.so;;" \ + "./?.dylib;;./lib?.dylib;;" \ + "./?.dll;;./msys-?.dll;;" \ + \ + "3rd/?.so;;3rd/lib?.so;;" \ + "3rd/?.dylib;3rd/lib?.dylib;;" \ + "3rd/?.dll;;3rd/msys-?.dll;;" /* const char *signame[]= { From f50988b1ad3bf7e7367a8ab6e35036585bf077d2 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 12 Dec 2020 10:50:18 +0800 Subject: [PATCH 663/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=91=BD=E4=BB=A4?= =?UTF-8?q?=E8=A1=8C=E5=8F=82=E6=95=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_start.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core_start.c b/src/core_start.c index e1776b02..9eee35d2 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -77,8 +77,8 @@ void specify_process_daemon() { void check_args(int argc, char const *argv[]) { int opt = -1; - opterr = 0; - while ((opt = getopt(argc, argv, "hde:p:")) != -1) { + int opterr = 0; + while ((opt = getopt(argc, (char *const *)argv, "hde:p:")) != -1) { switch(opt) { case 'e': specify_entry_file(optarg); From 192ef6682695eda6d3463111b8562a5f5ce7e013 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 16 Dec 2020 20:39:21 +0800 Subject: [PATCH 664/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0cfadmin=E7=9A=84?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E8=A1=8C=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_start.c | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/src/core_start.c b/src/core_start.c index 9eee35d2..8960544c 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -34,13 +34,15 @@ void usage_print() { printf( "cfadmin Usage: ./cfadmin [options]\n" \ "\n" \ - " -h \"Print cfadmin usage.\"\n" \ + " -h \"Print `cfadmin` usage.\"\n" \ "\n" \ - " -d \"Make cfadmin run in daemon mode.\"\n" \ + " -d \"Make `cfadmin` run in daemon mode.\"\n" \ "\n" \ - " -e \"Specify lua entry file name.\"\n" \ + " -e \"Specify `lua` entry file name.\"\n" \ "\n" \ - " -p \"Specify the process Pid write file name.\"\n" \ + " -p \"Specify the process `Pid` write file name.\"\n" \ + "\n" \ + " -k \"Send `SIGKILL` signal to `Pid` or `Pid File`.\"\n" \ "\n" \ ); } @@ -70,6 +72,28 @@ void specify_pid_file(const char *filename) { memmove(pid_filename, filename, strlen(filename)); } +/* 给指定`PID`或包含`PID`的文件发送`SIGKILL`信号 */ +void specify_kill_process(const char *spid) { + int pid = atoi(spid); + if (pid <= 1) { + FILE *fp = fopen(spid, "rb"); + if (!fp) { + LOG("ERROR", "Invalid Pid or pid file name."); + return; + } + char pbuf[20]; + memset(pbuf, 0x0, 20); + fread(pbuf, 1, 20, fp); + fclose(fp); + pid = atoi(pbuf); + if (pid <= 1){ + LOG("ERROR", "Invalid Pid or File name."); + return; + } + } + kill(pid, SIGKILL); +} + /* 后台运行 */ void specify_process_daemon() { daemon(1, 0); @@ -78,7 +102,7 @@ void specify_process_daemon() { void check_args(int argc, char const *argv[]) { int opt = -1; int opterr = 0; - while ((opt = getopt(argc, (char *const *)argv, "hde:p:")) != -1) { + while ((opt = getopt(argc, (char *const *)argv, "hde:p:k:")) != -1) { switch(opt) { case 'e': specify_entry_file(optarg); @@ -86,6 +110,9 @@ void check_args(int argc, char const *argv[]) { case 'p': specify_pid_file(optarg); continue; + case 'k': + specify_kill_process(optarg); + return _exit(0); case 'd': specify_process_daemon(); continue; From 79549581b106f5f39aad46b75c307c50808e9fed Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 16 Dec 2020 20:39:57 +0800 Subject: [PATCH 665/956] =?UTF-8?q?=E4=BC=98=E5=8C=96json=E5=BA=93?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/json/init.lua | 16 ++++++---------- lualib/json/jsonp.lua | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 10 deletions(-) create mode 100644 lualib/json/jsonp.lua diff --git a/lualib/json/init.lua b/lualib/json/init.lua index c640e8b5..96d50946 100644 --- a/lualib/json/init.lua +++ b/lualib/json/init.lua @@ -3,8 +3,6 @@ local cjson = require "cjson" local pcall = pcall local setmetatable = setmetatable -local cjson_encode = cjson.encode -local cjson_decode = cjson.decode local cjson_array_mt = cjson.array_mt -- this lib fork from resty cjson, only modified some compatible codes @@ -28,6 +26,10 @@ local CJSON = { encode_empty_table_as_object = cjson.encode_empty_table_as_object, } +local cjson = require "cjson.safe" +local cjson_encode = cjson.encode +local cjson_decode = cjson.decode + -- 设置稀疏数组用null填充 function CJSON.sparse_array_to_null(array) return setmetatable(array, cjson.array_mt) @@ -35,18 +37,12 @@ end -- json序列化 function CJSON.encode (...) - local ok, data = pcall(cjson_encode, ...) - if ok then - return data - end + return cjson_encode(...) end -- json反序列化 function CJSON.decode (...) - local ok, data = pcall(cjson_decode, ...) - if ok then - return data - end + return cjson_decode(...) end return CJSON diff --git a/lualib/json/jsonp.lua b/lualib/json/jsonp.lua new file mode 100644 index 00000000..2131dff7 --- /dev/null +++ b/lualib/json/jsonp.lua @@ -0,0 +1,23 @@ +local json = require "json" +local json_encode = json.encode +local json_decode = json.decode + +local sub = string.sub +local find = string.find +local concat = table.concat + +local jsonp = {} + +function jsonp.encode(callback_name, tab) + return concat {callback_name, "(", json_encode(tab), ")"} +end + +function jsonp.decode(str) + local s, e = find(str, "%("), find(str, "%)", -1) + if not s or not e or e <= s then + return + end + return json_decode(sub(str, s + 1, e - 1)) +end + +return jsonp \ No newline at end of file From bfdded54c10ee30d90f3f4142e2877ddd67f3896 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 16 Dec 2020 20:41:33 +0800 Subject: [PATCH 666/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0fd=E7=9A=84ipaddr?= =?UTF-8?q?=E4=B8=8Eport=E8=BF=BD=E8=B8=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 1 + lualib/httpd/init.lua | 12 ++++++------ lualib/internal/TCP.lua | 16 ++++++++-------- lualib/protocol/http/init.lua | 12 +++++++----- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index e5b02388..33593f29 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -326,6 +326,7 @@ static void IO_ACCEPT(CORE_P_ core_io *io, int revents){ inet_ntop(AF_INET6, &SA.sin6_addr, buf, INET6_ADDRSTRLEN); lua_pushinteger(co, client); lua_pushlstring(co, buf, strlen(buf)); + lua_pushinteger(co, SA.sin6_port); status = CO_RESUME(co, NULL, status == LUA_YIELD ? lua_gettop(co) : lua_gettop(co) - 1); if (status != LUA_YIELD && status != LUA_OK) { LOG("ERROR", lua_tostring(co, -1)); diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua index ac4f4a5f..a76bd6b2 100644 --- a/lualib/httpd/init.lua +++ b/lualib/httpd/init.lua @@ -97,7 +97,7 @@ end -- 在路由函数被执行之后执行此方法 function httpd:before(func) if type(func) == 'function' then - self._before_func = func + self.__before_func = func end end @@ -197,8 +197,8 @@ function httpd:listen(ip, port, backlog) self.ip = ip self.port = port self.sock:set_backlog(toint(backlog)) - return assert(self.sock:listen(ip or "0.0.0.0", toint(port), function (fd, ipaddr) - return RAW_DISPATCH(fd, match(ipaddr, '^::[f]+:(.+)') or ipaddr, self) + return assert(self.sock:listen(ip or "0.0.0.0", toint(port), function (fd, ipaddr, port) + return RAW_DISPATCH(fd, { ipaddr = match(ipaddr, '^::[f]+:(.+)') or ipaddr, port = port }, self) end)) end @@ -208,8 +208,8 @@ function httpd:listen_ssl(ip, port, backlog, key, cert, pw) self.ssl_key, self.ssl_cert, self.ssl_pw = key, cert, pw self.sock:set_backlog(toint(backlog)) return assert(self.sock:listen_ssl(ip or "0.0.0.0", self.ssl_port, { cert = self.ssl_cert, key = self.ssl_key, pw = self.ssl_pw }, - function (sock, ipaddr) - return RAW_DISPATCH(sock, match(ipaddr, '^::[f]+:(.+)') or ipaddr, self) + function (sock, ipaddr, port) + return RAW_DISPATCH(sock, { ipaddr = match(ipaddr, '^::[f]+:(.+)') or ipaddr, port = port }, self) end) ) end @@ -220,7 +220,7 @@ function httpd:listenx(unix_domain_path, backlog) self.unix_domain_path = unix_domain_path self.sock:set_backlog(toint(backlog)) return assert(self.sock:listen_ex(unix_domain_path, true, function (fd, ipaddr) - return RAW_DISPATCH(fd, match(ipaddr, '^::[f]+:(.+)') or ipaddr, self) + return RAW_DISPATCH(fd, { ipaddr = match(ipaddr, '^::[f]+:(.+)') or ipaddr }, self) end)) end diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index 975e6d48..45844189 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -18,7 +18,7 @@ local dns_resolve = dns.resolve local co = require "internal.Co" local co_new = co.new local co_wakeup = co.wakeup -local co_spwan = co.spwan +local co_spawn = co.spawn local co_self = co.self local co_wait = coroutine.yield @@ -514,11 +514,11 @@ function TCP:listen(ip, port, cb) if type(cb) ~= 'function' then return nil, "Listen function was invalid." end - self.listen_co = co_new(function (fd, ipaddr) + self.listen_co = co_new(function (fd, ipaddr, port) while 1 do if fd and ipaddr then - co_spwan(cb, fd, ipaddr) - fd, ipaddr = co_wait() + co_spawn(cb, fd, ipaddr, port) + fd, ipaddr, port = co_wait() end end end) @@ -565,11 +565,11 @@ function TCP:listen_ssl(ip, port, opt, cb) if type(cb) ~= 'function' then return nil, "Listen function was invalid." end - self.listen_ssl_co = co_new(function (fd, ipaddr) + self.listen_ssl_co = co_new(function (fd, ipaddr, port) while 1 do if fd and ipaddr then - co_spwan(ssl_accept, cb, opt, fd, ipaddr) - fd, ipaddr = co_wait() + co_spawn(cb, fd, ipaddr, port) + fd, ipaddr, port = co_wait() end end end) @@ -589,7 +589,7 @@ function TCP:listen_ex(unix_domain_path, removed, cb) self.listen_ex_co = co_new(function (fd) while 1 do if fd then - co_spwan(cb, fd, "127.0.0.1") + co_spawn(cb, fd, "127.0.0.1") fd = co_wait() end end diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index 7e98c851..e545acca 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -302,7 +302,7 @@ local function send_body (sock, body, filepath) return sock:send(body) end -function HTTP_PROTOCOL.DISPATCH(sock, ipaddr, http) +function HTTP_PROTOCOL.DISPATCH(sock, opt, http) local buffers = {} local ttl = http.ttl local server = http.__server @@ -317,6 +317,8 @@ function HTTP_PROTOCOL.DISPATCH(sock, ipaddr, http) local http_router = http.router local route_find = http_router.find local tolog = http.tolog + local ipaddr = opt.ipaddr + local port = opt.port secCookie(cookie_secure) -- 如果需要 while 1 do local buf = sock:recv(8192) @@ -389,7 +391,7 @@ function HTTP_PROTOCOL.DISPATCH(sock, ipaddr, http) goto CONTINUE end content['ROUTE'] = HTTP_PROTOCOL[typ] - content['method'], content['path'], content['headers'], content['client_ip'] = METHOD, PATH, HEADER, ipaddr + content['method'], content['path'], content['headers'], content['client_ip'], content['client_port'] = METHOD, PATH, HEADER, ipaddr, port -- before 函数只影响接口与view if before_func and (typ == HTTP_PROTOCOL.API or typ == HTTP_PROTOCOL.USE) then local ok, code, data = safe_call(before_func, tab_copy(content)) @@ -587,11 +589,11 @@ function HTTP_PROTOCOL.DISPATCH(sock, ipaddr, http) end -function HTTP_PROTOCOL.RAW_DISPATCH(s, ipaddr, http) +function HTTP_PROTOCOL.RAW_DISPATCH(s, opt, http) if type(s) == 'table' then - return HTTP_PROTOCOL.DISPATCH(s, ipaddr, http) + return HTTP_PROTOCOL.DISPATCH(s, opt, http) end - return HTTP_PROTOCOL.DISPATCH(tcp:new():set_fd(s):timeout(http.__timeout), ipaddr, http) + return HTTP_PROTOCOL.DISPATCH(tcp:new():set_fd(s):timeout(http.__timeout), opt, http) end return HTTP_PROTOCOL From cf29d05e20794b1f1a96fc03a9532365b844117c Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 16 Dec 2020 20:41:56 +0800 Subject: [PATCH 667/956] =?UTF-8?q?=E4=B8=80=E4=BA=9B=E5=91=BD=E5=90=8D?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/DB/mssql.lua | 4 ++-- lualib/DB/mysql.lua | 4 ++-- lualib/DB/pgsql.lua | 4 ++-- lualib/cf/init.lua | 2 +- lualib/internal/Co.lua | 4 ++-- lualib/internal/Timer.lua | 6 +++--- lualib/logging/init.lua | 12 +++++++++--- lualib/protocol/mqtt/init.lua | 4 ++-- lualib/protocol/redis.lua | 4 ++-- lualib/utils/init.lua | 2 +- 10 files changed, 26 insertions(+), 20 deletions(-) diff --git a/lualib/DB/mssql.lua b/lualib/DB/mssql.lua index 0c6aafb2..db1491e7 100644 --- a/lualib/DB/mssql.lua +++ b/lualib/DB/mssql.lua @@ -12,7 +12,7 @@ local hashkey = crypt.hashkey local co = require "internal.Co" local co_self = co.self local co_wait = co.wait -local co_spwan = co.spwan +local co_spawn = co.spawn local co_wakeup = co.wakeup local type = type @@ -184,7 +184,7 @@ function DB:transaction(f) db:close() local co = pop_wait(self) if co then - co_spwan(function ( ... ) + co_spawn(function ( ... ) co_wakeup(co, pop_db(self)) end) end diff --git a/lualib/DB/mysql.lua b/lualib/DB/mysql.lua index 98d4ec11..8f821960 100644 --- a/lualib/DB/mysql.lua +++ b/lualib/DB/mysql.lua @@ -12,7 +12,7 @@ local hashkey = crypt.hashkey local co = require "internal.Co" local co_self = co.self local co_wait = co.wait -local co_spwan = co.spwan +local co_spawn = co.spawn local co_wakeup = co.wakeup local type = type @@ -184,7 +184,7 @@ function DB:transaction(f) db:close() local co = pop_wait(self) if co then - co_spwan(function ( ... ) + co_spawn(function ( ... ) co_wakeup(co, pop_db(self)) end) end diff --git a/lualib/DB/pgsql.lua b/lualib/DB/pgsql.lua index d53469c3..35dfe33b 100644 --- a/lualib/DB/pgsql.lua +++ b/lualib/DB/pgsql.lua @@ -12,7 +12,7 @@ local hashkey = crypt.hashkey local co = require "internal.Co" local co_self = co.self local co_wait = co.wait -local co_spwan = co.spwan +local co_spawn = co.spawn local co_wakeup = co.wakeup local type = type @@ -183,7 +183,7 @@ function DB:transaction(f) db:close() local co = pop_wait(self) if co then - co_spwan(function ( ... ) + co_spawn(function ( ... ) co_wakeup(co, pop_db(self)) end) end diff --git a/lualib/cf/init.lua b/lualib/cf/init.lua index c05c6dc8..dd6c1ebc 100644 --- a/lualib/cf/init.lua +++ b/lualib/cf/init.lua @@ -1,6 +1,6 @@ local Co = require "internal.Co" local self = Co.self -local fork = Co.spwan +local fork = Co.spawn local wait = Co.wait local wakeup = Co.wakeup diff --git a/lualib/internal/Co.lua b/lualib/internal/Co.lua index fec55507..d282c916 100644 --- a/lualib/internal/Co.lua +++ b/lualib/internal/Co.lua @@ -89,13 +89,13 @@ function Co.wait() end -- 启动 -function Co.spwan(func, ...) +function Co.spawn(func, ...) if type(func) == "function" then local co = co_pop(f) cos[co] = task_pop() return task_start(cos[co], co, func, ...) end - error("Co Just Can Spwan a Coroutine to run in sometimes.") + error("Co Just Can spawn a Coroutine to run in sometimes.") end -- 唤醒 diff --git a/lualib/internal/Timer.lua b/lualib/internal/Timer.lua index fdc4c579..c98aad2c 100644 --- a/lualib/internal/Timer.lua +++ b/lualib/internal/Timer.lua @@ -10,7 +10,7 @@ local ti_stop = ti.stop local co_new = co.new local co_wait = co.wait -local co_spwan = co.spwan +local co_spawn = co.spawn local co_wakeup = co.wakeup local co_self = co.self @@ -74,7 +74,7 @@ function Timer.timeout(timeout, cb) if once.stoped then return end - co_spwan(cb) + co_spawn(cb) Timer_stop(once) end) Timer_start(once) @@ -99,7 +99,7 @@ function Timer.at(repeats, cb) if at.stoped then return end - co_spwan(cb) + co_spawn(cb) co_wait_ex() end end) diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua index 25a055ee..d815f0de 100644 --- a/lualib/logging/init.lua +++ b/lualib/logging/init.lua @@ -68,15 +68,21 @@ local function table_format(t) for key, value in pairs(t) do local k, v if type(key) == 'number' then - k = concat({'[', key, ']'}) + k = concat({'[', key, ']'}) + elseif type(key) == 'string' then + k = concat({'["', key, '"]'}) else - k = concat({'["', key, '"]'}) + k = concat({'[', tostring(key), ']'}) end if type(value) == 'table' then if t ~= value then v = table_format(value) else - v = tostring(value) + if type(value) == 'table' then + v = table_format(value) + else + v = tostring(value) + end end elseif type(value) == 'string' then v = concat({'"', value, '"'}) diff --git a/lualib/protocol/mqtt/init.lua b/lualib/protocol/mqtt/init.lua index 93bb8280..4e3259a8 100644 --- a/lualib/protocol/mqtt/init.lua +++ b/lualib/protocol/mqtt/init.lua @@ -36,7 +36,7 @@ local str_match = string.match local str_format = string.format local str_gsub = string.gsub local tbl_remove = table.remove -local co_spwan = Co.spwan +local co_spawn = Co.spawn -- Empty function to do nothing on MQTT client events local empty_func = function() end @@ -66,7 +66,7 @@ function client:subscribe(opt, func) if not ok then return false, 'SUBSCRIBE wait for SUBACK failed' end - co_spwan(function ( ... ) + co_spawn(function ( ... ) local time = os_time() local timer = Timer.at(self.keep_alive, function ( ... ) if os_time() >= time + self.keep_alive then diff --git a/lualib/protocol/redis.lua b/lualib/protocol/redis.lua index 260823a0..27067cfb 100644 --- a/lualib/protocol/redis.lua +++ b/lualib/protocol/redis.lua @@ -7,7 +7,7 @@ local new_tab = require "sys".new_tab local Log = log:new({ dump = true, path = 'protocol-redis' }) -local co_spwan = Co.spwan +local co_spawn = Co.spawn local type = type local pcall = pcall @@ -173,7 +173,7 @@ function redis:psubscribe(pattern, func) if not ok or not msg[2] then return nil, "PSUBSCRIBE error: 订阅"..tostring(pattern).."失败." end - co_spwan(function ( ... ) + co_spawn(function ( ... ) while 1 do local ok, msg = read_response(sock) if not ok or not msg or not self.sock then diff --git a/lualib/utils/init.lua b/lualib/utils/init.lua index 81b7a99d..f105ab8f 100644 --- a/lualib/utils/init.lua +++ b/lualib/utils/init.lua @@ -47,7 +47,7 @@ end -- local co = require "internal.Co" -- local tcp = require "internal.TCP" -- local Timer = require "internal.Timer" --- co.spwan(function ( ... ) +-- co.spawn(function ( ... ) -- while 1 do -- local self = co.self() -- local ti = Timer.timeout(1, function() From a67d57d1dd5a6654da65ab57fdf4984d69ce92e6 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 20 Dec 2020 01:29:01 +0800 Subject: [PATCH 668/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E7=B3=BB?= =?UTF-8?q?=E5=88=97=E5=BC=95=E7=94=A8=E9=97=AE=E9=A2=98=E4=B8=8Ebug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/Cache/init.lua | 20 ++++++++++---------- lualib/admin/db/menu.lua | 6 +++--- lualib/admin/db/permission.lua | 4 ++-- lualib/admin/http/system/menu.lua | 1 + lualib/admin/http/system/role.lua | 6 +++--- lualib/cloud/qiniu/stream.lua | 3 ++- lualib/cloud/translate/youdao.lua | 2 +- lualib/crypt/init.lua | 2 +- lualib/httpc/class.lua | 1 + lualib/httpc/init.lua | 2 +- lualib/httpd/Cookie.lua | 2 +- lualib/internal/UDP.lua | 1 - lualib/protobuf/protoc.lua | 2 +- lualib/protocol/http/code.lua | 4 ++-- lualib/protocol/http/init.lua | 4 ++-- lualib/protocol/http/mime.lua | 1 - lualib/protocol/mqtt/bit.lua | 2 +- lualib/protocol/mqtt/init.lua | 2 +- lualib/protocol/mqtt/protocol.lua | 2 +- lualib/protocol/mssql.lua | 7 ++++--- lualib/protocol/mysql.lua | 8 +++++--- lualib/protocol/smtp/init.lua | 18 +++++++++--------- lualib/protocol/stomp/protocol.lua | 4 ++-- lualib/template/new.lua | 1 - 24 files changed, 54 insertions(+), 51 deletions(-) diff --git a/lualib/Cache/init.lua b/lualib/Cache/init.lua index c707f577..422720d5 100644 --- a/lualib/Cache/init.lua +++ b/lualib/Cache/init.lua @@ -67,6 +67,16 @@ local function CREATE_CACHE(opt) return rds end +-- 加入到协程池内 +local function add_wait(self, co) + insert(self.co_pool, co) +end + +-- 弹出一个等待协程 +local function pop_wait(self) + return remove(self.co_pool) +end + -- 加入到连接池内 local function add_cache(self, cache) insert(self.cache_pool, cache) @@ -85,16 +95,6 @@ local function pop_cache(self) return co_wait() end --- 加入到协程池内 -local function add_wait(self, co) - insert(self.co_pool, co) -end - --- 弹出一个等待协程 -local function pop_wait(self) - return remove(self.co_pool) -end - -- 构建Cache对象 local function setmeta(self) keys['count'] = self.count diff --git a/lualib/admin/db/menu.lua b/lualib/admin/db/menu.lua index 3251c3b8..7a22128e 100644 --- a/lualib/admin/db/menu.lua +++ b/lualib/admin/db/menu.lua @@ -29,9 +29,9 @@ end -- 添加菜单菜单 function menu.menu_add (db, opt) local now = os_time() - if opt.id > 0 then -- 是否是增加二级菜单 - db:query(fmt([[UPDATE cfadmin_menus SET URL = 'NULL' WHERE id = '%s' AND active = '1']], id)) - end + -- if opt.id > 0 then -- 二级菜单增加后需要保留一级菜单 + -- db:query(fmt([[UPDATE cfadmin_menus SET URL = 'NULL' WHERE id = '%s' AND active = '1']], opt.id)) + -- end return db:query(fmt([[INSERT INTO cfadmin_menus(`parent`, `name`, `url`, `icon`, `create_at`, `update_at`, `active`) VALUES('%s', '%s', '%s', '%s', '%s', '%s', '1')]], opt.id, opt.name, opt.url, opt.icon, now, now)) end diff --git a/lualib/admin/db/permission.lua b/lualib/admin/db/permission.lua index 6ec00ed5..e58036ed 100644 --- a/lualib/admin/db/permission.lua +++ b/lualib/admin/db/permission.lua @@ -5,12 +5,12 @@ local permission = {} -- 用户是否有此菜单的权限 function permission.user_have_menu_permission (db, uid, url) -- 查询用户Role ID - local uinfo, err = db:query(fmt([[SELECT id, role AS role_id FROM cfadmin_users WHERE `cfadmin_users`.id = %u AND `cfadmin_users`.active = 1 LIMIT 1]], uid))[1] + local uinfo = db:query(fmt([[SELECT id, role AS role_id FROM cfadmin_users WHERE `cfadmin_users`.id = %u AND `cfadmin_users`.active = 1 LIMIT 1]], uid))[1] if type(uinfo) ~= 'table' then return false end -- 查询菜单Menu ID - local minfo, err = db:query(fmt([[SELECT * FROM cfadmin_menus WHERE `cfadmin_menus`.url = '%s' AND `cfadmin_menus`.active = '1' LIMIT 1]], url))[1] + local minfo = db:query(fmt([[SELECT * FROM cfadmin_menus WHERE `cfadmin_menus`.url = '%s' AND `cfadmin_menus`.active = '1' LIMIT 1]], url))[1] if type(minfo) ~= 'table' then return true end diff --git a/lualib/admin/http/system/menu.lua b/lualib/admin/http/system/menu.lua index d1f433fa..3ed8680e 100644 --- a/lualib/admin/http/system/menu.lua +++ b/lualib/admin/http/system/menu.lua @@ -24,6 +24,7 @@ local toint = math.tointeger local get_path = utils.get_path local get_locale = utils.get_locale local access_deny = utils.access_deny +local user_have_permission = utils.user_have_permission local template_path = 'lualib/admin/html/system/menu/' diff --git a/lualib/admin/http/system/role.lua b/lualib/admin/http/system/role.lua index 4107c8df..8a7d9b60 100644 --- a/lualib/admin/http/system/role.lua +++ b/lualib/admin/http/system/role.lua @@ -52,7 +52,7 @@ function system.role_render (content) local db = config.db local ok, user = verify_permission(content, db) if not ok then - return utils.redirect(opt) + return utils.redirect(user) end if not config.cache then template.cache = {} @@ -72,7 +72,7 @@ function system.role_add_render (content) local db = config.db local ok, user = verify_permission(content, db) if not ok then - return utils.redirect(opt) + return utils.redirect(user) end if not config.cache then template.cache = {} @@ -90,7 +90,7 @@ function system.role_edit_render (content) local db = config.db local ok, user = verify_permission(content, db) if not ok then - return utils.redirect(opt) + return utils.redirect(user) end if not config.cache then template.cache = {} diff --git a/lualib/cloud/qiniu/stream.lua b/lualib/cloud/qiniu/stream.lua index 1a5c19eb..258ae17b 100644 --- a/lualib/cloud/qiniu/stream.lua +++ b/lualib/cloud/qiniu/stream.lua @@ -62,7 +62,8 @@ function Stream.queryStreams (AccessKey, SecretKey, hub, opt) if type(hub) ~= 'string' or hub == '' then return nil, "invaild hub." end - local args, querys = {} + local args = {} + local querys if opt.liveonly then args[#args+1] = {"liveonly", "true"} end diff --git a/lualib/cloud/translate/youdao.lua b/lualib/cloud/translate/youdao.lua index 22100616..6d2356b7 100644 --- a/lualib/cloud/translate/youdao.lua +++ b/lualib/cloud/translate/youdao.lua @@ -36,7 +36,7 @@ youdao.SP2ZH_CN = "SP2ZH_CN" -- 转换 function youdao.translate(translate_type, translate_text) translate_type = youdao[translate_type] - if type(translate) ~= 'string' then + if type(translate_type) ~= 'string' then translate_type = "AUTO" end if type(translate_text) ~= 'string' or translate_text == '' then diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index 0232dad3..175e87b3 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -634,7 +634,7 @@ function crypt.desx_cbc_encrypt(key, text, iv, b64) end function crypt.desx_cbc_decrypt(key, cipher, iv, b64) - return crypt.desx_decrypt(key, text, iv, b64) + return crypt.desx_decrypt(key, cipher, iv, b64) end function crypt.des_cbc_encrypt(key, text, iv, b64) diff --git a/lualib/httpc/class.lua b/lualib/httpc/class.lua index 89ca8196..925f0c3d 100644 --- a/lualib/httpc/class.lua +++ b/lualib/httpc/class.lua @@ -27,6 +27,7 @@ local ipairs = ipairs local tostring = tostring local tonumber = tonumber +local upper = string.upper local random = math.random local find = string.find local match = string.match diff --git a/lualib/httpc/init.lua b/lualib/httpc/init.lua index d21963dd..5b1f77cf 100644 --- a/lualib/httpc/init.lua +++ b/lualib/httpc/init.lua @@ -85,7 +85,7 @@ local function raw( parameter ) local REQ = build_raw_req(opt) - local sock = sock_new():timeout(timeout or __TIMEOUT__) + local sock = sock_new():timeout(parameter.timeout or __TIMEOUT__) if parameter.cert_path and parameter.key_path then sock:ssl_set_privatekey(parameter.key_path) sock:ssl_set_certificate(parameter.cert_path) diff --git a/lualib/httpd/Cookie.lua b/lualib/httpd/Cookie.lua index 3fae7333..5cf9ce03 100644 --- a/lualib/httpd/Cookie.lua +++ b/lualib/httpd/Cookie.lua @@ -46,7 +46,7 @@ end -- 设置Cookie function Cookie.setCookie (name, value, expires, notall, https) - assert(type(name) == 'string' and key ~= '', 'invalide Cookie Key') + assert(type(name) == 'string' and name ~= '', 'invalide Cookie Key') assert(type(value) == 'string' or type(value) == 'number', 'invalide Cookie Value') assert(not expires or expires > os_time(), 'invalide Cookie Expires') local co = co_self() diff --git a/lualib/internal/UDP.lua b/lualib/internal/UDP.lua index 915c46a9..b0604245 100644 --- a/lualib/internal/UDP.lua +++ b/lualib/internal/UDP.lua @@ -59,7 +59,6 @@ end function UDP:send(data) if type(data) ~= 'string' or not self.fd or self.fd <= 0 then - Log:ERROR("Send udp Error: 不完整的参数:"..(data or '')..','..(self.fd or '-1')) return end return udp.send(self.fd, data, #data) diff --git a/lualib/protobuf/protoc.lua b/lualib/protobuf/protoc.lua index 935407f2..3ce0e0fa 100644 --- a/lualib/protobuf/protoc.lua +++ b/lualib/protobuf/protoc.lua @@ -61,7 +61,7 @@ end function Lexer:expected(patt, name) if not self:test(patt) then - return self:error((name or "'"..patt.."'").." expected") + return self:error((name or ("'"..patt.."'")).." expected") end return self end diff --git a/lualib/protocol/http/code.lua b/lualib/protocol/http/code.lua index 16ed911c..d59f3f6a 100644 --- a/lualib/protocol/http/code.lua +++ b/lualib/protocol/http/code.lua @@ -23,7 +23,7 @@ return { [305] = "HTTP/1.1 305 Use Proxy", [306] = "HTTP/1.1 306 unused", [307] = "HTTP/1.1 307 Temporary Redirect", - [305] = "HTTP/1.1 308 Permanent Redirect", + [308] = "HTTP/1.1 308 Permanent Redirect", [400] = "HTTP/1.1 400 Bad Request", [401] = "HTTP/1.1 401 Unauthorized", @@ -66,6 +66,6 @@ return { [507] = "HTTP/1.1 507 Insufficient Storage", [508] = "HTTP/1.1 508 Loop Detected (WebDAV)", [510] = "HTTP/1.1 510 Not Extended", - [503] = "HTTP/1.1 511 Network Authentication Required", + [511] = "HTTP/1.1 511 Network Authentication Required", } diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index e545acca..14dc25a1 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -156,7 +156,7 @@ local function PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEA return end buf_len = buf_len + len - if buf_len >= (max_body_size or 1024 * 1024) then + if buf_len >= (max_body_size or (1024 * 1024)) then return nil, 413 end buffers[#buffers + 1] = buf @@ -580,7 +580,7 @@ function HTTP_PROTOCOL.DISPATCH(sock, opt, http) buffers = {} end if #buffers ~= 0 and #buffer > (max_header_size or 65535) then - sock:send(ERROR_RESPONSE(http, 431, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, req_time(start))) + -- sock:send(ERROR_RESPONSE(http, 431, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, req_time(start))) return sock:close() end -- 大部分情况下不需要主动关闭TCP连接, 这样有利于减少负载均衡器对连接池频繁销毁与建立. diff --git a/lualib/protocol/http/mime.lua b/lualib/protocol/http/mime.lua index 956efc0f..9e1ef9fd 100644 --- a/lualib/protocol/http/mime.lua +++ b/lualib/protocol/http/mime.lua @@ -31,7 +31,6 @@ return { ['mpa'] = 'video/mpeg', ['mpe'] = 'video/mpeg', ['mp2'] = 'video/mpeg', - ['mpe'] = 'video/mpeg', ['mpeg'] = 'video/mpeg', ['mp4'] = 'audio/mp4', ['qt'] = 'video/mpeg', diff --git a/lualib/protocol/mqtt/bit.lua b/lualib/protocol/mqtt/bit.lua index 70e28c87..b8066ade 100644 --- a/lualib/protocol/mqtt/bit.lua +++ b/lualib/protocol/mqtt/bit.lua @@ -1,6 +1,6 @@ -- wrapper around BitOp module -if _VERSION == "Lua 5.1" or _VERSION == "Lua 5.2" or type(jit) == "table" then +if _VERSION == "Lua 5.1" or _VERSION == "Lua 5.2" then return require("bit") else return require("protocol.mqtt.bit53") diff --git a/lualib/protocol/mqtt/init.lua b/lualib/protocol/mqtt/init.lua index 4e3259a8..8f6fc489 100644 --- a/lualib/protocol/mqtt/init.lua +++ b/lualib/protocol/mqtt/init.lua @@ -7,7 +7,7 @@ local library_version = "1.4.2" local require = require local string = require "string" -local calss = require "class" +local class = require "class" local tcp = require "internal.TCP" local Co = require "internal.Co" local log = require "logging" diff --git a/lualib/protocol/mqtt/protocol.lua b/lualib/protocol/mqtt/protocol.lua index 4e3ff76a..acdafffc 100644 --- a/lualib/protocol/mqtt/protocol.lua +++ b/lualib/protocol/mqtt/protocol.lua @@ -40,7 +40,7 @@ local band = bit.band local lshift = bit.lshift local rshift = bit.rshift local div = tools.div -local unpack = unpack or table.unpack +local unpack = table.unpack -- Create uint8 value data diff --git a/lualib/protocol/mssql.lua b/lualib/protocol/mssql.lua index 6867055e..7ee76d5c 100644 --- a/lualib/protocol/mssql.lua +++ b/lualib/protocol/mssql.lua @@ -13,7 +13,7 @@ local now = sys.now local new_tab = sys.new_tab local hostname = sys.hostname -local null = null or NULL +local null = null local type = type local pcall = pcall local error = error @@ -773,7 +773,8 @@ local function tds_read_response(self, before_packets) end local result = new_tab(32, 8) - local pos, fields, token_type, order, more_result, meta_field = 1 + local pos = 1 + local fields, token_type, order, more_result, meta_field while 1 do token_type, pos = strunpack(" 0 then + if type(timeout) == 'number' and timeout > 0 then self.timeout = timeout end return self diff --git a/lualib/protocol/stomp/protocol.lua b/lualib/protocol/stomp/protocol.lua index 25c92233..fe23028b 100644 --- a/lualib/protocol/stomp/protocol.lua +++ b/lualib/protocol/stomp/protocol.lua @@ -248,7 +248,7 @@ function protocol.ack (self, opt) })) if not ok then self.state = nil - return nil, pack + return nil, "Send error" end return true end @@ -260,7 +260,7 @@ function protocol.disconnect (self) ['receipt'] = 1, })) if not ok then - return nil, pack + return nil, "Send error" end return true end diff --git a/lualib/template/new.lua b/lualib/template/new.lua index 9cf7a5a9..65de036c 100644 --- a/lualib/template/new.lua +++ b/lualib/template/new.lua @@ -6,7 +6,6 @@ local newtab = sys.new_tab local setmetatable = setmetatable local tostring = tostring -local setfenv = setfenv local concat = table.concat local assert = assert local pcall = pcall From 0447b0bc74ef56ccdd39075adb8f7f7e2826e1f2 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 20 Dec 2020 01:42:09 +0800 Subject: [PATCH 669/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/json/init.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lualib/json/init.lua b/lualib/json/init.lua index 96d50946..eac4a287 100644 --- a/lualib/json/init.lua +++ b/lualib/json/init.lua @@ -36,13 +36,13 @@ function CJSON.sparse_array_to_null(array) end -- json序列化 -function CJSON.encode (...) - return cjson_encode(...) +function CJSON.encode (tab) + return cjson_encode(tab) end -- json反序列化 -function CJSON.decode (...) - return cjson_decode(...) +function CJSON.decode (rawJsonString) + return cjson_decode(rawJsonString) end return CJSON From c3166af9fe36747d5368e7df3b31794c361bcf55 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 24 Dec 2020 20:59:41 +0800 Subject: [PATCH 670/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=AF=95=E7=94=A8?= =?UTF-8?q?=E6=80=A7=E8=B4=A8=E7=9A=84=E5=A4=9A=E8=BF=9B=E7=A8=8B=E5=8F=82?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 136 ++++++++++++++++++++++------------------------ src/core.h | 4 +- src/core_ev.c | 97 ++++++++++----------------------- src/core_ev.h | 25 +++++---- src/core_memory.c | 23 +++----- src/core_memory.h | 6 +- src/core_start.c | 43 ++++++--------- src/core_sys.c | 16 ------ src/core_sys.h | 18 +++++- 9 files changed, 151 insertions(+), 217 deletions(-) diff --git a/src/core.c b/src/core.c index f0bf175e..c39eab98 100644 --- a/src/core.c +++ b/src/core.c @@ -18,67 +18,35 @@ "3rd/?.dylib;3rd/lib?.dylib;;" \ "3rd/?.dll;;3rd/msys-?.dll;;" -/* -const char *signame[]= { - "INVALID", - "SIGHUP", - "SIGINT", - "SIGQUIT", - "SIGILL", - "SIGTRAP", - "SIGABRT", - "SIGBUS", - "SIGFPE", - "SIGKILL", - "SIGUSR1", - "SIGSEGV", - "SIGUSR2", - "SIGPIPE", - "SIGALRM", - "SIGTERM", - "SIGSTKFLT", - "SIGCHLD", - "SIGCONT", - "SIGSTOP", - "SIGTSTP", - "SIGTTIN", - "SIGTTOU", - "SIGURG", - "SIGXCPU", - "SIGXFSZ", - "SIGVTALRM", - "SIGPROF", - "SIGWINCH", - "SIGPOLL", - "SIGPWR", - "SIGSYS", - NULL -}; - -#define signum_to_string(number) (signame[number]) -*/ - -static void // 忽略信号 -SIG_IGNORE(core_loop *loop, core_signal *signal, int revents){ +/* 忽略信号 */ +static void SIG_IGNORE(core_loop *loop, core_signal *signal, int revents){ // LOG("ERROR", signum_to_string(signal->signum)); return ; } -static void // 退出信号 -SIG_EXIT(core_loop *loop, core_signal *signal, int revents){ +/* 退出信号 */ +static void SIG_EXIT(core_loop *loop, core_signal *signal, int revents){ // LOG("ERROR", signum_to_string(signal->signum)); + if (ev_userdata(loop) && core_get_watcher_userdata(signal)) { + int index; + pid_t *pids = (pid_t *)ev_userdata(loop); + int pidcount = *(int*)core_get_watcher_userdata(signal); + for (index = 0; index < pidcount; index++) { + pid_t pid = pids[index]; + if (pid > 0) + kill(pid, SIGKILL); + } + } _exit(-1); return ; } -static void -ERROR_CB(const char *msg){ +static void ERROR_CB(const char *msg){ LOG("ERROR", msg); return ; } -static void * -EV_ALLOC(void *ptr, long nsize){ +static void *EV_ALLOC(void *ptr, long nsize){ // 为libev内存hook注入日志; if (nsize == 0) return xfree(ptr), NULL; for (;;) { @@ -89,8 +57,7 @@ EV_ALLOC(void *ptr, long nsize){ } } -static void * -L_ALLOC(void *ud, void *ptr, size_t osize, size_t nsize){ +static void* L_ALLOC(void *ud, void *ptr, size_t osize, size_t nsize){ // 为lua内存hook注入日志; /* 用户自定义数据 */ (void)ud; (void)osize; @@ -103,8 +70,7 @@ L_ALLOC(void *ud, void *ptr, size_t osize, size_t nsize){ } } -void -init_lua_libs(lua_State *L){ +void init_lua_libs(lua_State *L){ /* lua 标准库 */ luaL_openlibs(L); @@ -142,8 +108,7 @@ core_signal sigint; core_signal sigterm; core_signal sigquit; -void -signal_init(){ +void signal_init(int* workers){ /* 忽略父进程退出的信号 */ core_signal_init(&sighup, SIG_IGNORE, SIGHUP); @@ -160,58 +125,85 @@ signal_init(){ /* TERM信号 显示退出 */ core_signal_init(&sigterm, SIG_EXIT, SIGTERM); core_signal_start(CORE_LOOP_ &sigterm); - + if (workers) + core_set_watcher_userdata(&sigterm, workers); /* INT信号 显示退出 */ core_signal_init(&sigint, SIG_EXIT, SIGINT); core_signal_start(CORE_LOOP_ &sigint); + if (workers) + core_set_watcher_userdata(&sigint, workers); /* QUIT信号 显示退出 */ core_signal_init(&sigquit, SIG_EXIT, SIGQUIT); core_signal_start(CORE_LOOP_ &sigquit); + if (workers) + core_set_watcher_userdata(&sigquit, workers); } -void -init_main(const char script_entry[]){ +int core_slave_run(const char entry[]) { + core_loop *loop = core_loop_fork(core_default_loop()); int status = 0; lua_State *L = lua_newstate(L_ALLOC, NULL); - if (!L) return ; + if (!L) + exit(-1); init_lua_libs(L); CO_GCRESET(L); - status = luaL_loadfile(L, script_entry); + status = luaL_loadfile(L, entry); if (status > 1){ LOG("ERROR", lua_tostring(L, -1)); - return lua_close(L), _exit(-1); + lua_close(L); + exit(-1); } status = CO_RESUME(L, NULL, 0); if (status > 1){ LOG("ERROR", lua_tostring(L, -1)); - return lua_close(L), _exit(-1); + lua_close(L); + _exit(-1); } if (status == LUA_YIELD) - signal_init(); + signal_init(NULL); + + return core_start(loop, 0); +} +int core_master_run(int *pids[], int* pidcount) { + /* 初始化信号 */ + signal_init(pidcount); + /* 设置pid */ + ev_set_userdata(core_default_loop(), pids); + /* 初始化主进程 */ + return core_start(core_loop_fork(core_default_loop()), 0); } -void -core_sys_init(const char script_entry[]){ +int core_run(const char entry[], int workers) { /* hook libev 内存分配 */ core_ev_set_allocator(EV_ALLOC); /* hook 事件循环错误信息 */ core_ev_set_syserr_cb(ERROR_CB); - /* 初始化Lua脚本 */ - init_main(script_entry); - -} - -int -core_sys_run(){ - return core_start(core_default_loop(), 0); + /* 初始化进程 */ +#if defined(__MSYS__) + /* Windows下不可使用多进程 */ + workers = 1; +#endif + pid_t pids[workers]; + int i; + for (i = 0; i < workers; i++) { + int pid = fork(); + if (pid == 0){ + return core_slave_run(entry); + } else if (pid < 0) { + LOG("ERROR", "Create Process Error."); + _exit(-1); + } + pids[i] = pid; + } + return core_master_run((pid_t **)&pids, &workers); } diff --git a/src/core.h b/src/core.h index f3a80523..46e30b21 100644 --- a/src/core.h +++ b/src/core.h @@ -5,8 +5,6 @@ #include "core_memory.h" #include "core_ev.h" -void core_sys_init(); - -int core_sys_run(); +int core_run(const char entry[], int workers); #endif diff --git a/src/core_ev.c b/src/core_ev.c index 52c16748..9435b4e3 100644 --- a/src/core_ev.c +++ b/src/core_ev.c @@ -1,139 +1,102 @@ #include "core_ev.h" /* =========== Timer =========== */ -void -core_timer_init(core_timer *timer, _TIMER_CB cb){ - +void core_timer_init(core_timer *timer, _TIMER_CB cb){ timer->repeat = timer->at = 0x0; - ev_init(timer, cb); - } -void -core_timer_start(core_loop *loop, core_timer *timer, ev_tstamp timeout){ - +void core_timer_start(core_loop *loop, core_timer *timer, ev_tstamp timeout){ timer->repeat = timeout; - ev_timer_again(loop ? loop : CORE_LOOP, timer); - } -void -core_timer_stop(core_loop *loop, core_timer *timer){ - +void core_timer_stop(core_loop *loop, core_timer *timer){ timer->repeat = timer->at = 0x0; - ev_timer_again(loop ? loop : CORE_LOOP, timer); - } /* =========== Timer =========== */ /* =========== IO =========== */ -void -core_io_init(core_io *io, _IO_CB cb, int fd, int events){ - +void core_io_init(core_io *io, _IO_CB cb, int fd, int events){ ev_io_init(io, cb, fd, events); - } -void -core_io_start(core_loop *loop, core_io *io){ - +void core_io_start(core_loop *loop, core_io *io){ ev_io_start(loop ? loop : CORE_LOOP, io); - } -void -core_io_stop(core_loop *loop, core_io *io){ - +void core_io_stop(core_loop *loop, core_io *io){ if (io->events || io->fd){ - ev_io_stop(loop ? loop : CORE_LOOP, io); - io->fd = io->events = 0x0; - } - } /* =========== IO =========== */ /* =========== TASK =========== */ - -void -core_task_init(core_task *task, _TASK_CB cb){ - +void core_task_init(core_task *task, _TASK_CB cb){ ev_idle_init(task, cb); - } -void -core_task_start(core_loop *loop, core_task *task){ - +void core_task_start(core_loop *loop, core_task *task){ ev_idle_start(loop ? loop : CORE_LOOP, task); - } -void -core_task_stop(core_loop *loop, core_task *task){ - +void core_task_stop(core_loop *loop, core_task *task){ ev_idle_stop(loop ? loop : CORE_LOOP, task); - } /* =========== TASK =========== */ /* =========== Signal =========== */ -void -core_signal_init(core_signal *signal, _SIGNAL_CB cb, int signum){ +void core_signal_init(core_signal *signal, _SIGNAL_CB cb, int signum){ ev_signal_init(signal, cb, signum); } -void -core_signal_start(core_loop *loop, core_signal *signal){ +void core_signal_start(core_loop *loop, core_signal *signal){ ev_signal_start(loop ? loop : CORE_LOOP, signal); } /* =========== Signal =========== */ -core_loop * -core_default_loop(){ +core_loop* core_loop_fork(core_loop *loop) { + ev_loop_fork(loop); + return loop; +} - /* 默认使用 SELECT */ - int BEST_BACKEND = EVBACKEND_SELECT; +core_loop* core_default_loop(){ -#ifdef EV_USE_EPOLL /* Linux */ - BEST_BACKEND = EVBACKEND_EPOLL; -#endif + int BEST_BACKEND = 0; -#ifdef EV_USE_KQUEUE /* Unix | Mac */ - BEST_BACKEND = EVBACKEND_KQUEUE; +#if defined(__MSYS__) + BEST_BACKEND |= EVBACKEND_SELECT; +#elif defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) + BEST_BACKEND |= EVBACKEND_KQUEUE; +#elif defined(linux) || defined(__linux) || defined(__linux__) + BEST_BACKEND |= EVBACKEND_EPOLL; +#else + BEST_BACKEND |= EVBACKEND_PORT | EVBACKEND_DEVPOLL | EVBACKEND_SELECT; #endif - return ev_default_loop( BEST_BACKEND ); + return ev_default_loop( BEST_BACKEND | EVFLAG_FORKCHECK ); } -void -core_break(core_loop *loop, int mode){ +void core_break(core_loop *loop, int mode){ return ev_break(loop ? loop : CORE_LOOP, mode); } -void -core_ev_set_allocator(void *(*cb)(void *ptr, long size)){ +void core_ev_set_allocator(void *(*cb)(void *ptr, long size)){ return ev_set_allocator(cb); } -void -core_ev_set_syserr_cb(void (*cb)(const char *msg)){ +void core_ev_set_syserr_cb(void (*cb)(const char *msg)){ return ev_set_syserr_cb(cb); } -int -core_start(core_loop *loop, int mode){ - +int core_start(core_loop *loop, int mode){ return ev_run(loop ? loop : CORE_LOOP, mode); - } diff --git a/src/core_ev.h b/src/core_ev.h index 1b746381..44f4a191 100644 --- a/src/core_ev.h +++ b/src/core_ev.h @@ -19,31 +19,32 @@ #define EV_PREPARE_ENABLE 0 #define EV_PREPARE_ENABLE 0 +/* 使用四叉堆结构 */ #define EV_USE_4HEAP 1 - #define EV_HEAP_CACHE_AT 1 -/* 单进程/单线程模型 */ +/* 单线程模型 */ #define EV_NO_SMP 1 #define EV_NO_THREADS 1 /* eventfd 与 signalfd */ -#if !defined(__MSYS__) && (defined(__linux) || defined(__linux__)) - // #define EV_USE_LINUXAIO 1 /* 待完整支持AIO后再根据情况开启 */ - #define EV_USE_EPOLL 1 - #define EV_USE_INOTIFY 1 +#if defined(__linux) || defined(__linux__) + #ifdef __MSYS__ + #define EV_USE_SELECT 1 + #else + #define EV_USE_EPOLL 1 + #define EV_USE_INOTIFY 1 + #define EV_USE_EVENTFD 1 + #endif + // MSYS与Linux都支持 #define EV_USE_SIGNALFD 1 - #define EV_USE_EVENTFD 1 + #define EV_USE_TIMERFD 1 #endif #if defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) #define EV_USE_KQUEUE 1 #endif -#ifdef __MSYS__ - #define EV_USE_SELECT 1 -#endif - #include "ev.h" #define CORE_LOOP core_default_loop() @@ -109,6 +110,8 @@ void core_break(core_loop *loop, int mode); int core_start(core_loop *loop, int mode); +core_loop* core_loop_fork(core_loop* loop); + core_loop* core_default_loop(); #endif diff --git a/src/core_memory.c b/src/core_memory.c index 7273cdd3..acdcd6b3 100644 --- a/src/core_memory.c +++ b/src/core_memory.c @@ -1,26 +1,19 @@ #include "core_memory.h" -void * -xmalloc(size_t size){ - return malloc(size); +void* xmalloc(size_t size){ + return malloc(size); } - -void * -xcalloc(size_t nmemb, size_t size){ - return calloc(nmemb, size); +void* xcalloc(size_t nmemb, size_t size){ + return calloc(nmemb, size); } - -void * -xrealloc(void* ptr, size_t size){ - return realloc(ptr, size); +void* xrealloc(void* ptr, size_t size){ + return realloc(ptr, size); } - -void -xfree(void *ptr){ - return free(ptr); +void xfree(void *ptr){ + return free(ptr); } \ No newline at end of file diff --git a/src/core_memory.h b/src/core_memory.h index fff81959..2f6ccc75 100644 --- a/src/core_memory.h +++ b/src/core_memory.h @@ -14,11 +14,11 @@ #endif -void *xmalloc (size_t size); +void* xmalloc (size_t size); -void *xcalloc (size_t nmemb, size_t size); +void* xcalloc (size_t nmemb, size_t size); -void *xrealloc(void* ptr, size_t size); +void* xrealloc(void* ptr, size_t size); void xfree(void *ptr); diff --git a/src/core_start.c b/src/core_start.c index 8960544c..dc244284 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -1,30 +1,15 @@ #include "core.h" -#ifdef __MSYS__ - const char *__OS__ = "Windows"; -#endif - -#if !defined(__MSYS__) && (defined(__linux) || defined(__linux__)) - const char *__OS__ = "Linux"; -#endif - -#ifdef __APPLE__ - const char *__OS__ = "Apple"; -#endif - -#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) - const char *__OS__ = "Unix"; -#endif - #define __CFADMIN_VERSION__ "1.0" - #define MAX_ENTRY_LENGTH (1 << 10) static char script_entry[MAX_ENTRY_LENGTH] = "script/main.lua"; static char pid_filename[MAX_ENTRY_LENGTH] = "cfadmin.pid"; +static int workers = 1; + /* 打印使用指南 */ void usage_print() { printf("cfadmin System : %s(%s)\n", __OS__, __VERSION__ ); @@ -72,7 +57,7 @@ void specify_pid_file(const char *filename) { memmove(pid_filename, filename, strlen(filename)); } -/* 给指定`PID`或包含`PID`的文件发送`SIGKILL`信号 */ +/* 给指定`PID`或包含`PID`的文件发送`SIGQUIT`信号 */ void specify_kill_process(const char *spid) { int pid = atoi(spid); if (pid <= 1) { @@ -91,7 +76,13 @@ void specify_kill_process(const char *spid) { return; } } - kill(pid, SIGKILL); + kill(pid, SIGQUIT); +} + +void specify_workers(const char* w) { + workers = atoi(w); + if (workers <= 0 ) + workers = 1; } /* 后台运行 */ @@ -102,8 +93,11 @@ void specify_process_daemon() { void check_args(int argc, char const *argv[]) { int opt = -1; int opterr = 0; - while ((opt = getopt(argc, (char *const *)argv, "hde:p:k:")) != -1) { + while ((opt = getopt(argc, (char *const *)argv, "hde:p:k:w:")) != -1) { switch(opt) { + case 'w': + specify_workers(optarg); + continue; case 'e': specify_entry_file(optarg); continue; @@ -134,11 +128,6 @@ int main(int argc, char const *argv[]) /* 参数检查 */ check_args(argc, argv); - /* 系统初始化 */ - core_sys_init(script_entry); - - /* 运行事件循环 */ - core_sys_run(); - - return 0; + /* 初始化 */ + return core_run(script_entry, workers); } diff --git a/src/core_sys.c b/src/core_sys.c index be2c9dbb..6ea65538 100644 --- a/src/core_sys.c +++ b/src/core_sys.c @@ -1,21 +1,5 @@ #include "core_sys.h" -#ifdef __MSYS__ - const char *__OS__ = "Windows"; -#endif - -#if !defined(__MSYS__) && (defined(__linux) || defined(__linux__)) - const char *__OS__ = "Linux"; -#endif - -#ifdef __APPLE__ - const char *__OS__ = "Apple"; -#endif - -#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) - const char *__OS__ = "Unix"; -#endif - /* 此方法提供一个精确到微秒级的时间戳 */ double now(void){ struct timespec now = {}; diff --git a/src/core_sys.h b/src/core_sys.h index 2d0f722b..94d3c08c 100644 --- a/src/core_sys.h +++ b/src/core_sys.h @@ -24,9 +24,21 @@ #include #include -#include "lua.h" -#include "lualib.h" -#include "lauxlib.h" +#include +#include +#include + +#if defined(__MSYS__) + #define __OS__ ("Windows") +#elif defined(__APPLE__) + #define __OS__ ("Apple") +#elif defined(linux) || defined(__linux) || defined(__linux__) + #define __OS__ ("Linux") +#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) + #define __OS__ ("BSD") +#else + #define __OS__ ("Unix") +#endif #if LUA_VERSION_NUM >= 504 #ifndef CO_GCRESET From 15be69ad1f21579130ffadd097b85cdc74397c78 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 24 Dec 2020 21:00:21 +0800 Subject: [PATCH 671/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=9A=84=E5=BC=95=E7=94=A8=E4=B8=8E=E5=91=BD?= =?UTF-8?q?=E5=90=8D=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/http/profile.lua | 6 ++--- lualib/admin/http/system/header.lua | 22 +++++++++--------- lualib/admin/http/system/menu.lua | 10 ++++---- lualib/admin/http/system/role.lua | 12 +++++----- lualib/admin/http/system/user.lua | 36 ++++++++++++++--------------- lualib/protocol/http/init.lua | 14 +++++++---- 6 files changed, 52 insertions(+), 48 deletions(-) diff --git a/lualib/admin/http/profile.lua b/lualib/admin/http/profile.lua index ec431186..9947033e 100644 --- a/lualib/admin/http/profile.lua +++ b/lualib/admin/http/profile.lua @@ -101,17 +101,17 @@ function profile.response (content) local args = content.args local token = args.token if not token then - return json_encode({code = 400, data = null, msg = '2. 错误的参数'}) + return json_encode({code = 400, msg = '2. 错误的参数'}) end -- 验证Token local exists = user_token.token_exists(db, token) if not exists then - return json_encode({code = 400, data = null, msg = '3. token不存在'}) + return json_encode({code = 400, msg = '3. token不存在'}) end args.id = toint(exists.uid) local user_info = user.user_info(db, args.id) if not user_info then - return json_encode({code = 400, data = null, msg = '4. 用户不存在'}) + return json_encode({code = 400, msg = '4. 用户不存在'}) end if args.action == 'update_password' then if not args.password or not args.cupassword then diff --git a/lualib/admin/http/system/header.lua b/lualib/admin/http/system/header.lua index 1fec3833..f1f6a3c7 100644 --- a/lualib/admin/http/system/header.lua +++ b/lualib/admin/http/system/header.lua @@ -119,32 +119,32 @@ function system.header_response (content) local db = config.db local args = content.args if type(args) ~= 'table' then - return json_encode({code = 400, data = null, msg = '1. 错误的参数'}) + return json_encode({code = 400, msg = '1. 错误的参数'}) end local token = args.token if not token then - return json_encode({code = 400, data = null, msg = '2. 错误的参数'}) + return json_encode({code = 400, msg = '2. 错误的参数'}) end -- 验证Token local exists = user_token.token_exists(db, token) if not exists then - return json_encode({code = 400, data = null, msg = '3. token不存在或权限不足'}) + return json_encode({code = 400, msg = '3. token不存在或权限不足'}) end local user_info = user.user_info(db, exists.uid) if not user_info or user_info.is_admin == 0 then - return json_encode({code = 400, data = null, msg = '4. 用户权限不足'}) + return json_encode({code = 400, msg = '4. 用户权限不足'}) end if args.action == 'delete' then local headerid = toint(args.headerid) if not headerid then - return json_encode({ code = 400, data = null, msg = '1. 未知的header' }) + return json_encode({ code = 400, msg = '1. 未知的header' }) end local exists = header.header_exists(db, headerid) if not exists then - return json_encode({ code = 400, data = null, msg = '2. 试图删除一个不存在的的header' }) + return json_encode({ code = 400, msg = '2. 试图删除一个不存在的的header' }) end header.header_delete(db, headerid) - return json_encode({ code = 0, data = null, msg = '删除成功'}) + return json_encode({ code = 0, msg = '删除成功'}) end if args.action == 'list' then local headers = header.header_list(db, args) @@ -156,7 +156,7 @@ function system.header_response (content) end if args.action == 'add' then if not args.url or not args.name then - return json_encode({ code = 400, data = null, msg = "1. 添加导航栏参数错误"}) + return json_encode({ code = 400, msg = "1. 添加导航栏参数错误"}) end args.url = url_decode(args.url) args.name = url_decode(args.name) @@ -168,12 +168,12 @@ function system.header_response (content) end if args.action == 'edit' then if not args.id then - json_encode({ code = 400, data = null, msg = "1. 找不到此header"}) + json_encode({ code = 400, msg = "1. 找不到此header"}) end local url = tostring(args.url) local name = tostring(args.name) if not url or not name then - json_encode({ code = 400, data = null, msg = "2. 非法的参数"}) + json_encode({ code = 400, msg = "2. 非法的参数"}) end args.url = url_decode(args.url) args.name = url_decode(args.name) @@ -183,7 +183,7 @@ function system.header_response (content) end return json_encode({ code = 0, msg = "修改成功"}) end - return json_encode({code = 500, data = null, msg = '恭喜您完美的错过了所有正确参数'}) + return json_encode({code = 500, msg = '恭喜您完美的错过了所有正确参数'}) end diff --git a/lualib/admin/http/system/menu.lua b/lualib/admin/http/system/menu.lua index 3ed8680e..5f228301 100644 --- a/lualib/admin/http/system/menu.lua +++ b/lualib/admin/http/system/menu.lua @@ -176,20 +176,20 @@ function system.menu_response (content) local db = config.db local args = content.args if type(args) ~= 'table' then - return json_encode({code = 400, data = null, msg = '1. 错误的参数'}) + return json_encode({code = 400, msg = '1. 错误的参数'}) end local token = args.token if not token then - return json_encode({code = 400, data = null, msg = '2. 错误的参数'}) + return json_encode({code = 400, msg = '2. 错误的参数'}) end -- 验证Token local exists = user_token.token_exists(db, token) if not exists then - return json_encode({code = 400, data = null, msg = '3. token不存在或权限不足'}) + return json_encode({code = 400, msg = '3. token不存在或权限不足'}) end local user_info = user.user_info(db, exists.uid) if not user_info or user_info.is_admin == 0 then - return json_encode({code = 400, data = null, msg = '4. 用户权限不足'}) + return json_encode({code = 400, msg = '4. 用户权限不足'}) end -- 添加菜单 local action = args.action @@ -237,7 +237,7 @@ function system.menu_response (content) return json_encode({ code = 0, data = menus, count = menu.menu_count(db) }) end -- 如果没有action字段则返回500 - return json_encode({code = 500, data = null, msg = '恭喜您完美的给予了错误的参数'}) + return json_encode({code = 500, msg = '恭喜您完美的给予了错误的参数'}) end diff --git a/lualib/admin/http/system/role.lua b/lualib/admin/http/system/role.lua index 8a7d9b60..7232da0e 100644 --- a/lualib/admin/http/system/role.lua +++ b/lualib/admin/http/system/role.lua @@ -123,29 +123,29 @@ function system.role_response (content) local args = content.args if type(args) ~= 'table' then if not content.json then -- 可以用这个字段判断是否json请求 - return json_encode({code = 400, data = null, msg = '1. 错误的参数'}) + return json_encode({code = 400, msg = '1. 错误的参数'}) end args = json_decode(content.body) end local token = args.token if not token then - return json_encode({code = 400, data = null, msg = '2. 错误的参数'}) + return json_encode({code = 400, msg = '2. 错误的参数'}) end -- 验证Token local exists = user_token.token_exists(db, token) if not exists then - return json_encode({code = 400, data = null, msg = '3. token不存在或权限不足'}) + return json_encode({code = 400, msg = '3. token不存在或权限不足'}) end local user_info = user.user_info(db, exists.uid) if not user_info or user_info.is_admin == 0 then - return json_encode({code = 400, data = null, msg = '4. 用户权限不足'}) + return json_encode({code = 400, msg = '4. 用户权限不足'}) end if args.action == 'list' then return json_encode({code = 0, count = role.role_count(db), data = role.role_list(db, args)}) end if args.action == 'add' then if not args.name or not args.permissions then - return json_encode({code = 400, data = null, msg = '错误的role创建参数'}) + return json_encode({code = 400, msg = '错误的role创建参数'}) end args.name = url_decode(args.name) if role.role_name_exists(db, args.name) then @@ -177,7 +177,7 @@ function system.role_response (content) if args.action == 'edit' then args.id = toint(args.id) if not args.id or not args.name or type(args.permissions) ~= 'table' then - return json_encode({code = 400, data = null, msg = '错误的role修改参数'}) + return json_encode({code = 400, msg = '错误的role修改参数'}) end args.name = url_decode(args.name) role.role_update(db, args) diff --git a/lualib/admin/http/system/user.lua b/lualib/admin/http/system/user.lua index cb757813..61b094ea 100644 --- a/lualib/admin/http/system/user.lua +++ b/lualib/admin/http/system/user.lua @@ -118,30 +118,30 @@ function system.user_response (content) local db = config.db local args = content.args if type(args) ~= 'table' then - return json_encode({code = 400, data = null, msg = '1. 错误的参数'}) + return json_encode({code = 400, msg = '1. 错误的参数'}) end local token = args.token if not token then - return json_encode({code = 400, data = null, msg = '2. 错误的参数'}) + return json_encode({code = 400, msg = '2. 错误的参数'}) end -- 验证Token local exists = user_token.token_exists(db, token) if not exists then - return json_encode({code = 400, data = null, msg = '3. token不存在或权限不足'}) + return json_encode({code = 400, msg = '3. token不存在或权限不足'}) end local user_info = user.user_info(db, exists.uid) if not user_info or user_info.is_admin ~= 1 then - return json_encode({code = 400, data = null, msg = '4. 用户权限不足'}) + return json_encode({code = 400, msg = '4. 用户权限不足'}) end local action = args.action -- 查找用户(模糊) if action == 'findUser' then if not args.value or not args.condition then - return json_encode({code = 400, data = null, msg = '1. 无效的参数'}) + return json_encode({code = 400, msg = '1. 无效的参数'}) end local users, count = user.find_user(db, args) if not users or not count then - return json_encode({code = 400, data = null, msg = '2. 无效的参数'}) + return json_encode({code = 400, msg = '2. 无效的参数'}) end return json_encode({code = 0, data = users, count = count}) end @@ -156,22 +156,22 @@ function system.user_response (content) -- 添加用户 if action == 'add' then if not args.name or not args.username or not args.password then - return json_encode({code = 400, data = null, msg = '1. 用户信息不完善'}) + return json_encode({code = 400, msg = '1. 用户信息不完善'}) end args.phone = toint(args.phone) args.role = toint(args.role) if not args.role or not args.email or not args.phone then - return json_encode({code = 400, data = null, msg = '2. 用户信息不完善'}) + return json_encode({code = 400, msg = '2. 用户信息不完善'}) end if #args.username < 6 or #args.username > 20 or #args.password < 6 or #args.password > 20 then - return json_encode({code = 400, data = null, msg = '3. 用户名与密码需要在6~20字符之间'}) + return json_encode({code = 400, msg = '3. 用户名与密码需要在6~20字符之间'}) end args.name = utils.escape_script(url.decode(args.name)) args.email = utils.escape_script(url.decode(args.email)) args.username = utils.escape_script(url.decode(args.username)) local exists = user.user_name_or_username_exists(db, args.name, args.username) if exists then - return json_encode({code = 400, data = null, msg = '4. 用户已存在'}) + return json_encode({code = 400, msg = '4. 用户已存在'}) end args.password = crypt.sha1(args.password, true) local ok = user.user_add(db, args) @@ -184,14 +184,14 @@ function system.user_response (content) if action == 'delete' then local uid = toint(args.id) if not uid then - return json_encode({code = 400, data = null, msg = '1. 未知的用户ID'}) + return json_encode({code = 400, msg = '1. 未知的用户ID'}) end if exists.uid == uid then - return json_encode({code = 401, data = null, msg = "2. 不能删除当前用户"}) + return json_encode({code = 401, msg = "2. 不能删除当前用户"}) end local exists = user.user_exists(db, nil, uid) if not exists then - return json_encode({code = 403, data = null, msg = '3. 试图删除不存在的用户'}) + return json_encode({code = 403, msg = '3. 试图删除不存在的用户'}) end user.user_delete(db, uid) user_token.token_delete(db, uid) -- 清除Token @@ -208,14 +208,14 @@ function system.user_response (content) end if action == 'edit' then if not args.id or not args.role then - return json_encode({code = 400, data = null, msg = "1. 未知的用户与权限"}) + return json_encode({code = 400, msg = "1. 未知的用户与权限"}) end if not args.name then - return json_encode({code = 400, data = null, msg = "2. 未知的用户名"}) + return json_encode({code = 400, msg = "2. 未知的用户名"}) end args.name = utils.escape_script(url.decode(args.name)) if not args.username or not args.password then - return json_encode({code = 400, data = null, msg = "3. 未知的账户与密码"}) + return json_encode({code = 400, msg = "3. 未知的账户与密码"}) end if #args.username < 6 or #args.username > 20 or #args.password < 6 or #args.password > 20 then return json_encode({code = 401, msg = "4. 账户/密码应该为6-20个字符."}) @@ -224,7 +224,7 @@ function system.user_response (content) args.password = crypt.sha1(args.password, true) args.username = utils.escape_script(url.decode(args.username)) if not args.phone or not args.email then - return json_encode({code = 400, data = null, msg = "5. 无效的邮箱与手机号"}) + return json_encode({code = 400, msg = "5. 无效的邮箱与手机号"}) end args.email = utils.escape_script(url.decode(args.email)) local ok = user.user_update(db, args) @@ -235,7 +235,7 @@ function system.user_response (content) return json_encode({code = 0, msg = "SUCCESS"}) end -- 如果没有action字段则返回500 - return json_encode({code = 500, data = null, msg = '恭喜您完美的给予了错误的参数'}) + return json_encode({code = 500, msg = '恭喜您完美的给予了错误的参数'}) end diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index 14dc25a1..3480875d 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -449,7 +449,7 @@ function HTTP_PROTOCOL.DISPATCH(sock, opt, http) if typ == HTTP_PROTOCOL.API or typ == HTTP_PROTOCOL.USE then -- 如果httpd开启了记录Cookie字段, 则每次尝试是否deCookie - if enable_cookie and typ == HTTP_PROTOCOL.USE then + if enable_cookie then deCookie(HEADER["Cookie"] or HEADER["cookie"]) end if http_router.enable_rest then @@ -467,7 +467,7 @@ function HTTP_PROTOCOL.DISPATCH(sock, opt, http) ok, body = safe_call(cls, tab_copy(content)) end -- 如果httpd开启了记录Cookie字段, 则每次尝试是否需要seCookie - if enable_cookie and typ == HTTP_PROTOCOL.USE then + if enable_cookie then local Cookies = seCookie() for _, Cookie in ipairs(Cookies) do header[#header+1] = Cookie @@ -500,13 +500,17 @@ function HTTP_PROTOCOL.DISPATCH(sock, opt, http) sock:send(ERROR_RESPONSE(http, statucode, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, req_time(start))) goto CONTINUE end + print(filepath) statucode = 200 header[#header+1] = REQUEST_STATUCODE_RESPONSE(statucode) local conten_type = REQUEST_MIME_RESPONSE(lower(file_type or '')) if not conten_type then - header[#header+1] = 'Content-Disposition: attachment' -- 确保浏览器提示需要下载 + -- 确保浏览器提示需要下载 + header[#header+1] = fmt('Content-Disposition: attachment; filename="%s"', filepath) static = 'Content-Type: application/octet-stream' else + -- 确保内容展示在浏览器内 + header[#header+1] = 'Content-Disposition: inline' static = 'Content-Type: ' .. conten_type .. '; charset=utf-8' end end @@ -522,9 +526,9 @@ function HTTP_PROTOCOL.DISPATCH(sock, opt, http) header[#header+1] = Connection if typ == HTTP_PROTOCOL.API or typ == HTTP_PROTOCOL.USE then if typ == HTTP_PROTOCOL.API then - header[#header+1] = 'Content-Type: ' .. REQUEST_MIME_RESPONSE('json') .. "; charset=utf-8" + header[#header+1] = "Content-Type: application/json; charset=utf-8" else - header[#header+1] = 'Content-Type: ' .. REQUEST_MIME_RESPONSE('html') .. "; charset=utf-8" + header[#header+1] = "Content-Type: text/html; charset=utf-8" end if type(body) ~= 'string' or body == '' then Log:ERROR("Response Error ["..(split(PATH , 1, (find(PATH, '?') or 0 ) - 1)).."]: response must be a string and not empty.") From 2bfd8c9402ece7c2acc620e0cb5d24400d7f003a Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 24 Dec 2020 21:00:40 +0800 Subject: [PATCH 672/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcjson/Makefile | 4 ++-- luaclib/src/lcjson/fpconv.c | 2 +- luaclib/src/lcjson/lua_cjson.c | 2 +- luaclib/src/lcjson/strbuf.c | 2 +- luaclib/src/lcjson/strbuf.h | 2 +- luaclib/src/lcrypt/Makefile | 4 ++-- luaclib/src/lcrypt/lcrypt.h | 2 +- luaclib/src/lfs/Makefile | 2 +- luaclib/src/lfs/lfs.c | 4 +--- luaclib/src/lhttpparser/Makefile | 2 +- luaclib/src/lhttpparser/lhttpparser.c | 2 +- luaclib/src/lmsgpack/Makefile | 2 +- luaclib/src/lmsgpack/lmsgpack.c | 2 +- luaclib/src/lpeg/lpcap.c | 3 +-- luaclib/src/lpeg/lpcode.c | 6 +----- luaclib/src/lpeg/lpcode.h | 2 +- luaclib/src/lpeg/lpprint.c | 4 ++-- luaclib/src/lpeg/lptree.c | 6 +----- luaclib/src/lpeg/lpvm.c | 7 +------ luaclib/src/lpeg/makefile | 2 +- luaclib/src/ltcp.c | 16 ++++++++-------- luaclib/src/lz/Makefile | 2 +- luaclib/src/lz/lzlib.c | 2 +- 23 files changed, 33 insertions(+), 49 deletions(-) diff --git a/luaclib/src/lcjson/Makefile b/luaclib/src/lcjson/Makefile index 79ef25d0..10d01570 100644 --- a/luaclib/src/lcjson/Makefile +++ b/luaclib/src/lcjson/Makefile @@ -12,8 +12,8 @@ default : CFLAGS = -O3 -Wall -shared -fPIC CC = cc -INCLUDES = -I../../../ -I/usr/local/include -LIBS = -L../ -L../../../ -L/usr/local/lib +INCLUDES = -I../../../src -I/usr/local/include +LIBS = -L../ -L../../ -L../../../ -L/usr/local/lib DLL = -lcore -llua build: diff --git a/luaclib/src/lcjson/fpconv.c b/luaclib/src/lcjson/fpconv.c index 0e058873..2d2a5d36 100644 --- a/luaclib/src/lcjson/fpconv.c +++ b/luaclib/src/lcjson/fpconv.c @@ -28,7 +28,7 @@ * fpconv_* will around these issues with a translation buffer if required. */ -#include "../../../src/core.h" +#include #include "fpconv.h" diff --git a/luaclib/src/lcjson/lua_cjson.c b/luaclib/src/lcjson/lua_cjson.c index 993bc3c9..c09e83ac 100644 --- a/luaclib/src/lcjson/lua_cjson.c +++ b/luaclib/src/lcjson/lua_cjson.c @@ -36,7 +36,7 @@ * difficult to know object/array sizes ahead of time. */ -#include "../../../src/core.h" +#include #include "strbuf.h" #include "fpconv.h" diff --git a/luaclib/src/lcjson/strbuf.c b/luaclib/src/lcjson/strbuf.c index 8b4dc00a..09d5b711 100644 --- a/luaclib/src/lcjson/strbuf.c +++ b/luaclib/src/lcjson/strbuf.c @@ -22,7 +22,7 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "../../../src/core.h" +#include #include "strbuf.h" diff --git a/luaclib/src/lcjson/strbuf.h b/luaclib/src/lcjson/strbuf.h index 47bb4c2c..31b41bcc 100644 --- a/luaclib/src/lcjson/strbuf.h +++ b/luaclib/src/lcjson/strbuf.h @@ -22,7 +22,7 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "../../../src/core.h" +#include /* Workaround for MSVC */ #ifdef _MSC_VER diff --git a/luaclib/src/lcrypt/Makefile b/luaclib/src/lcrypt/Makefile index 742e04fa..eec6b338 100644 --- a/luaclib/src/lcrypt/Makefile +++ b/luaclib/src/lcrypt/Makefile @@ -12,8 +12,8 @@ CC = cc CFLAGS = -O3 -Wall -shared -fPIC -fno-strict-aliasing DLL = -lcore -llua -lcrypto -INCLUDES = -I../../../ -I/usr/local/include -LIBS = -L../ -L../../../ -L/usr/local/lib +INCLUDES = -I../../../src -I/usr/local/include +LIBS = -L../ -L../../ -L../../../ -L/usr/local/lib build: @$(CC) -o lcrypt.so lcrypt.c aes.c des.c dh.c rsa.c sha.c hmac.c hmac_ex.c b64.c crc.c url.c hex.c uuid.c sm.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) diff --git a/luaclib/src/lcrypt/lcrypt.h b/luaclib/src/lcrypt/lcrypt.h index 0870ac7b..f37a6138 100644 --- a/luaclib/src/lcrypt/lcrypt.h +++ b/luaclib/src/lcrypt/lcrypt.h @@ -1,6 +1,6 @@ #define LUA_LIB -#include "../../../src/core.h" +#include #include #include #include diff --git a/luaclib/src/lfs/Makefile b/luaclib/src/lfs/Makefile index ae258f13..65728318 100644 --- a/luaclib/src/lfs/Makefile +++ b/luaclib/src/lfs/Makefile @@ -10,7 +10,7 @@ default : CC = cc INCLUDES += -I../../../src -I/usr/local/include -LIBS = -L../ -L../../../ -L/usr/local/lib +LIBS = -L../ -L../../ -L../../../ -L/usr/local/lib CFLAGS = -O3 -Wall -shared -fPIC DLL = -lcore -llua diff --git a/luaclib/src/lfs/lfs.c b/luaclib/src/lfs/lfs.c index 8627f190..05d0b552 100644 --- a/luaclib/src/lfs/lfs.c +++ b/luaclib/src/lfs/lfs.c @@ -63,9 +63,7 @@ #define LFS_MAXPATHLEN MAXPATHLEN #endif -#include "lua.h" -#include "lualib.h" -#include "lauxlib.h" +#include #include "lfs.h" diff --git a/luaclib/src/lhttpparser/Makefile b/luaclib/src/lhttpparser/Makefile index 9bfbd037..fae37532 100644 --- a/luaclib/src/lhttpparser/Makefile +++ b/luaclib/src/lhttpparser/Makefile @@ -13,7 +13,7 @@ CC = cc # CFLAGS = -O3 -Wall -shared -fPIC -msse4 INCLUDES += -I../../../src -I/usr/local/include -LIBS = -L../ -L../../../ -L/usr/local/lib +LIBS = -L../ -L../../ -L../../../ -L/usr/local/lib CFLAGS = -O3 -Wall -shared -fPIC DLL = -lcore -llua diff --git a/luaclib/src/lhttpparser/lhttpparser.c b/luaclib/src/lhttpparser/lhttpparser.c index 4d507c88..206f9db7 100644 --- a/luaclib/src/lhttpparser/lhttpparser.c +++ b/luaclib/src/lhttpparser/lhttpparser.c @@ -1,6 +1,6 @@ #define LUA_LIB -#include "../../../src/core.h" +#include #include "httpparser.h" #define MAX_HEADER (128) diff --git a/luaclib/src/lmsgpack/Makefile b/luaclib/src/lmsgpack/Makefile index 84fa7753..e43f2785 100644 --- a/luaclib/src/lmsgpack/Makefile +++ b/luaclib/src/lmsgpack/Makefile @@ -10,7 +10,7 @@ default : CC = cc INCLUDES += -I../../../src -I/usr/local/include -LIBS = -L../ -L../../../ -L/usr/local/lib +LIBS = -L../ -L../../ -L../../../ -L/usr/local/lib CFLAGS = -O3 -Wall -shared -fPIC DLL = -lcore -llua diff --git a/luaclib/src/lmsgpack/lmsgpack.c b/luaclib/src/lmsgpack/lmsgpack.c index 77db1c25..d40db7ef 100644 --- a/luaclib/src/lmsgpack/lmsgpack.c +++ b/luaclib/src/lmsgpack/lmsgpack.c @@ -1,6 +1,6 @@ #define LUA_LIB -#include "../../../src/core.h" +#include // #include // #include diff --git a/luaclib/src/lpeg/lpcap.c b/luaclib/src/lpeg/lpcap.c index b332fde4..3202a1d7 100644 --- a/luaclib/src/lpeg/lpcap.c +++ b/luaclib/src/lpeg/lpcap.c @@ -3,8 +3,7 @@ ** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license) */ -#include "lua.h" -#include "lauxlib.h" +#include #include "lpcap.h" #include "lptypes.h" diff --git a/luaclib/src/lpeg/lpcode.c b/luaclib/src/lpeg/lpcode.c index 39234597..29125b8f 100644 --- a/luaclib/src/lpeg/lpcode.c +++ b/luaclib/src/lpeg/lpcode.c @@ -3,11 +3,7 @@ ** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license) */ -#include - - -#include "lua.h" -#include "lauxlib.h" +#include #include "lptypes.h" #include "lpcode.h" diff --git a/luaclib/src/lpeg/lpcode.h b/luaclib/src/lpeg/lpcode.h index 34ee2763..f4f98a0a 100644 --- a/luaclib/src/lpeg/lpcode.h +++ b/luaclib/src/lpeg/lpcode.h @@ -5,7 +5,7 @@ #if !defined(lpcode_h) #define lpcode_h -#include "lua.h" +#include #include "lptypes.h" #include "lptree.h" diff --git a/luaclib/src/lpeg/lpprint.c b/luaclib/src/lpeg/lpprint.c index df62cbee..ce48672a 100644 --- a/luaclib/src/lpeg/lpprint.c +++ b/luaclib/src/lpeg/lpprint.c @@ -4,8 +4,8 @@ */ #include -#include -#include + +#include #include "lptypes.h" diff --git a/luaclib/src/lpeg/lptree.c b/luaclib/src/lpeg/lptree.c index 5c8de947..7cd8c546 100644 --- a/luaclib/src/lpeg/lptree.c +++ b/luaclib/src/lpeg/lptree.c @@ -4,12 +4,8 @@ */ #include -#include -#include - -#include "lua.h" -#include "lauxlib.h" +#include #include "lptypes.h" #include "lpcap.h" diff --git a/luaclib/src/lpeg/lpvm.c b/luaclib/src/lpeg/lpvm.c index 737418c4..ef8c8225 100644 --- a/luaclib/src/lpeg/lpvm.c +++ b/luaclib/src/lpeg/lpvm.c @@ -3,12 +3,7 @@ ** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license) */ -#include -#include - - -#include "lua.h" -#include "lauxlib.h" +#include #include "lpcap.h" #include "lptypes.h" diff --git a/luaclib/src/lpeg/makefile b/luaclib/src/lpeg/makefile index 391488dc..2ce6ca3b 100644 --- a/luaclib/src/lpeg/makefile +++ b/luaclib/src/lpeg/makefile @@ -9,7 +9,7 @@ default : CC = cc INCLUDES += -I../../../src -I/usr/local/include -LIBS = -L../ -L../../../ -L/usr/local/lib +LIBS = -L../ -L../../ -L../../../ -L/usr/local/lib CFLAGS = -O3 -shared -fPIC DLL = -lcore -llua diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 33593f29..cbe77aee 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -48,7 +48,12 @@ static inline void SETSOCKETOPT(int sockfd, int mode){ /* 端口重用 */ #ifdef SO_REUSEPORT if (mode == SERVER) { + #ifdef SO_REUSEPORT_LB + // BSD系统的多进程负载需要使用此宏 + ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT_LB, &Enable, sizeof(Enable)); + #else ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &Enable, sizeof(Enable)); + #endif if (ret < 0) { LOG("ERROR", "Setting SO_REUSEPORT failed."); LOG("ERROR", strerror(errno)); @@ -381,8 +386,7 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ if (revents & EV_WRITE){ errno = 0; struct io_sendfile *sf = core_get_watcher_userdata(io); - -#ifdef EV_USE_KQUEUE +#if defined(EV_USE_KQUEUE) int tag = 0; off_t nBytes = 0; for (;;) { #if defined(__APPLE__) @@ -400,9 +404,7 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ // 当nBytes与tag同时为0时说明发送成功, 其它情况下都当做发送失败. if (0 == nBytes){ lua_pushboolean(sf->L, 1); break; } } -#endif - -#ifdef EV_USE_EPOLL +#elif defined(EV_USE_EPOLL) #include for (;;) { int tag = sendfile(io->fd, sf->fd, NULL, sf->offset); @@ -414,9 +416,7 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ break; } } -#endif - -#ifdef __MSYS__ +#else char buf[sf->offset]; for(;;) { int rBytes = pread(sf->fd, buf, sf->offset, sf->pos); diff --git a/luaclib/src/lz/Makefile b/luaclib/src/lz/Makefile index f9b57d62..740a80ac 100644 --- a/luaclib/src/lz/Makefile +++ b/luaclib/src/lz/Makefile @@ -10,7 +10,7 @@ default : CC = cc INCLUDES += -I../../../src -I/usr/local/include -LIBS = -L../ -L../../../ -L/usr/local/lib +LIBS = -L../ -L../../ -L../../../ -L/usr/local/lib CFLAGS = -O3 -Wall -shared -fPIC DLL = -lcore -llua -lz diff --git a/luaclib/src/lz/lzlib.c b/luaclib/src/lz/lzlib.c index 65367eb6..4c62abe4 100644 --- a/luaclib/src/lz/lzlib.c +++ b/luaclib/src/lz/lzlib.c @@ -1,6 +1,6 @@ #define LUA_LIB -#include "../../../src/core.h" +#include #include #define MAX_COMPRESS_BUF_SIZE_TIMES (1 << 10) From 2bfe008b2b3998343760c31d2b607b47d96de69f Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 24 Dec 2020 21:41:42 +0800 Subject: [PATCH 673/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=9D=99=E6=80=81?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E4=B8=80=E4=BA=9B=E5=A4=B4=E9=83=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 10 +++--- lualib/protocol/http/mime.lua | 67 ++++++++++++++++++----------------- 2 files changed, 40 insertions(+), 37 deletions(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index 3480875d..578672d3 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -500,18 +500,18 @@ function HTTP_PROTOCOL.DISPATCH(sock, opt, http) sock:send(ERROR_RESPONSE(http, statucode, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, req_time(start))) goto CONTINUE end - print(filepath) statucode = 200 header[#header+1] = REQUEST_STATUCODE_RESPONSE(statucode) local conten_type = REQUEST_MIME_RESPONSE(lower(file_type or '')) - if not conten_type then + if type(conten_type) ~= 'string' then -- 确保浏览器提示需要下载 - header[#header+1] = fmt('Content-Disposition: attachment; filename="%s"', filepath) - static = 'Content-Type: application/octet-stream' + local s, e = find(filepath, "/[^/]+$") + header[#header+1] = fmt('Content-Disposition: attachment; filename="%s"', filepath:sub(s + 1, e)) + static = fmt('Content-Type: %s', type(conten_type) ~= "table" and "application/octet-stream" or conten_type.type) else -- 确保内容展示在浏览器内 header[#header+1] = 'Content-Disposition: inline' - static = 'Content-Type: ' .. conten_type .. '; charset=utf-8' + static = fmt('Content-Type: %s; charset=utf-8', conten_type) end end header[#header+1] = HTTP_DATE() diff --git a/lualib/protocol/http/mime.lua b/lualib/protocol/http/mime.lua index 9e1ef9fd..01ffb440 100644 --- a/lualib/protocol/http/mime.lua +++ b/lualib/protocol/http/mime.lua @@ -4,11 +4,16 @@ return { ['htm'] = 'text/html', ['html'] = 'text/html', ['shtml'] = 'text/html', + ['mml'] = 'text/mathml', ['xhtml'] = 'application/xhtml+xml', ['txt'] = 'text/plain', + ['htc'] = 'text/x-component', + ['wml'] = 'text/vnd.wap.wml', ['css'] = 'text/css', + ['rss'] = 'application/rss+xml', ['json'] = 'application/json', ['rtf'] = 'application/rtf', + ['atom'] = 'application/atom+xml', ['ogx'] = 'application/ogg', -- 图片格式 ['bmp'] = 'image/bmp', @@ -27,32 +32,30 @@ return { ['mp3'] = 'audio/mpeg', ['oga'] = 'audio/ogg', -- 视频 - ['avi'] = 'video/x-msvideo', - ['mpa'] = 'video/mpeg', - ['mpe'] = 'video/mpeg', - ['mp2'] = 'video/mpeg', - ['mpeg'] = 'video/mpeg', - ['mp4'] = 'audio/mp4', - ['qt'] = 'video/mpeg', - ['mov'] = 'video/mpeg', - ['webm'] = 'video/webm', - ['flv'] = 'video/x-flv', - ['m4v'] = 'video/x-m4v', - ['3gp'] = 'video/3gpp', - ['3gpp'] = 'video/3gpp', - ['ts'] = 'video/mp2t', - ['ogv'] = 'video/ogg', + ['avi'] = { type = 'video/x-msvideo' }, + ['mpa'] = { type = 'video/mpeg' }, + ['mpe'] = { type = 'video/mpeg'}, + ['mp2'] = { type = 'video/mpeg' }, + ['mpeg'] = { type = 'video/mpeg' }, + ['mp4'] = { type = 'audio/mp4' }, + ['qt'] = { type = 'video/mpeg' }, + ['mov'] = { type = 'video/mpeg' }, + ['webm'] = { type = 'video/webm' }, + ['flv'] = { type = 'video/x-flv' }, + ['m4v'] = { type = 'video/x-m4v' }, + ['ts'] = { type = 'video/mp2t' }, + ['ogv'] = { type = 'video/ogg' }, -- 文档 - ['csv'] = 'text/csv', - ['dot'] = 'application/msword', - ['doc'] = 'application/msword', - ['mdb'] = 'application/x-msaccess', - ['xls'] = 'application/vnd.ms-excel', - ['ppt'] = 'application/vnd.ms-powerpoint', - ['chm'] = 'application/vnd.ms-htmlhelp', - ['xlsx'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - ['docx'] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - ['pptx'] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + ['csv'] = { type = 'text/csv'}, + ['dot'] = { type = 'application/msword' }, + ['doc'] = { type = 'application/msword' }, + ['mdb'] = { type = 'application/x-msaccess' }, + ['xls'] = { type = 'application/vnd.ms-excel' }, + ['ppt'] = { type = 'application/vnd.ms-powerpoint' }, + ['chm'] = { type = 'application/vnd.ms-htmlhelp' }, + ['xlsx'] = { type = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }, + ['docx'] = { type = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' }, + ['pptx'] = { type = 'application/vnd.openxmlformats-officedocument.presentationml.presentation' }, ['pdf'] = 'application/pdf', -- 密匙 ['der'] = 'application/x-x509-ca-cert', @@ -69,13 +72,13 @@ return { ['p7r'] = 'application/x-pkcs7-certreqresp', ['p7s'] = 'application/x-pkcs7-signature', -- 压缩文件 - ['7z'] = 'application/x-7z-compressed', - ['zip'] = 'application/zip', - ['gz'] = 'application/x-gzip', - ['tar'] = 'application/x-tar', - ['gtar'] = 'application/x-gtar', - ['tgz'] = 'application/x-compressed', - ['rar'] = 'application/x-rar-compressed', + ['7z'] = { type = 'application/x-7z-compressed' }, + ['zip'] = { type = 'application/zip' }, + ['gz'] = { type = 'application/x-gzip' }, + ['tar'] = { type = 'application/x-tar' }, + ['gtar'] = { type = 'application/x-gtar' }, + ['tgz'] = { type = 'application/x-compressed' }, + ['rar'] = { type = 'application/x-rar-compressed' }, -- 源文件 ['c'] = 'text/plain', ['cxx'] = 'text/plain', From 8dd1b2219c4d0ec60707bd1e1ec6300393bdba2f Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 24 Dec 2020 22:02:38 +0800 Subject: [PATCH 674/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=8F=AF=E8=83=BD?= =?UTF-8?q?=E5=87=BA=E7=8E=B0=E7=9A=84=E9=94=99=E8=AF=AF=E5=90=8E=E7=BC=80?= =?UTF-8?q?=E5=90=8D=E4=B8=8E=E4=B8=8B=E8=BD=BD=E5=A4=B4=E9=83=A8=E4=B8=8D?= =?UTF-8?q?=E4=B8=80=E8=87=B4=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Router.lua | 2 +- lualib/protocol/http/init.lua | 6 +++--- lualib/protocol/http/mime.lua | 5 +++++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index 6e6d04eb..d4f18857 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -204,7 +204,7 @@ function Router:find (method, path) if type(stat) ~= 'table' or stat.mode ~= 'file' then return end - return stat.size, filepath, match(path, '.+%.([%a]+)') + return stat.size, filepath, match(filepath, '%.([^.]+)$') end end return self.load_file, typ diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index 578672d3..886494fc 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -488,13 +488,13 @@ function HTTP_PROTOCOL.DISPATCH(sock, opt, http) end return sock:close() else - local file_type + local filetype local path = PATH local pos, _ = find(PATH, '%?') if pos then path = split(PATH, 1, pos - 1) end - body_len, filepath, file_type = cls(path) + body_len, filepath, filetype = cls(path) if not body_len then statucode = 404 sock:send(ERROR_RESPONSE(http, statucode, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, req_time(start))) @@ -502,7 +502,7 @@ function HTTP_PROTOCOL.DISPATCH(sock, opt, http) end statucode = 200 header[#header+1] = REQUEST_STATUCODE_RESPONSE(statucode) - local conten_type = REQUEST_MIME_RESPONSE(lower(file_type or '')) + local conten_type = REQUEST_MIME_RESPONSE(lower(filetype or '')) if type(conten_type) ~= 'string' then -- 确保浏览器提示需要下载 local s, e = find(filepath, "/[^/]+$") diff --git a/lualib/protocol/http/mime.lua b/lualib/protocol/http/mime.lua index 01ffb440..d4af15fe 100644 --- a/lualib/protocol/http/mime.lua +++ b/lualib/protocol/http/mime.lua @@ -31,6 +31,11 @@ return { ['acc'] = 'audio/aac', ['mp3'] = 'audio/mpeg', ['oga'] = 'audio/ogg', + ['kar'] = 'audio/midi', + ['mid'] = 'audio/midi', + ['midi'] = 'audio/midi', + ['m4a'] = 'audio/x-m4a', + ['ra'] = 'audio/x-realaudio', -- 视频 ['avi'] = { type = 'video/x-msvideo' }, ['mpa'] = { type = 'video/mpeg' }, From 54e4990e24413c1262168b74c80f2a5374c9440d Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 27 Dec 2020 19:40:43 +0800 Subject: [PATCH 675/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/laio.c | 4 +- luaclib/src/ltask.c | 34 +++----- luaclib/src/ltcp.c | 188 ++++++++++++++++--------------------------- luaclib/src/ltimer.c | 53 +++++------- luaclib/src/ludp.c | 138 ++++++++++--------------------- 5 files changed, 145 insertions(+), 272 deletions(-) diff --git a/luaclib/src/laio.c b/luaclib/src/laio.c index b5f5ccb1..80cb200e 100644 --- a/luaclib/src/laio.c +++ b/luaclib/src/laio.c @@ -119,7 +119,7 @@ static inline const char *mode2string (mode_t mode) { else if ( S_ISLNK(mode) ) return "link"; else if ( S_ISSOCK(mode) ) - return "socket"; + return "socket"; else if ( S_ISFIFO(mode) ) return "named pipe"; else if ( S_ISCHR(mode) ) @@ -392,9 +392,7 @@ static int aio_init() { eio_set_min_parallel(AIO_MAX_NTHREADS); eio_set_max_parallel(AIO_MAX_NTHREADS); eio_set_max_idle(AIO_MAX_NTHREADS); - return 0; - } /* aio.open 打开一个文件(不存在则创建) */ diff --git a/luaclib/src/ltask.c b/luaclib/src/ltask.c index c6143bfe..e80289bc 100644 --- a/luaclib/src/ltask.c +++ b/luaclib/src/ltask.c @@ -2,8 +2,7 @@ #include -static void -TASK_CB(CORE_P_ core_task *task, int revents){ +static void TASK_CB(CORE_P_ core_task *task, int revents){ lua_State *co = (lua_State *) core_get_watcher_userdata(task); if (co && (lua_status(co) == LUA_YIELD || lua_status(co) == LUA_OK)){ int status = CO_RESUME(co, NULL, lua_status(co) == LUA_YIELD ? lua_gettop(co) : lua_gettop(co) - 1); @@ -14,43 +13,34 @@ TASK_CB(CORE_P_ core_task *task, int revents){ } } -static int -task_new(lua_State *L){ +static int task_new(lua_State *L){ core_task *task = lua_newuserdata(L, sizeof(core_task)); - if (!task) return 0; - + if (!task) + return 0; core_task_init(task, TASK_CB); - luaL_setmetatable(L, "__Task__"); - return 1; } -static int -task_start(lua_State *L){ +static int task_start(lua_State *L){ core_task *task = (core_task *) luaL_testudata(L, 1, "__Task__"); - if (!task) return luaL_error(L, "attemp to pass a invaild core_task value."); - + if (!task) + return luaL_error(L, "attemp to pass a invaild core_task value."); lua_State *co = lua_tothread(L, 2); - if (!co) return luaL_error(L, "attemp to pass a invaild lua_State value."); - + if (!co) + return luaL_error(L, "attemp to pass a invaild lua_State value."); /* 这里假设栈大小永远够用, 因为调用与回调都不需要传入那么多参数 */ lua_xmove(L, co, lua_gettop(L) - 2); - core_set_watcher_userdata(task, co); - core_task_start(CORE_LOOP_ task); - return 1; } -static int -task_stop(lua_State *L){ +static int task_stop(lua_State *L){ core_task *task = (core_task *) luaL_testudata(L, 1, "__Task__"); - if (!task) return luaL_error(L, "attemp to pass a invaild core_task value."); - + if (!task) + return luaL_error(L, "attemp to pass a invaild core_task value."); core_task_stop(CORE_LOOP_ task); - return 0; } diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index cbe77aee..01425d66 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -27,14 +27,10 @@ static inline const char * fast_cmp(const char* src, size_t src_len, const char* } static inline void SETSOCKETOPT(int sockfd, int mode){ - int Enable = 1; - int ret = 0; - /* 设置非阻塞 */ non_blocking(sockfd); - /* 地址重用 */ #ifdef SO_REUSEADDR ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &Enable, sizeof(Enable)); @@ -265,12 +261,10 @@ static int create_server_unixsock(const char* path, size_t path_len, int backlog } static void TCP_IO_CB(CORE_P_ core_io *io, int revents) { - if (revents & EV_ERROR) { LOG("ERROR", "Recevied a core_io object internal error from libev."); return ; } - lua_State *co = (lua_State *)core_get_watcher_userdata(io); if (lua_status(co) == LUA_YIELD || lua_status(co) == LUA_OK){ int status = CO_RESUME(co, NULL, 0); @@ -282,12 +276,10 @@ static void TCP_IO_CB(CORE_P_ core_io *io, int revents) { } static void IO_CONNECT(CORE_P_ core_io *io, int revents){ - if (revents & EV_ERROR) { LOG("ERROR", "Recevied a core_io object internal error from libev."); return ; } - if (revents & EV_WRITE){ lua_State *co = (lua_State *)core_get_watcher_userdata(io); if (lua_status(co) == LUA_YIELD || lua_status(co) == LUA_OK){ @@ -302,18 +294,16 @@ static void IO_CONNECT(CORE_P_ core_io *io, int revents){ } } } - } /* 接受链接 */ static void IO_ACCEPT(CORE_P_ core_io *io, int revents){ - if (revents & EV_READ){ lua_State *co = (lua_State *) core_get_watcher_userdata(io); int status = lua_status(co); if (status != LUA_YIELD && status != LUA_OK) { LOG("ERROR", "accept get a invalid lua vm."); - return ; + return; } for(;;) { errno = 0; @@ -324,7 +314,7 @@ static void IO_ACCEPT(CORE_P_ core_io *io, int revents){ if (0 >= client) { if (errno != EWOULDBLOCK) LOG("ERROR", strerror(errno)); - return ; + return; } SETSOCKETOPT(client, None); char buf[INET6_ADDRSTRLEN]; @@ -515,21 +505,20 @@ static int tcp_readline(lua_State *L) { } static int tcp_sslreadline(lua_State *L){ - - errno = 0; - SSL *ssl = lua_touserdata(L, 1); - if (!ssl) return 0; + if (!ssl) + return 0; size_t sp_len = 0; const char * sp = luaL_checklstring(L, 2, &sp_len); - if (sp_len < 1 && !sp) return 0; + if (sp_len < 1 && !sp) + return 0; int no_sp = lua_toboolean(L, 3); size_t tmp_size = 1024; char *tmp_buf = NULL; - + errno = 0; while (1) { lua_settop(L, 2); tmp_buf = lua_newuserdata(L, tmp_size); @@ -567,20 +556,19 @@ static int tcp_sslreadline(lua_State *L){ } static int tcp_read(lua_State *L){ - - errno = 0; - int fd = lua_tointeger(L, 1); - if (0 >= fd) return 0; + if (0 >= fd) + return 0; lua_Integer bytes = lua_tointeger(L, 2); - if (0 >= bytes) return 0; + if (0 >= bytes) + return 0; + errno = 0; char* str = alloca(1 << 16); if (bytes > (1 << 16)){ str = (char*)lua_newuserdata(L, bytes); } - do { int rsize = read(fd, str, bytes); if (rsize > 0) { @@ -597,25 +585,24 @@ static int tcp_read(lua_State *L){ } } } while(0); - return 0; } static int tcp_sslread(lua_State *L){ - - errno = 0; - SSL *ssl = lua_touserdata(L, 1); - if (!ssl) return 0; + if (!ssl) + return 0; lua_Integer bytes = lua_tointeger(L, 2); - if (0 >= bytes) return 0; + if (0 >= bytes) + return 0; char* str = alloca(1 << 16); if (bytes > (1 << 16)){ str = (char*)lua_newuserdata(L, bytes); } + errno = 0; do { int rsize = SSL_read(ssl, str, bytes); if (0 < rsize) { @@ -632,24 +619,18 @@ static int tcp_sslread(lua_State *L){ } } } while (0); - return 0; } static int tcp_write(lua_State *L){ - - errno = 0; - size_t resp_len = 0; - int fd = lua_tointeger(L, 1); - const char *response = luaL_checklstring(L, 2, &resp_len); if (!response) return luaL_error(L, "tcp_write ERROR: attempt to write an empty string."); + errno = 0; int offset = lua_tointeger(L, 3); - do { int wsize = write(fd, response + offset, resp_len - offset); @@ -667,7 +648,6 @@ static int tcp_write(lua_State *L){ } static int tcp_sslwrite(lua_State *L){ - SSL *ssl = lua_touserdata(L, 1); if (!ssl) return 0; @@ -676,10 +656,8 @@ static int tcp_sslwrite(lua_State *L){ if (!response) return luaL_error(L, "tcp_sslwrite ERROR: attempt to write an empty string."); - int resp_len = lua_tointeger(L, 3); - errno = 0; - + int resp_len = lua_tointeger(L, 3); do { int wsize = SSL_write(ssl, response, resp_len); if (wsize > 0) { lua_pushinteger(L, wsize); return 1; } @@ -694,33 +672,37 @@ static int tcp_sslwrite(lua_State *L){ static int new_server_fd(lua_State *L){ const char *ip = lua_tostring(L, 1); - if(!ip) return 0; + if (!ip) + return 0; int port = lua_tointeger(L, 2); - if(!port) return 0; + if (!port) + return 0; int backlog = lua_tointeger(L, 3); int fd = create_server_fd(port, 0 >= backlog ? 128 : backlog); - if (0 >= fd) return 0; + if (0 >= fd) + return 0; lua_pushinteger(L, fd); - return 1; } static int new_client_fd(lua_State *L){ const char *ip = lua_tostring(L, 1); - if(!ip) return 0; + if (!ip) + return 0; int port = lua_tointeger(L, 2); - if(!port) return 0; + if (!port) + return 0; int fd = create_client_fd(ip, port); - if (0 >= fd) return 0; + if (0 >= fd) + return 0; lua_pushinteger(L, fd); - return 1; } @@ -751,72 +733,70 @@ static int new_unixsock_fd(lua_State *L) { static int tcp_listen(lua_State *L){ core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); - if(!io) return 0; + if (!io) + return 0; /* socket文件描述符 */ int fd = lua_tointeger(L, 2); - if (0 >= fd) return 0; + if (0 >= fd) + return 0; /* 回调协程 */ lua_State *co = lua_tothread(L, 3); - if (!co) return 0; + if (!co) + return 0; core_set_watcher_userdata(io, co); - core_io_init(io, IO_ACCEPT, fd, EV_READ); - core_io_start(CORE_LOOP_ io); - return 1; } static int tcp_listen_ex(lua_State *L) { core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); - if (!io) return 0; + if (!io) + return 0; /* socket文件描述符 */ int fd = lua_tointeger(L, 2); - if (0 >= fd) return 0; + if (0 >= fd) + return 0; /* 回调协程 */ lua_State *co = lua_tothread(L, 3); - if (!co) return 0; + if (!co) + return 0; core_set_watcher_userdata(io, co); - core_io_init(io, IO_ACCEPT_EX, fd, EV_READ); - core_io_start(CORE_LOOP_ io); - return 1; } static int tcp_connect(lua_State *L){ - core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); if(!io) return 0; /* socket文件描述符 */ int fd = lua_tointeger(L, 2); - if (0 >= fd) return 0; + if (0 >= fd) + return 0; /* 回调协程 */ lua_State *co = lua_tothread(L, 3); - if (!co) return 0; + if (!co) + return 0; core_set_watcher_userdata(io, co); - core_io_init(io, IO_CONNECT, fd, EV_READ | EV_WRITE); - core_io_start(CORE_LOOP_ io); - return 0; - } static int tcp_sslconnect(lua_State *L){ SSL *ssl = (SSL*) lua_touserdata(L, 1); - if (!ssl) return 0; + if (!ssl) + return 0; int reason = SSL_get_error(ssl, SSL_do_handshake(ssl)); /* 握手结束 -> 握手成功 */ @@ -832,34 +812,32 @@ static int tcp_sslconnect(lua_State *L){ } /* 握手失败 */ return 0; - } static int tcp_start(lua_State *L){ - core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); - if(!io) return 0; + if(!io) + return 0; /* socket文件描述符 */ int fd = lua_tointeger(L, 2); - if (0 >= fd) return 0; + if (0 >= fd) + return 0; /* 监听事件 */ int events = lua_tointeger(L, 3); - if (0 >= events || events > 3) return 0; + if (0 >= events || events > 3) + return 0; /* 回调协程 */ lua_State *co = lua_tothread(L, 4); - if (!co) return 0; + if (!co) + return 0; core_set_watcher_userdata(io, co); - core_io_init(io, TCP_IO_CB, fd, events); - core_io_start(CORE_LOOP_ io); - return 0; - } static int ssl_set_connect_mode(lua_State *L) { @@ -892,7 +870,6 @@ static int ssl_set_accept_mode(lua_State *L) { // 加载证书 static int ssl_set_certificate(lua_State *L) { - SSL *ssl = (SSL*) lua_touserdata(L, 1); if (!ssl) return luaL_error(L, "Invalid SSL ssl."); @@ -918,7 +895,6 @@ static int ssl_set_certificate(lua_State *L) { // 加载私钥 static int ssl_set_privatekey(lua_State *L) { - SSL *ssl = (SSL*) lua_touserdata(L, 1); if (!ssl) return luaL_error(L, "Invalid SSL ssl."); @@ -944,7 +920,6 @@ static int ssl_set_privatekey(lua_State *L) { // 如果私钥有安装密钥, 则再这里设置 static int ssl_set_userdata_key(lua_State *L) { - SSL *ssl = (SSL*) lua_touserdata(L, 1); if (!ssl) return luaL_error(L, "Invalid SSL ssl."); @@ -960,13 +935,11 @@ static int ssl_set_userdata_key(lua_State *L) { // SSL_set_default_passwd_cb_userdata(ssl, (void*)password); SSL_CTX_set_default_passwd_cb_userdata(ctx, (void*)password); - return 1; } // 验证证书与私钥是否有效 static int ssl_verify(lua_State *L) { - SSL *ssl = (SSL*) lua_touserdata(L, 1); if (!ssl) return luaL_error(L, "Invalid SSL ssl."); @@ -984,7 +957,6 @@ static int ssl_verify(lua_State *L) { } static int ssl_set_alpn(lua_State *L) { - SSL *ssl = (SSL*) lua_touserdata(L, 1); if (!ssl) return luaL_error(L, "Invalid SSL ssl."); @@ -1027,17 +999,16 @@ static int ssl_get_alpn(lua_State *L) { } static int ssl_new(lua_State *L){ - SSL_CTX *ssl_ctx = SSL_CTX_new(SSLv23_method()); - if (!ssl_ctx) return 0; + if (!ssl_ctx) + return 0; SSL *ssl = SSL_new(ssl_ctx); - if (!ssl) return 0; + if (!ssl) + return 0; lua_pushlightuserdata(L, (void*) ssl); - lua_pushlightuserdata(L, (void*) ssl_ctx); - return 2; } @@ -1050,9 +1021,7 @@ static int ssl_set_fd(lua_State *L) { int fd = lua_tointeger(L, 2); SSL_set_fd(ssl, fd); - SSL_set_connect_state(ssl); - return 1; } @@ -1061,67 +1030,52 @@ static int ssl_new_fd(lua_State *L){ int fd = lua_tointeger(L, 1); SSL_CTX *ssl_ctx = SSL_CTX_new(SSLv23_method()); - if (!ssl_ctx) return 0; + if (!ssl_ctx) + return 0; SSL *ssl = SSL_new(ssl_ctx); - if (!ssl) return 0; + if (!ssl) + return 0; SSL_set_fd(ssl, fd); - SSL_set_connect_state(ssl); - lua_pushlightuserdata(L, (void*) ssl); - lua_pushlightuserdata(L, (void*) ssl_ctx); - return 2; } static int ssl_free(lua_State *L){ - SSL *ssl = (SSL*) lua_touserdata(L, 1); if (ssl) SSL_free(ssl); // 销毁基于ctx的ssl对象; - SSL_CTX *ssl_ctx = (SSL_CTX*) lua_touserdata(L, 2); if (ssl_ctx) SSL_CTX_free(ssl_ctx); // 销毁ctx上下文; - return 0; } static int tcp_new(lua_State *L){ - core_io *io = (core_io *) lua_newuserdata(L, sizeof(core_io)); - - if(!io) return 0; - + if(!io) + return 0; luaL_setmetatable(L, "__TCP__"); - return 1; - } static int tcp_stop(lua_State *L){ - core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); - if(!io) return 0; - + if(!io) + return 0; core_io_stop(CORE_LOOP_ io); - return 0; - } static int tcp_close(lua_State *L){ - int fd = lua_tointeger(L, 1); - - if (fd && fd > 0) close(fd); - + if (fd && fd > 0) + close(fd); return 0; - } LUAMOD_API int diff --git a/luaclib/src/ltimer.c b/luaclib/src/ltimer.c index 5b59fd6b..25bc2f27 100644 --- a/luaclib/src/ltimer.c +++ b/luaclib/src/ltimer.c @@ -5,66 +5,49 @@ /* === 定时器 === */ static void TIMEOUT_CB(CORE_P_ core_timer *timer, int revents){ - - if (revents & EV_TIMER){ - - lua_State *co = (lua_State *) core_get_watcher_userdata(timer); - - int status = CO_RESUME(co, NULL, lua_gettop(co) > 0 ? lua_gettop(co) - 1 : 0); - - if (status != LUA_OK && status != LUA_YIELD){ - - LOG( "ERROR", lua_tostring(co, -1)); - - core_timer_stop(CORE_LOOP_ timer); - - } - } + if (revents & EV_TIMER){ + lua_State *co = (lua_State *) core_get_watcher_userdata(timer); + int status = CO_RESUME(co, NULL, lua_gettop(co) > 0 ? lua_gettop(co) - 1 : 0); + if (status != LUA_OK && status != LUA_YIELD){ + LOG( "ERROR", lua_tostring(co, -1)); + core_timer_stop(CORE_LOOP_ timer); + } + } } static int timer_stop(lua_State *L){ - core_timer *timer = (core_timer *) luaL_testudata(L, 1, "__TIMER__"); - if(!timer) return 0; - + if(!timer) + return 0; core_timer_stop(CORE_LOOP_ timer); - return 0; } static int timer_start(lua_State *L){ - core_timer *timer = (core_timer *) luaL_testudata(L, 1, "__TIMER__"); - if(!timer) return 0; - + if(!timer) + return 0; lua_Number timeout = luaL_checknumber(L, 2); - if (timeout <= 0 ) return 0; - + if (timeout <= 0 ) + return 0; lua_State *co = lua_tothread(L, 3); - if(!co) return 0; - + if(!co) + return 0; core_set_watcher_userdata(timer, (void*)co); - core_timer_start(CORE_LOOP, timer, timeout); - return 0; - } static int timer_new(lua_State *L){ - core_timer *timer = (core_timer *) lua_newuserdata(L, sizeof(core_timer)); - if(!timer) return 0; - + if(!timer) + return 0; core_timer_init(timer, TIMEOUT_CB); - luaL_setmetatable(L, "__TIMER__"); - return 1; - } LUAMOD_API int diff --git a/luaclib/src/ludp.c b/luaclib/src/ludp.c index 37873ec9..4c47abcd 100644 --- a/luaclib/src/ludp.c +++ b/luaclib/src/ludp.c @@ -2,12 +2,9 @@ #include -static inline -void SETSOCKETOPT(int sockfd) { +static inline void SETSOCKETOPT(int sockfd) { int Enable = 1; - int ret = 0; - /* 设置非阻塞 */ non_blocking(sockfd); @@ -41,18 +38,15 @@ void SETSOCKETOPT(int sockfd) { } -static int -udp_socket_new(const char *ipaddr, int port){ +static int udp_socket_new(const char *ipaddr, int port){ errno = 0; /* 建立 UDP Socket */ int sockfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); - if (0 >= sockfd) return -1; - + if (0 >= sockfd) + return -1; SETSOCKETOPT(sockfd); - struct sockaddr_in6 SA; memset(&SA, 0x0, sizeof(SA)); - SA.sin6_family = AF_INET6; SA.sin6_port = htons(port); int error = inet_pton(AF_INET6, ipaddr, &SA.sin6_addr); @@ -61,7 +55,6 @@ udp_socket_new(const char *ipaddr, int port){ close(sockfd); return -1; } - int ret = connect(sockfd, (struct sockaddr*)&SA, sizeof(SA)); if (ret == -1) { LOG("ERROR", strerror(errno)); @@ -71,16 +64,12 @@ udp_socket_new(const char *ipaddr, int port){ return sockfd; } -static void -UDP_IO_CB(CORE_P_ core_io *io, int revents){ - +static void UDP_IO_CB(CORE_P_ core_io *io, int revents){ int status = 0; - if (revents & EV_ERROR) { LOG("ERROR", "Recevied a core_io object internal error from libev."); return ; } - if (revents & EV_READ){ lua_State *co = (lua_State *)core_get_watcher_userdata(io); if (lua_status(co) == LUA_YIELD || lua_status(co) == LUA_OK){ @@ -92,138 +81,97 @@ UDP_IO_CB(CORE_P_ core_io *io, int revents){ } } -static int -udp_send(lua_State *L){ - +static int udp_send(lua_State *L){ int fd = lua_tointeger(L, 1); - if (fd < 0) return 0; - + if (fd < 0) + return 0; const char* data = lua_tostring(L, 2); - if (!data) return 0; - + if (!data) + return 0; size_t len = lua_tointeger(L, 3); - int wsize = write(fd, data, len); - lua_pushinteger(L, wsize); - return 1; - } -static int -udp_recv(lua_State *L){ - +static int udp_recv(lua_State *L){ int fd = lua_tointeger(L, 1); - if (fd < 0) return 0; - + if (fd < 0) + return 0; char str[4096] = {0}; - int rsize = read(fd, str, 4096); - - if (rsize < 0) return 0; - + if (rsize < 0) + return 0; lua_pushlstring(L, str, rsize); - lua_pushinteger(L, rsize); - return 2; - } -static int -udp_connect(lua_State *L){ - +static int udp_connect(lua_State *L){ const char *ip = lua_tostring(L, 1); - if(!ip) return 0; - + if(!ip) + return 0; int port = lua_tointeger(L, 2); - if(!port) return 0; - + if(!port) + return 0; int fd = udp_socket_new(ip, port); - - if (0 >= fd) return 0; - + if (0 >= fd) + return 0; lua_pushinteger(L, fd > 0 ? fd : -1); - return 1; - } -static int -udp_start(lua_State *L){ - +static int udp_start(lua_State *L){ core_io *io = (core_io *) luaL_testudata(L, 1, "__UDP__"); if(!io) return 0; - int fd = lua_tointeger(L, 2); - if (fd < 0) return 0; - + if (fd < 0) + return 0; /* 回调协程 */ lua_State *co = lua_tothread(L, 3); - if (!co) return 0; - + if (!co) + return 0; core_set_watcher_userdata(io, co); - core_io_init (io, UDP_IO_CB, fd, EV_READ); - core_io_start (CORE_LOOP_ io); - return 0; - } -static int -udp_stop(lua_State *L){ - +static int udp_stop(lua_State *L){ core_io *io = (core_io *) luaL_testudata(L, 1, "__UDP__"); - if(!io) return 0; - + if(!io) + return 0; core_io_stop(CORE_LOOP_ io); - return 0; - } -static int -udp_close(lua_State *L){ - +static int udp_close(lua_State *L){ int fd = lua_tointeger(L, 1); - - if (fd && fd > 0) close(fd); - + if (fd && fd > 0) + close(fd); return 0; - } -static int -udp_new(lua_State *L){ - +static int udp_new(lua_State *L){ core_io *io = (core_io *) lua_newuserdata(L, sizeof(core_io)); - - if(!io) return 0; - + if(!io) + return 0; luaL_setmetatable(L, "__UDP__"); - return 1; - } LUAMOD_API int luaopen_udp(lua_State *L){ - luaL_checkversion(L); - - luaL_newmetatable(L, "__UDP__"); - lua_pushstring (L, "__index"); - lua_pushvalue(L, -2); - lua_rawset(L, -3); - lua_pushliteral(L, "__mode"); - lua_pushliteral(L, "kv"); - lua_rawset(L, -3); - + luaL_newmetatable(L, "__UDP__"); + lua_pushstring (L, "__index"); + lua_pushvalue(L, -2); + lua_rawset(L, -3); + lua_pushliteral(L, "__mode"); + lua_pushliteral(L, "kv"); + lua_rawset(L, -3); luaL_Reg udp_libs[] = { {"new", udp_new}, {"close", udp_close}, From 6c29b98b1ae4ffd475154a37dc3ae0dbcec6c10d Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 27 Dec 2020 19:41:11 +0800 Subject: [PATCH 676/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=86=85=E7=BD=AE?= =?UTF-8?q?=E5=BA=93=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/dns.lua | 27 +++++++++++++-------------- lualib/protocol/redis.lua | 15 ++++++--------- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/lualib/protocol/dns.lua b/lualib/protocol/dns.lua index 7397a179..c3f2c763 100644 --- a/lualib/protocol/dns.lua +++ b/lualib/protocol/dns.lua @@ -113,7 +113,7 @@ if #dns_list < 1 then file:close() end if #dns_list < 1 then - dns_list = {"114.114.114.114", "8.8.8.8"} + dns_list = {"1.2.4.8", "210.2.4.8"} end gen_cache() end @@ -122,7 +122,7 @@ local function get_dns_client(ip_version) if #dns_list >= 1 then local udp = UDP:new():timeout(dns._timeout or 30) local ip = dns_list[random(1, #dns_list)] - local ok, v = check_ip(ip) + local _, v = check_ip(ip) if v == 4 then ip = prefix .. ip end @@ -165,7 +165,7 @@ local function pack_question(name, version) end local function unpack_header(chunk) - local tid, flags, qdcount, ancount, nscount, arcount, nbyte = unpack(">HHHHHH", chunk) + local tid, flags, qdcount, ancount, _, _, nbyte = unpack(">HHHHHH", chunk) return { tid = tid, flags = flags, qdcount = qdcount, ancount = ancount}, nbyte end @@ -232,24 +232,23 @@ local function dns_query(domain, ip_version) check_wait(domain, wlist, nil, msg) return nil, msg end - local dns_resp, len, readable + local no_response = true cf_fork(function () - local req = pack_header()..pack_question(domain, msg) local times = 1 - while 1 do - -- 每轮发送三次请求, 减少丢包几率 - local ok1, ok2, ok3 = dns_client:send(req), dns_client:send(req), dns_client:send(req) - cf_sleep(1) - if readable then - return + local req = pack_header()..pack_question(domain, msg) + while no_response do + for _ = 1, 5 do + local _ = dns_client:send(req) and dns_client:send(req) + if not no_response then + return + end + cf_sleep(0.2) end Log:WARN("第"..times.."次尝试解析["..domain.."]:") times = times + 1 end end) - dns_resp, len = dns_client:recv() - dns_client:close() - readable = true + local dns_resp, len = dns_client:recv(); dns_client:close(); no_response = false; if not dns_resp or not len or len < LIMIT_HEADER_LEN then local err = "1. Malformed message length." check_wait(domain, wlist, nil, err) diff --git a/lualib/protocol/redis.lua b/lualib/protocol/redis.lua index 27067cfb..dd1421b0 100644 --- a/lualib/protocol/redis.lua +++ b/lualib/protocol/redis.lua @@ -20,11 +20,7 @@ local concat = table.concat local unpack = table.unpack local sub = string.sub -local string = string -local match = string.match -local find = string.find local byte = string.byte -local upper = string.upper local toint = math.tointeger local CRLF = '\x0d\x0a' @@ -150,11 +146,12 @@ function redis:connect() if not sock then return nil, "Can't Create redis Socket" end - local ok, err = sock:connect(self.host, toint(self.port) or 6379) + local ok, err + ok, err = sock:connect(self.host, toint(self.port) or 6379) if not ok then return nil, "redis connect error: please check network" end - local ok, err = redis_login(sock, self.auth, self.db) + ok, err = redis_login(sock, self.auth, self.db) if not ok then return nil, "redis login error:"..(err or 'close') end @@ -173,7 +170,7 @@ function redis:psubscribe(pattern, func) if not ok or not msg[2] then return nil, "PSUBSCRIBE error: 订阅"..tostring(pattern).."失败." end - co_spawn(function ( ... ) + co_spawn(function () while 1 do local ok, msg = read_response(sock) if not ok or not msg or not self.sock then @@ -242,8 +239,8 @@ function redis:pipeline(opt) local sock = self.sock sock:send(concat(cmds)) local rets = new_tab(max_read_times, 0) - for i = 1, max_read_times do - rets[#rets+1] = {read_response(sock)} + for index = 1, max_read_times do + rets[index] = {read_response(sock)} end return true, rets end From 3d0c34d1778bc16440684462b74fc6b7d3c3aa5f Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 27 Dec 2020 19:42:00 +0800 Subject: [PATCH 677/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=A4=9A=E8=BF=9B?= =?UTF-8?q?=E7=A8=8B=E5=AE=9E=E7=8E=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 58 +++++------- src/core.h | 2 - src/core_ev.h | 12 +-- src/core_start.c | 227 +++++++++++++++++++++++++++++++++++++---------- 4 files changed, 205 insertions(+), 94 deletions(-) diff --git a/src/core.c b/src/core.c index c39eab98..6785666d 100644 --- a/src/core.c +++ b/src/core.c @@ -108,7 +108,7 @@ core_signal sigint; core_signal sigterm; core_signal sigquit; -void signal_init(int* workers){ +void signal_init(int* nprocess){ /* 忽略父进程退出的信号 */ core_signal_init(&sighup, SIG_IGNORE, SIGHUP); @@ -125,30 +125,35 @@ void signal_init(int* workers){ /* TERM信号 显示退出 */ core_signal_init(&sigterm, SIG_EXIT, SIGTERM); core_signal_start(CORE_LOOP_ &sigterm); - if (workers) - core_set_watcher_userdata(&sigterm, workers); + if (nprocess) + core_set_watcher_userdata(&sigterm, nprocess); /* INT信号 显示退出 */ core_signal_init(&sigint, SIG_EXIT, SIGINT); core_signal_start(CORE_LOOP_ &sigint); - if (workers) - core_set_watcher_userdata(&sigint, workers); + if (nprocess) + core_set_watcher_userdata(&sigint, nprocess); /* QUIT信号 显示退出 */ core_signal_init(&sigquit, SIG_EXIT, SIGQUIT); core_signal_start(CORE_LOOP_ &sigquit); - if (workers) - core_set_watcher_userdata(&sigquit, workers); + if (nprocess) + core_set_watcher_userdata(&sigquit, nprocess); } -int core_slave_run(const char entry[]) { +int core_worker_run(const char entry[]) { + /* hook libev 内存分配 */ + core_ev_set_allocator(EV_ALLOC); + /* hook 事件循环错误信息 */ + core_ev_set_syserr_cb(ERROR_CB); + /* 初始化事件循环对象 */ core_loop *loop = core_loop_fork(core_default_loop()); int status = 0; lua_State *L = lua_newstate(L_ALLOC, NULL); if (!L) - exit(-1); + _exit(-1); init_lua_libs(L); @@ -158,7 +163,7 @@ int core_slave_run(const char entry[]) { if (status > 1){ LOG("ERROR", lua_tostring(L, -1)); lua_close(L); - exit(-1); + _exit(-1); } status = CO_RESUME(L, NULL, 0); @@ -174,36 +179,15 @@ int core_slave_run(const char entry[]) { return core_start(loop, 0); } -int core_master_run(int *pids[], int* pidcount) { +int core_master_run(pid_t *pids[], int* pidcount) { + /* hook libev 内存分配 */ + core_ev_set_allocator(EV_ALLOC); + /* hook 事件循环错误信息 */ + core_ev_set_syserr_cb(ERROR_CB); /* 初始化信号 */ signal_init(pidcount); /* 设置pid */ ev_set_userdata(core_default_loop(), pids); /* 初始化主进程 */ return core_start(core_loop_fork(core_default_loop()), 0); -} - -int core_run(const char entry[], int workers) { - /* hook libev 内存分配 */ - core_ev_set_allocator(EV_ALLOC); - /* hook 事件循环错误信息 */ - core_ev_set_syserr_cb(ERROR_CB); - /* 初始化进程 */ -#if defined(__MSYS__) - /* Windows下不可使用多进程 */ - workers = 1; -#endif - pid_t pids[workers]; - int i; - for (i = 0; i < workers; i++) { - int pid = fork(); - if (pid == 0){ - return core_slave_run(entry); - } else if (pid < 0) { - LOG("ERROR", "Create Process Error."); - _exit(-1); - } - pids[i] = pid; - } - return core_master_run((pid_t **)&pids, &workers); -} +} \ No newline at end of file diff --git a/src/core.h b/src/core.h index 46e30b21..4d45c9af 100644 --- a/src/core.h +++ b/src/core.h @@ -5,6 +5,4 @@ #include "core_memory.h" #include "core_ev.h" -int core_run(const char entry[], int workers); - #endif diff --git a/src/core_ev.h b/src/core_ev.h index 44f4a191..291bf23c 100644 --- a/src/core_ev.h +++ b/src/core_ev.h @@ -27,22 +27,18 @@ #define EV_NO_SMP 1 #define EV_NO_THREADS 1 -/* eventfd 与 signalfd */ -#if defined(__linux) || defined(__linux__) - #ifdef __MSYS__ +#if defined(linux) || defined(__linux__) || defined(__MSYS__) + #if defined(__MSYS__) #define EV_USE_SELECT 1 #else #define EV_USE_EPOLL 1 #define EV_USE_INOTIFY 1 #define EV_USE_EVENTFD 1 #endif - // MSYS与Linux都支持 #define EV_USE_SIGNALFD 1 #define EV_USE_TIMERFD 1 -#endif - -#if defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) - #define EV_USE_KQUEUE 1 +#elif defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) + #define EV_USE_KQUEUE 1 #endif #include "ev.h" diff --git a/src/core_start.c b/src/core_start.c index dc244284..dad59ce3 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -1,5 +1,14 @@ #include "core.h" +int core_worker_run(const char entry[]); + +int core_master_run(pid_t *pids[], int* pidcount); + +enum { + isMaster = 1, + isWorker = 2, +}; + #define __CFADMIN_VERSION__ "1.0" #define MAX_ENTRY_LENGTH (1 << 10) @@ -8,10 +17,17 @@ static char script_entry[MAX_ENTRY_LENGTH] = "script/main.lua"; static char pid_filename[MAX_ENTRY_LENGTH] = "cfadmin.pid"; -static int workers = 1; +static int nprocess = 1; + +static int daemoned = 0; + +static int pmode = 0; + +#define MasterPrefix ("cfadmin - Manager Process :") +#define WorkerPrefix ("cfadmin - Worker Process") /* 打印使用指南 */ -void usage_print() { +void cfadmin_usage_print() { printf("cfadmin System : %s(%s)\n", __OS__, __VERSION__ ); printf("\n"); printf("cfadmin Version : %s\n", __CFADMIN_VERSION__ ); @@ -19,46 +35,35 @@ void usage_print() { printf( "cfadmin Usage: ./cfadmin [options]\n" \ "\n" \ - " -h \"Print `cfadmin` usage.\"\n" \ + " -h \"Print `cfadmin` usage.\"\n" \ + "\n" \ + " -d \"Make `cfadmin` run in daemon mode.\"\n" \ "\n" \ - " -d \"Make `cfadmin` run in daemon mode.\"\n" \ + " -e \"Specified `lua` entry file name.\"\n" \ "\n" \ - " -e \"Specify `lua` entry file name.\"\n" \ + " -p \"Specified the process `Pid` write file name.\"\n" \ "\n" \ - " -p \"Specify the process `Pid` write file name.\"\n" \ + " -k \"Send `SIGKILL` signal to `Pid` or `Pid File`.\"\n" \ "\n" \ - " -k \"Send `SIGKILL` signal to `Pid` or `Pid File`.\"\n" \ + " -w \"Spawn specified number of worker processes.\"\n" \ "\n" \ ); } -/* 建立Pid文件 */ -void write_pid_file(const char *filename) { - errno = 0; - FILE *f = fopen(filename, "w"); - if (!f) { - LOG("ERROR", strerror(errno)); - return exit(-1); - } - fprintf(f, "%d\n", getpid()); - fflush(f); - fclose(f); -} - /* 指定入口文件路径 */ -void specify_entry_file(const char *filename) { +void cfadmin_specify_entry_file(const char *filename) { memset(script_entry, 0x0, MAX_ENTRY_LENGTH); memmove(script_entry, filename, strlen(filename)); } /* 指定pid文件路径 */ -void specify_pid_file(const char *filename) { +void cfadmin_specify_pid_file(const char *filename) { memset(pid_filename, 0x0, MAX_ENTRY_LENGTH); memmove(pid_filename, filename, strlen(filename)); } /* 给指定`PID`或包含`PID`的文件发送`SIGQUIT`信号 */ -void specify_kill_process(const char *spid) { +void cfadmin_specify_kill_process(const char *spid) { int pid = atoi(spid); if (pid <= 1) { FILE *fp = fopen(spid, "rb"); @@ -75,59 +80,187 @@ void specify_kill_process(const char *spid) { LOG("ERROR", "Invalid Pid or File name."); return; } + remove(pid_filename); } kill(pid, SIGQUIT); } -void specify_workers(const char* w) { - workers = atoi(w); - if (workers <= 0 ) - workers = 1; +/* 指定子进程数量 */ +void cfadmin_specify_nprocess(const char* w) { + nprocess = atoi(w); + if (nprocess <= 0 || nprocess > 255 ) + nprocess = 1; } /* 后台运行 */ -void specify_process_daemon() { - daemon(1, 0); +void cfadmin_specify_process_daemon() { + daemoned = 1; } -void check_args(int argc, char const *argv[]) { +void cfadmin_init_args(int argc, char const *argv[]) { int opt = -1; int opterr = 0; while ((opt = getopt(argc, (char *const *)argv, "hde:p:k:w:")) != -1) { switch(opt) { + case 'd': + cfadmin_specify_process_daemon(); + continue; case 'w': - specify_workers(optarg); + cfadmin_specify_nprocess(optarg); continue; case 'e': - specify_entry_file(optarg); + cfadmin_specify_entry_file(optarg); continue; case 'p': - specify_pid_file(optarg); + cfadmin_specify_pid_file(optarg); continue; case 'k': - specify_kill_process(optarg); - return _exit(0); - case 'd': - specify_process_daemon(); - continue; + cfadmin_specify_kill_process(optarg); + _exit(0); case '?': case 'h': default : - usage_print(); + cfadmin_usage_print(); exit(0); } } - - write_pid_file(pid_filename); return; } -int main(int argc, char const *argv[]) -{ +/* 将主进程ID写入到文件内 */ +void cfadmin_write_pid_file(const char *filename, pid_t pid) { + errno = 0; + FILE *f = fopen(filename, "w"); + if (!f) { + LOG("ERROR", strerror(errno)); + return exit(-1); + } + fprintf(f, "%d", pid); + fflush(f); + fclose(f); +} - /* 参数检查 */ - check_args(argc, argv); +static inline void cfadmin_set_parameters(int mode) { + if (mode == isMaster){ + unsetenv("cfadmin_isWorker"); + setenv("cfadmin_isMaster", "true", 1); + setenv("cfadmin_script", script_entry, 1); + char np[3]; + memset(np, 0x0, 3); + sprintf(np, "%d", nprocess); + setenv("cfadmin_nprocess", np, 1); + } else { + unsetenv("cfadmin_isMaster"); + setenv("cfadmin_isWorker", "true", 1); + } +} - /* 初始化 */ - return core_run(script_entry, workers); +static inline void cfadmin_unset_parameters() { + unsetenv("cfadmin_isMaster"); + unsetenv("cfadmin_isWorker"); + unsetenv("cfadmin_nprocess"); + unsetenv("cfadmin_script"); } + +int main(int argc, char const *argv[]) { + + /* 命令行参数初始化 */ + cfadmin_init_args(argc, argv); + +#if defined(__MSYS__) + /* Windows下不可使用多进程 */ + nprocess = 1; +#else + int n = -1; + if (getenv("cfadmin_nprocess")) + n = atoi(getenv("cfadmin_nprocess")); + if (n < 255 && n > 0) + nprocess = n; +#endif + + // /* 工作进程执行代码 */ + if(getenv("cfadmin_isWorker") && getenv("cfadmin_script")) { + const char* entry = getenv("cfadmin_script"); + cfadmin_unset_parameters(); + return core_worker_run(entry); + } + + /* 主进程执行代码 */ + if (getenv("cfadmin_isMaster")) { + pid_t ppid = getpid(); + /* 所有子进程的PID数组 */ + pid_t npid[nprocess]; + /* 初始化工作进程命令行参数 */ + char *argp[] = { WorkerPrefix, NULL }; + cfadmin_set_parameters(isWorker); + int i; + for (i = 0; i < nprocess; i++){ + int pid = fork(); + if (pid <= 0) { + if (!pid) { + /* 启动工作进程 */ + int e = execvp("./cfadmin", (char *const *)argp); + if (e < 0) + LOG("ERROR", strerror(errno)); + } + int index; + for (index = 0; index < i; index ++) + kill(npid[index], SIGQUIT); + kill(ppid, SIGQUIT); + return -1; + } + npid[i] = pid; + } + /* 执行主进程事件循环 */ + return core_master_run((pid_t **)npid, &nprocess); + } + + /* 是否需要后台运行 */ + pid_t p = getpid(); + if (daemoned){ + int ok = fork(); + if (ok != 0){ + exit(0); + } + /* 设置会话ID */ + setsid(); + /* 打开空设备 */ + int nfd = open("/dev/null", O_RDWR); + if (nfd < 0) { + LOG("ERROR", strerror(errno)); + exit(-1); + } + + // 关闭标准输入输出 + int fd; + for (fd = 0; fd <= 2; fd++) + close(fd); + + /* 重定向输入输出 */ + (void)dup2(nfd, STDIN_FILENO); + (void)dup2(nfd, STDOUT_FILENO); + (void)dup2(nfd, STDERR_FILENO); + + /* 获取fork后的进程PID */ + p = getpid(); + } + + /* 初始化命令行参数 */ + argv[0] = MasterPrefix; + + /* 设置环境变量 */ + cfadmin_set_parameters(isMaster); + + /*将主进程的PID写入到*/ + cfadmin_write_pid_file(pid_filename, p); + + /* 执行代码*/ + int e = execvp("./cfadmin", (char *const *)argv); + if (e < 0) { + LOG("ERROR", strerror(errno)); + exit(-1); + } + + /* 如果失败就删除pid文件*/ + return remove(pid_filename); +} \ No newline at end of file From ee2ecc487fa68ad905229f788fc8564858773615 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 27 Dec 2020 19:42:22 +0800 Subject: [PATCH 678/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 16 +++++++--------- src/Makefile | 6 +++--- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index c22ad075..cdebfba9 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,7 @@ .PHONY : build rebuild clean +RM = rm -rf + default : @echo "=======================================" @echo "Please use 'make build' command to build it.." @@ -9,24 +11,20 @@ default : # 如果需要修改内存分配器,请修改: # 1. src/Makefile -# 2. lualib/Makefile -# 3. lualib/src/cjson/Makefile +# 2. luaclib/Makefile build : - @$(MAKE) -s -C src build @$(MAKE) -s -C luaclib internal @$(MAKE) -s -C luaclib 3part @$(MAKE) -s -C 3rd build rebuild : - - @$(MAKE) -s -C src build - @$(MAKE) -s -C luaclib internal - @$(MAKE) -s -C luaclib 3part - @$(MAKE) -s -C 3rd build + @$(MAKE) -s clean + @$(MAKE) -s build clean : @echo "********** Clean All Files **********" - rm -rf cfadmin libcore.so luaclib/*.so + @echo "rm -rf cfadmin libcore.so luaclib/*.so 3rd/*.so" + @$(RM) cfadmin libcore.so luaclib/*.so @$(MAKE) -s -C 3rd clean diff --git a/src/Makefile b/src/Makefile index 49fc8f9e..4ac09ea3 100644 --- a/src/Makefile +++ b/src/Makefile @@ -15,17 +15,17 @@ INCLUDES += -I. -L../ -I/usr/local/include # 使用jemalloc内存分配器请启用这段 # CFLAGS += -Wall -O3 -fPIC --shared -DJEMALLOC -fno-strict-aliasing -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib # DLL += -ljemalloc -lev -llua -ldl -lm -# MACRO += -w -O3 -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib/ -DJEMALLOC +# MACRO += -O3 -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib/ -DJEMALLOC # 使用tcmalloc内存分配器请启用这段 # CFLAGS += -Wall -O3 -fPIC --shared -DTCMALLOC -fno-strict-aliasing -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib # DLL += -ltcmalloc -lev -llua -ldl -lm -# MACRO += -w -O3 -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib -DTCMALLOC +# MACRO += -O3 -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib -DTCMALLOC # 默认情况下使用系统内存分配器 CFLAGS += -Wall -O3 -fPIC --shared -fno-strict-aliasing -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib DLL += -lev -llua -ldl -lm -MACRO += -w -O3 -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib +MACRO += -O3 -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib build : From a5a0b78aefb7959eb1c479e8cb1713becd0444ad Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 28 Dec 2020 23:35:26 +0800 Subject: [PATCH 679/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8DBSD=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E4=B8=8B=E7=9A=84=E4=B8=80=E4=BA=9B=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 01425d66..c5b5118e 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -365,9 +365,9 @@ static void IO_ACCEPT_EX(CORE_P_ core_io *io, int revents) { } struct io_sendfile { - uint32_t offset; - uint32_t fd; - uint64_t pos; + int32_t fd; + off_t pos; + off_t offset; lua_State *L; }; @@ -376,7 +376,7 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ if (revents & EV_WRITE){ errno = 0; struct io_sendfile *sf = core_get_watcher_userdata(io); -#if defined(EV_USE_KQUEUE) +#if defined(__APPLE__) || defined(__FreeBSD__) int tag = 0; off_t nBytes = 0; for (;;) { #if defined(__APPLE__) @@ -394,7 +394,7 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ // 当nBytes与tag同时为0时说明发送成功, 其它情况下都当做发送失败. if (0 == nBytes){ lua_pushboolean(sf->L, 1); break; } } -#elif defined(EV_USE_EPOLL) +#elif defined(linux) || defined(__linux__) #include for (;;) { int tag = sendfile(io->fd, sf->fd, NULL, sf->offset); @@ -433,11 +433,7 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ } static int tcp_sendfile(lua_State *L){ - - errno = 0; - core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); - int fd = open(luaL_checkstring(L, 3), O_RDONLY); if (fd < 0) return luaL_error(L, "[%s]: %s.", luaL_checkstring(L, 3), strerror(errno)); From 1176a856b34d78c8e5c18d6dc3b0225015220505 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 28 Dec 2020 23:36:34 +0800 Subject: [PATCH 680/956] =?UTF-8?q?=E4=BC=98=E5=8C=96admin=E7=9A=84?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E8=B6=85=E5=87=BA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/html/dashboard/aside.html | 18 +++++++++--------- lualib/admin/html/dashboard/header.html | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lualib/admin/html/dashboard/aside.html b/lualib/admin/html/dashboard/aside.html index 897e5fe6..6bd0df37 100644 --- a/lualib/admin/html/dashboard/aside.html +++ b/lualib/admin/html/dashboard/aside.html @@ -1,9 +1,9 @@ -
                  +
                  *{*locale['dashboard.menu.menu_manage.menu_add.form.icon.notice']*} +
                  diff --git a/lualib/admin/html/system/menu/menu-edit.html b/lualib/admin/html/system/menu/menu-edit.html index c34c57af..40cef0fc 100644 --- a/lualib/admin/html/system/menu/menu-edit.html +++ b/lualib/admin/html/system/menu/menu-edit.html @@ -52,6 +52,7 @@
                  *{*locale['dashboard.menu.menu_manage.menu_edit.form.icon.notice']*} +
                  diff --git a/lualib/admin/locales/EN-US.lua b/lualib/admin/locales/EN-US.lua index e09190c9..cea260bf 100644 --- a/lualib/admin/locales/EN-US.lua +++ b/lualib/admin/locales/EN-US.lua @@ -122,6 +122,7 @@ return { ['dashboard.menu.menu_manage.menu_add.form.icon.notice'] = 'Menu Left Icon', ['dashboard.menu.menu_manage.menu_add.form.submit'] = 'Submit', + ['dashboard.menu.menu_manage.menu_add.form.icon.help'] = 'View icon', ['dashboard.menu.menu_manage.menu_edit.form.name'] = 'Menu Name', ['dashboard.menu.menu_manage.menu_edit.form.name.notice'] = 'Displaye Menu Name', @@ -131,6 +132,7 @@ return { ['dashboard.menu.menu_manage.menu_edit.form.icon.notice'] = 'Menu Left Icon', ['dashboard.menu.menu_manage.menu_edit.form.submit'] = 'Submit', + ['dashboard.menu.menu_manage.menu_edit.form.icon.help'] = 'View icon', -- 导航管理 ['dashboard.menu.header_manage.title'] = 'NaviBar Manage', diff --git a/lualib/admin/locales/ZH-CN.lua b/lualib/admin/locales/ZH-CN.lua index 8916589c..3d720983 100644 --- a/lualib/admin/locales/ZH-CN.lua +++ b/lualib/admin/locales/ZH-CN.lua @@ -123,7 +123,8 @@ return { ['dashboard.menu.menu_manage.menu_add.form.icon'] = '菜单图标', ['dashboard.menu.menu_manage.menu_add.form.icon.notice'] = '菜单左侧图标', - ['dashboard.menu.menu_manage.menu_add.form.submit'] = '提交', + ['dashboard.menu.menu_manage.menu_add.form.submit'] = '确认增加', + ['dashboard.menu.menu_manage.menu_add.form.icon.help'] = '查看图标代码', ['dashboard.menu.menu_manage.menu_edit.form.name'] = '菜单名称', ['dashboard.menu.menu_manage.menu_edit.form.name.notice'] = '菜单显示的名称', @@ -132,7 +133,8 @@ return { ['dashboard.menu.menu_manage.menu_edit.form.icon'] = '菜单图标', ['dashboard.menu.menu_manage.menu_edit.form.icon.notice'] = '菜单左侧图标', - ['dashboard.menu.menu_manage.menu_edit.form.submit'] = '提交', + ['dashboard.menu.menu_manage.menu_edit.form.submit'] = '确认修改', + ['dashboard.menu.menu_manage.menu_edit.form.icon.help'] = '查看图标代码', -- 导航管理 ['dashboard.menu.header_manage.title'] = '导航管理', From 60d9ccadec41467a45263a23b976b3356431f82b Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 11 Apr 2021 20:35:00 +0800 Subject: [PATCH 729/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0Cookie=E5=9C=A8?= =?UTF-8?q?=E5=86=85=E9=83=A8=E7=9A=84=E5=AE=89=E5=85=A8=E5=8A=A0=E5=AF=86?= =?UTF-8?q?=E3=80=81=E8=A7=A3=E5=AF=86=E7=9A=84=E6=96=B9=E6=B3=95.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Cookie.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lualib/httpd/Cookie.lua b/lualib/httpd/Cookie.lua index 4cce9be5..0ec704a0 100644 --- a/lualib/httpd/Cookie.lua +++ b/lualib/httpd/Cookie.lua @@ -2,9 +2,9 @@ local Co = require "internal.Co" local co_self = Co.self local crypt = require "crypt" -local xor_str = crypt.xor_str -local hexencode = crypt.hexencode -local hexdecode = crypt.hexdecode +local hashkey = crypt.hashkey +local desencode = crypt.desencode +local desdecode = crypt.desdecode local type = type local pcall = pcall @@ -21,16 +21,16 @@ local secure = 'http://github.com/candymi/core_framework' -- 加密Cookie Value local function encode_value (value) - return xor_str(value, secure, true):upper() + return desencode(hashkey(secure), value, true):upper() end -- 解密Cookie Value local function decode_value (value) - local ok, msg = pcall(hexdecode, value:lower()) + local ok, info = pcall(desdecode, hashkey(secure), value:lower(), true) if not ok then - return msg + return end - return xor_str(msg, secure) + return info end -- 当前协程注册的cookie @@ -41,7 +41,7 @@ local Cookie = { function Cookie.setSecure (sec) if type(sec) == 'string' and sec ~= '' then - secure = sec + secure = sec end end From 0c544e59a5090a7bbb88f54dce56d5c163580f3f Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 20 Apr 2021 23:40:44 +0800 Subject: [PATCH 730/956] =?UTF-8?q?=E4=BC=98=E5=8C=96httpd=E8=B7=AF?= =?UTF-8?q?=E7=94=B1=E6=9F=A5=E6=89=BE=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Router.lua | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index d4f18857..2f463f84 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -130,25 +130,20 @@ function Router:hex_route (route) end -- 检查是路径回退是否超出静态文件根目录(是否合法路径.) -function Router:check_path_deep (paths) - local head, tail = paths[1], paths[#paths] - if head == point2 or tail == point or tail == point2 then - return true - end +function Router:is_out_of_directory (paths) local deep = 1 - for _, path in ipairs(paths) do - if path ~= point then - if path == point2 then - deep = deep - 1 - else - deep = deep + 1 - end + for _, p in ipairs(paths) do + if p == point2 then + deep = deep - 1 + elseif p ~= point then + deep = deep + 1 end - if deep <= 0 then - return true - end - end - return false + -- 如果超出目录则直接返回 + if deep <= 0 then + return true + end + end + return false end -- 路由查找 @@ -192,11 +187,11 @@ function Router:find (method, path) return end local tab = self:hex_route(path) - -- 凡是找到'../'并且检查路径回退已经超出静态文件根目录返回404 - if self:check_path_deep(tab) then + -- 凡是超出静态文件根目录返回404. + if self:is_out_of_directory(tab) then return end - + -- 构建静态静态文件检查器 if not self.load_file then self.load_file = function ( path ) local filepath = prefix .. url_decode(path) @@ -204,7 +199,7 @@ function Router:find (method, path) if type(stat) ~= 'table' or stat.mode ~= 'file' then return end - return stat.size, filepath, match(filepath, '%.([^.]+)$') + return stat.size, filepath, match(filepath, '[%.]?([^%./]+)$') end end return self.load_file, typ From 555b5dec289e2951a075136b699ed8abe25fc8dd Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 20 Apr 2021 23:41:29 +0800 Subject: [PATCH 731/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=9A=84=E7=BC=96?= =?UTF-8?q?=E7=A0=81=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Redirect.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lualib/httpd/Redirect.lua b/lualib/httpd/Redirect.lua index b1ea61bc..9ea5ce0e 100644 --- a/lualib/httpd/Redirect.lua +++ b/lualib/httpd/Redirect.lua @@ -2,6 +2,8 @@ local type = type local assert = assert local find = string.find +local urldecode = require "url".decode + local codes = { [301] = "301 Moved Permanently", -- (永久移动) [302] = "302 Found", -- (发现) @@ -24,6 +26,7 @@ local function check_url(url) if type(url) ~= "string" or url == "" then return false end + url = urldecode(url) if find(url, "^/.*") then return url end From 5fb77752a2f77aed13f8bad95b1d5d4c9b71123e Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 20 Apr 2021 23:41:58 +0800 Subject: [PATCH 732/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=89=B9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Response.lua | 33 +++++++++++++++++++++ lualib/httpd/init.lua | 18 +++++++----- lualib/protocol/http/init.lua | 55 +++++++++++++++++++---------------- 3 files changed, 74 insertions(+), 32 deletions(-) create mode 100644 lualib/httpd/Response.lua diff --git a/lualib/httpd/Response.lua b/lualib/httpd/Response.lua new file mode 100644 index 00000000..a20fa5ab --- /dev/null +++ b/lualib/httpd/Response.lua @@ -0,0 +1,33 @@ +local type = type +local assert = assert + +local aio = require "aio" +local aio_stat = aio.stat + +---@comment Httpd响应构造器 +local Response = { __VERSION__ = 0.1 } + +---@comment 文件类型 +---@param filename string @合法且完整的`文件路径`(如: `static/index.html`) +---@param filetype string @合法且完整的`文件类型`(如: `text/css`) +---@param fileinline boolean @响应的文件是否需要内嵌到浏览器 +---@return table @`文件`响应构造器 +function Response.file_response(filename, filetype, fileinline) + assert(type(filename) == 'string' and filename ~= '', "[http `make_file` response] : file name does not exist.") + local info = aio_stat(filename) + if type(info) ~= 'table' or info.mode ~= 'file' then + return assert(nil, "[http `make_file` response] : can't find file or invalid file type.") + end + return { __OPCODE__ = -128, __CODE__ = 200, __FILEINLINE__ = fileinline, __FILENAME__ = filename, __FILETYPE__ = type(filetype) == 'string' and filetype or nil, __FILESIZE__ = aio_stat(filename).size } +end + +---@comment 文本类型 +---@param ctext string @合法且完整的`响应内容`(如: `Hello World.`) +---@param ctype string @合法且完整的`响应类型`(如: `text/html`) +---@return table @`文本`响应构造器 +function Response.text_response(ctext, ctype) + assert(type(ctype) == 'string' and type(ctext) == 'string', "[http `make_text` response] : Invalid content type or response content.") + return { __OPCODE__ = -128, __CODE__ = 200, __TYPE__ = ctype, __MSG__ = ctext } +end + +return Response \ No newline at end of file diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua index 5000a59e..a7ae282c 100644 --- a/lualib/httpd/init.lua +++ b/lualib/httpd/init.lua @@ -16,6 +16,8 @@ local ipairs = ipairs local fmt = string.format local match = string.match local io_write = io.write +local io_type = io.type +local io_output = io.output local toint = math.tointeger -- 请求解析 @@ -166,6 +168,7 @@ function httpd:enable_error_pages () end ---comment 设置Cookie加密Key +---@param secure string @开启并设置`Cookie`密钥 function httpd:cookie_secure (secure) if type(secure) == 'string' and secure ~= '' then self.__cookie_secure = secure @@ -173,6 +176,8 @@ function httpd:cookie_secure (secure) end ---comment 注册静态文件读取路径, foldor是一个目录, ttl是静态文件缓存周期 +---@param foldor string @目录名称 +---@param ttl number @设置缓存时间 function httpd:static(foldor, ttl) if not self.foldor then self.foldor = foldor or 'static' @@ -207,7 +212,7 @@ function httpd:tolog(code, path, ip, ip_list, method, speed) if self.logging then self.logging:dump(fmt(LOG_FMT, now, ip, ip_list, path, method, code, speed)) end - if io.type(io.output()) == 'file' then + if io_type(io_output()) == 'file' then io_write(fmt(LOG_FMT, now, ip, ip_list, path, method, code, speed)) end end @@ -219,8 +224,7 @@ end ---@return boolean function httpd:listen(ip, port, backlog) assert(type(ip) == 'string' and toint(port), "httpd error: invalid ip or port") - self.ip = (ipv4(ip) or ipv6(ip)) and ip or "0.0.0.0" - self.port = port + self.ip, self.port = (ipv4(ip) or ipv6(ip)) and ip or "0.0.0.0", port self.sock:set_backlog(toint(backlog) and toint(backlog) > 0 and toint(backlog) or 128) return assert(self.sock:listen(self.ip, self.port, function (fd, ipaddr, port) @@ -232,7 +236,7 @@ end ---comment 监听加密套接字与端口 ---@param ip string @需要监听的合法`IP`地址. ---@param port integer @指定一个在有效范围内并未被占用的端口. ----@param backlog integer @默认为128 +---@param backlog integer @默认为`128`, 这一般已经够用了. ---@param key string @指定TLS套接字所需的私钥所在路径; ---@param cert string @指定TLS套接字所需的证书所在路径; ---@param pw string @如果证书和私钥设置的密码请填写此字段; @@ -249,8 +253,8 @@ function httpd:listen_ssl(ip, port, backlog, key, cert, pw) ) end ----comment 监听unixsock套接字 ----@param unix_domain_path string @unixdomain所在路径 +---comment 监听`unixsock`套接字 +---@param unix_domain_path string @`unixdomain`所在路径 ---@param backlog integer @默认为128 ---@return boolean function httpd:listenx(unix_domain_path, backlog) @@ -262,7 +266,7 @@ function httpd:listenx(unix_domain_path, backlog) end)) end ----comment 此方法应该在配置完成所有httpd参数后调用, 此方法之后的代码或将永远不可能被执行. +---comment 此方法应该在配置完成所有`httpd`配置后调用, 此方法之后的代码或将永远不会被执行. function httpd:run() if self.ip and self.port then if self.logging then diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index 3e796206..151f947a 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -73,6 +73,7 @@ local PARSER_HTTP_RESPONSE = HTTP_PARSER.PARSER_HTTP_RESPONSE local RESPONSE_CHUNKED_PARSER = HTTP_PARSER.RESPONSE_CHUNKED_PARSER -- OPCODE +local OPCODE_RESP = -128 local OPCODE_THROW = -256 local OPCODE_REDIRECT = -65536 @@ -431,8 +432,6 @@ function HTTP_PROTOCOL.DISPATCH(sock, opt, http) tolog(http, code, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, req_time(start)) sock:send(concat({concat({ REQUEST_STATUCODE_RESPONSE(code), HTTP_DATE(), - 'Origin: *', - 'Allow: GET, POST, PUT, HEAD, OPTIONS', 'Server: ' .. server, 'Connection: keep-alive', 'Content-Type: ' .. (typ == HTTP_PROTOCOL.API and REQUEST_MIME_RESPONSE('json') or REQUEST_MIME_RESPONSE('html')), @@ -488,33 +487,39 @@ function HTTP_PROTOCOL.DISPATCH(sock, opt, http) sock:send(ERROR_RESPONSE(http, 500, PATH, HEADER['X-Real-IP'] or ipaddr, HEADER['X-Forwarded-For'] or ipaddr, METHOD, req_time(start))) goto CONTINUE end + statucode = 200 -- 开发者主动`抛出异常`与`重定向`的时候需要特殊处理. - if type(body) == "table" and body.__OPCODE__ and body.__CODE__ and body.__MSG__ then - local opcode, rcode, response = body.__OPCODE__, body.__CODE__, body.__MSG__ - if opcode == OPCODE_THROW then -- 抛异常 - sock:send(concat({ - REQUEST_STATUCODE_RESPONSE(rcode), HTTP_DATE(), - 'Server: ' .. server, - 'Connection: keep-alive', - 'Content-Length: ' .. tostring(#response), - 'Content-Type: ' .. (typ == HTTP_PROTOCOL.API and REQUEST_MIME_RESPONSE('json') or REQUEST_MIME_RESPONSE('html')), - "", response - }, CRLF)) - tolog(http, rcode, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, req_time(start)) - elseif opcode == OPCODE_REDIRECT then -- 重定向 - sock:send(concat({ - REQUEST_STATUCODE_RESPONSE(rcode), HTTP_DATE(), - 'Server: ' .. server, - 'Connection: keep-alive', - 'Content-Length: 0', - 'Location: ' .. response, - CRLF, - }, CRLF)) - tolog(http, rcode, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, req_time(start)) + if type(body) == "table" and body.__OPCODE__ then + statucode = body.__CODE__ + local opcode = body.__OPCODE__ + if opcode == OPCODE_THROW then -- `异常`构造器 + tolog(http, statucode, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, req_time(start)) + if not send_header(sock, { REQUEST_STATUCODE_RESPONSE(statucode), HTTP_DATE(), 'Server: ' .. server, 'Connection: keep-alive', 'Content-Length: ' .. toint(#body.__MSG__), 'Content-Type: ' .. (typ == HTTP_PROTOCOL.API and REQUEST_MIME_RESPONSE('json') or REQUEST_MIME_RESPONSE('html'))}) or not send_body(sock, body.__MSG__) then + return sock:close() + end + -- goto CONTINUE + elseif opcode == OPCODE_RESP then -- `响应`构造器 + local resp = new_tab(16, 0) + insert(resp, REQUEST_STATUCODE_RESPONSE(statucode)) + insert(resp, HTTP_DATE()) + insert(resp, 'Server: ' .. server) + insert(resp, 'Connection: keep-alive') + insert(resp, 'Content-Length: ' .. (toint(body.__FILESIZE__) or toint(#body.__MSG__))) + insert(resp, 'Content-Type: ' .. (body.__TYPE__ or body.__FILETYPE__ or "application/octet-stream")) + if body.__FILETYPE__ then + insert(resp, fmt('Content-Disposition: %s; filename="%s"', body.__FILEINLINE__ and "inline" or "attachment", body.__FILENAME__:match("[/]?([^/]+)$"))) + end + tolog(http, statucode, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, req_time(start)) + if not send_header(sock, resp) or not send_body(sock, body.__MSG__, body.__FILENAME__) then + return sock:close() + end + -- goto CONTINUE + elseif opcode == OPCODE_REDIRECT then -- `跳转`构造器 + tolog(http, statucode, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, req_time(start)) + send_header(sock, { REQUEST_STATUCODE_RESPONSE(statucode), HTTP_DATE(), 'Server: ' .. server, 'Connection: keep-alive', 'Content-Length: 0', 'Location: ' .. body.__MSG__}) end return sock:close() end - statucode = 200 insert(header, 1, REQUEST_STATUCODE_RESPONSE(statucode)) elseif typ == HTTP_PROTOCOL.WS then local ok, msg = safe_call(Switch_Protocol, http, cls, sock, HEADER, METHOD, VERSION, PATH, HEADER['X-Real-IP'] or ipaddr, start) From f8638682d00101b0fdd1c93bf0af979dc911b73a Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 26 Apr 2021 22:44:30 +0800 Subject: [PATCH 733/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0tcp=20readline?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 123 +++++++++--------------------- lualib/internal/TCP.lua | 160 +++++++++++++++++++--------------------- 2 files changed, 111 insertions(+), 172 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 18d6125c..f91ec76e 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -9,23 +9,6 @@ #define SERVER (0) #define CLIENT (1) -/* 快速字符串比较 */ -static inline const char * fast_cmp(const char* src, size_t src_len, const char* sp, size_t sp_len) { - /* 如果只有一个字符, 那么使用memchr实现来完成 */ - if (sp_len == 1) - return (const char *)memchr(src, sp[0], src_len); - - size_t offset = 0; - while (offset < src_len) { - if (src[offset] == sp[0] && src_len - offset >= sp_len) { - if (!memcmp(src + offset, sp, sp_len)) - return (const char *)src + offset + sp_len - 1; - } - offset++; - } - return NULL; -} - static inline void SETSOCKETOPT(int sockfd, int mode){ int Enable = 1; int ret = 0; @@ -489,110 +472,72 @@ static int tcp_sendfile(lua_State *L){ return 1; } -static int tcp_readline(lua_State *L) { +static int tcp_peek(lua_State *L) { errno = 0; int fd = lua_tointeger(L, 1); if (0 >= fd) return 0; - size_t sp_len = 0; - const char * sp = luaL_checklstring(L, 2, &sp_len); - if (sp_len < 1 && !sp) return 0; + int bsize = lua_tointeger(L, 2); + char buffer[bsize]; - int no_sp = lua_toboolean(L, 3); + if (0 == lua_toboolean(L, 3)) { + lua_pushinteger(L, read(fd, buffer, bsize)); + return 1; + } - size_t tmp_size = 1024; - char *tmp_buf = NULL; - while (1) { - lua_settop(L, 2); - tmp_buf = lua_newuserdata(L, tmp_size); - memset(tmp_buf, 0x0, tmp_size); - int len = recv(fd, tmp_buf, tmp_size, MSG_PEEK); + while (1){ + int len = recv(fd, buffer, bsize, MSG_PEEK); if (len <= 0){ if (errno == EINTR) continue; if (errno == EWOULDBLOCK) { lua_pushnil(L); lua_pushinteger(L, 0); - return 2; + break; } lua_pushnil(L); lua_pushstring(L, strerror(errno)); - return 0; - } - const char *p = fast_cmp(tmp_buf, len, sp, sp_len); - if (!p) { - // 如果查找不到分隔符, 我们需要检查是否因为我们读取的字节数量不够导致的; - if (len == tmp_size){ - tmp_size *= 2; - continue; - } - // 如果读取了所有字节后还是没发现分隔符, 那就等待下一次再检查; - lua_pushboolean(L, 1); - lua_pushinteger(L, 0); - return 2; + break; } - // 检查是否需要去掉分隔符 - tmp_size = read(fd, tmp_buf, p - tmp_buf + 1); - if (no_sp) - tmp_size -= sp_len; + lua_pushlstring(L, buffer, bsize); + lua_pushinteger(L, bsize); break; } - lua_pushlstring(L, tmp_buf, tmp_size); - lua_pushinteger(L, tmp_size); return 2; } -static int tcp_sslreadline(lua_State *L){ +static int tcp_sslpeek(lua_State *L) { + errno = 0; + SSL *ssl = lua_touserdata(L, 1); - if (!ssl) - return 0; + if (!ssl) return 0; - size_t sp_len = 0; - const char * sp = luaL_checklstring(L, 2, &sp_len); - if (sp_len < 1 && !sp) - return 0; + int bsize = lua_tointeger(L, 2); + char buffer[bsize]; - int no_sp = lua_toboolean(L, 3); + if (0 == lua_toboolean(L, 3)) { + lua_pushinteger(L, SSL_read(ssl, buffer, bsize)); + return 1; + } - size_t tmp_size = 1024; - char *tmp_buf = NULL; - errno = 0; - while (1) { - lua_settop(L, 2); - tmp_buf = lua_newuserdata(L, tmp_size); - memset(tmp_buf, 0x0, tmp_size); - int len = SSL_peek(ssl, tmp_buf, tmp_size); - if (0 > len){ - if (errno == EINTR) continue; + while (1){ + int len = SSL_peek(ssl, buffer, bsize); + if (len <= 0){ + if (errno == EINTR) + continue; if (SSL_ERROR_WANT_READ == SSL_get_error(ssl, len)){ lua_pushnil(L); lua_pushinteger(L, 0); - return 2; + break; } lua_pushnil(L); lua_pushstring(L, strerror(errno)); - return 0; - } - const char *p = fast_cmp(tmp_buf, len, sp, sp_len); - if (!p) { - // 如果查找不到分隔符, 我们需要检查是否因为我们读取的字节数量不够导致的; - if (len == tmp_size){ - tmp_size *= 2; - continue; - } - // 如果读取了所有字节后还是没发现分隔符, 那就等待下一次再检查; - lua_pushboolean(L, 1); - lua_pushinteger(L, 0); - return 2; + break; } - // 检查是否需要去掉分隔符 - tmp_size = SSL_read(ssl, tmp_buf, p - tmp_buf + 1); - if (no_sp) - tmp_size -= sp_len; + lua_pushlstring(L, buffer, bsize); + lua_pushinteger(L, bsize); break; } - lua_pushlstring(L, tmp_buf, tmp_size); - lua_pushinteger(L, tmp_size); return 2; } @@ -1192,10 +1137,10 @@ LUAMOD_API int luaopen_tcp(lua_State *L){ luaL_Reg tcp_libs[] = { {"read", tcp_read}, {"write", tcp_write}, - {"readline", tcp_readline}, + {"peek", tcp_peek}, + {"sslpeek", tcp_sslpeek}, {"ssl_read", tcp_sslread}, {"ssl_write", tcp_sslwrite}, - {"ssl_readline", tcp_sslreadline}, {"stop", tcp_stop}, {"start", tcp_start}, {"close", tcp_close}, diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index 7c5a11a2..af5de18d 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -43,8 +43,8 @@ local tcp_listen = tcp.listen local tcp_listen_ex = tcp.listen_ex local tcp_sendfile = tcp.sendfile -local tcp_readline = tcp.readline -local tcp_sslreadline = tcp.ssl_readline +local tcp_peek = tcp.peek +local tcp_sslpeek = tcp.sslpeek local tcp_new_client_fd = tcp.new_client_fd local tcp_new_server_fd = tcp.new_server_fd @@ -302,121 +302,115 @@ function TCP:ssl_send(buf) return co_wait() end -function TCP:readline(sp, no_sp) +-- READLINE +function TCP:readline(sp, nosp) if self.ssl then - return self:ssl_readline(sp, no_sp) + return self:ssl_readline(sp, nosp) end if type(sp) ~= 'string' or #sp < 1 then return nil, "Invalid separator." end - local fd = self.fd - local data, len = tcp_readline(fd, sp, no_sp) - if type(len) ~= 'number' or len > 0 then - return data, len - end - local co = co_self() - self.READ_IO = tcp_pop() - self.read_current_co = co_self() - self.read_co = co_new(function ( ) - while 1 do - local buf, buf_size = tcp_readline(fd, sp, no_sp) - if type(buf) == "string" and type(buf_size) == 'number' then - if self.timer then - self.timer:stop() - self.timer = nil - end + local buffer + local msize = 65535 + while 1 do + ::CONTONIE:: + local buf, bsize = tcp_peek(self.fd, msize, true) + if not buf then + if bsize ~= 0 then + return false, bsize + end + local co = co_self() + self.READ_IO = tcp_pop() + self.read_co = co_new(function ( ) tcp_push(self.READ_IO) tcp_stop(self.READ_IO) self.READ_IO = nil self.read_co = nil - self.read_current_co = nil - return co_wakeup(co, buf, buf_size) - end - if not buf and not buf_size then - if self.timer then - self.timer:stop() - self.timer = nil - end + return co_wakeup(co, true) + end) + self.timer = ti_timeout(self._timeout, function ( ) tcp_push(self.READ_IO) tcp_stop(self.READ_IO) + self.timer = nil self.READ_IO = nil self.read_co = nil self.read_current_co = nil - return co_wakeup(co, nil, "server close") + return co_wakeup(co, nil, "read timeout") + end) + tcp_start(self.READ_IO, self.fd, EVENT_READ, self.read_co) + local ok, errinfo = co_wait() + if not ok then + return false, errinfo end - co_wait() + goto CONTONIE end - end) - self.timer = ti_timeout(self._timeout, function ( ) - tcp_push(self.READ_IO) - tcp_stop(self.READ_IO) - self.timer = nil - self.READ_IO = nil - self.read_co = nil - self.read_current_co = nil - return co_wakeup(co, nil, "read timeout") - end) - tcp_start(self.READ_IO, fd, EVENT_READ, self.read_co) - return co_wait() + buffer = buffer and (buffer .. buf) or buf + local s, e = buffer:find(sp) + if s and e then + tcp_peek(self.fd, #buf - (#buffer - e), false) + if nosp then + e = s - 1 + end + return buffer:sub(1, e), e + end + tcp_peek(self.fd, bsize, false) + end end -function TCP:ssl_readline(sp, no_sp) +-- SSL READLINE +function TCP:ssl_readline(sp, nosp) if not self.ssl then - Log:ERROR("Please use readline method :)") - return nil, "Please use readline method :)" + return self:readline(sp, nosp) end if type(sp) ~= 'string' or #sp < 1 then return nil, "Invalid separator." end - local ssl = self.ssl - local data, len = tcp_sslreadline(ssl, sp, no_sp) - if type(len) ~= 'number' or len > 0 then - return data, len - end - local co = co_self() - self.read_current_co = co_self() - self.READ_IO = tcp_pop() - self.read_co = co_new(function ( ) - while 1 do - local buf, buf_size = tcp_sslreadline(ssl, sp, no_sp) - if type(buf) == "string" and type(buf_size) == 'number' then - if self.timer then - self.timer:stop() - self.timer = nil - end + local buffer + local msize = 65535 + -- 开始读取数据 + while 1 do + ::CONTONIE:: + local buf, bsize = tcp_sslpeek(self.ssl, msize, true) + if not buf then + if bsize ~= 0 then + return false, bsize + end + local co = co_self() + self.READ_IO = tcp_pop() + self.read_co = co_new(function ( ) tcp_push(self.READ_IO) tcp_stop(self.READ_IO) self.READ_IO = nil self.read_co = nil - self.read_current_co = nil - return co_wakeup(co, buf, buf_size) - end - if not buf and not buf_size then - if self.timer then - self.timer:stop() - self.timer = nil - end + return co_wakeup(co, true) + end) + self.timer = ti_timeout(self._timeout, function ( ) tcp_push(self.READ_IO) tcp_stop(self.READ_IO) + self.timer = nil self.READ_IO = nil self.read_co = nil self.read_current_co = nil - return co_wakeup(co, nil, "server close") + return co_wakeup(co, nil, "read timeout") + end) + tcp_start(self.READ_IO, self.fd, EVENT_READ, self.read_co) + local ok, errinfo = co_wait() + if not ok then + return false, errinfo end - co_wait() + goto CONTONIE end - end) - self.timer = ti_timeout(self._timeout, function ( ) - tcp_push(self.READ_IO) - tcp_stop(self.READ_IO) - self.timer = nil - self.READ_IO = nil - self.read_co = nil - self.read_current_co = nil - return co_wakeup(co, nil, "read timeout") - end) - tcp_start(self.READ_IO, self.fd, EVENT_READ, self.read_co) - return co_wait() + buffer = buffer and (buffer .. buf) or buf + local s, e = buffer:find(sp) + if s and e then + tcp_sslpeek(self.ssl, #buf - (#buffer - e), false) + if nosp then + e = s - 1 + end + return buffer:sub(1, e), e + end + tcp_sslpeek(self.ssl, bsize, false) + end end function TCP:recv(bytes) From 62ccbd2f631ac5a589401b21fb489d9a1bbcf12f Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 26 Apr 2021 22:46:34 +0800 Subject: [PATCH 734/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=AE=9A=E6=97=B6?= =?UTF-8?q?=E5=99=A8=E6=9C=AA=E6=AD=A3=E5=B8=B8=E5=81=9C=E6=AD=A2=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/TCP.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index af5de18d..ff49595a 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -322,6 +322,10 @@ function TCP:readline(sp, nosp) local co = co_self() self.READ_IO = tcp_pop() self.read_co = co_new(function ( ) + if self.timer then + self.timer:stop() + self.timer = nil + end tcp_push(self.READ_IO) tcp_stop(self.READ_IO) self.READ_IO = nil @@ -378,6 +382,10 @@ function TCP:ssl_readline(sp, nosp) local co = co_self() self.READ_IO = tcp_pop() self.read_co = co_new(function ( ) + if self.timer then + self.timer:stop() + self.timer = nil + end tcp_push(self.READ_IO) tcp_stop(self.READ_IO) self.READ_IO = nil From 70e996167c864b315e9d3f311485893a065e2e1e Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 6 May 2021 20:44:35 +0800 Subject: [PATCH 735/956] =?UTF-8?q?=E4=BC=98=E5=8C=96Timer=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E4=B8=8E=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/Timer.lua | 126 ++++++++++++++++++++++---------------- 1 file changed, 74 insertions(+), 52 deletions(-) diff --git a/lualib/internal/Timer.lua b/lualib/internal/Timer.lua index 1e81eb6b..c66ebdad 100644 --- a/lualib/internal/Timer.lua +++ b/lualib/internal/Timer.lua @@ -16,93 +16,115 @@ local co_self = co.self local co_wait_ex = coroutine.yield -local remove = table.remove +local tremove = table.remove +local tinsert = table.insert local Timer = new_tab(0, 1 << 10) -local TIMER_LIST = new_tab(1 << 10, 0) +local Tlist = new_tab(1 << 10, 0) --- 内部函数防止被误用 +-- 创建`Timer`对象 local function Timer_new() - if #TIMER_LIST > 0 then - return remove(TIMER_LIST) - end - return ti_new() + return tremove(Tlist) or ti_new() end -local function Timer_release(t) - ti_stop(t) - TIMER_LIST[#TIMER_LIST+1] = t +-- 停止`Timer`对象 +local function Timer_stop(self) + Timer[self] = nil + tinsert(Tlist, self.t) + ti_stop(self.t) + return true end +-- 启动`Timer`对象 local function Timer_start(self) Timer[self] = self ti_start(self.t, self.timeout or self.repeats, self.co) return self end -local function Timer_stop(self) - if self and not self.stoped then - self.stoped = true - if self.t then - Timer_release(self.t) - self.t = nil - end - Timer[self] = nil - end -end - -function Timer.count() - return #TIMER_LIST -end - --- 超时器 -- -function Timer.timeout(timeout, cb) - if type(timeout) ~= 'number' or timeout <= 0 or type(cb) ~= 'function' then +---comment 一次性定时器 +---@param timeout number @超时时间 +---@param callback function @回调函数 +function Timer.timeout(timeout, callback) + if type(timeout) ~= 'number' or timeout <= 0 or type(callback) ~= 'function' then return end - local once = {stoped = false, timeout = timeout, stop = Timer_stop, t = Timer_new(), co = nil} - once.co = co_new(function() - if once.stoped then + local timer = { t = Timer_new(), timeout = timeout, stoped = false } + -- 实现`停止定时器`的方法 + timer.stop = function(self) + if not self.stoped then + self.stoped = true + Timer_stop(self) + end + return true + end + -- 实现定时器回调协程 + timer.co = co_new(function() + if timer.stoped then + timer.co = nil return end - co_spawn(cb) - Timer_stop(once) + co_spawn(callback) + return Timer_stop(timer) end) - return Timer_start(once) + -- 启动定时器 + return Timer_start(timer) end --- 循环定时器 -- -function Timer.at(repeats, cb) - if type(repeats) ~= 'number' or repeats <= 0 or type(cb) ~= 'function' then +---comment 重复定时器 +---@param repeats number @间隔时间 +---@param callback function @回调函数 +function Timer.at(repeats, callback) + if type(repeats) ~= 'number' or repeats <= 0 or type(callback) ~= 'function' then return end - local at = {stoped = false, repeats = repeats, stop = Timer_stop, t = Timer_new(), co = nil} - at.co = co_new(function( ) - while 1 do - if at.stoped then + local timer = { t = Timer_new(), repeats = repeats, stoped = false } + -- 实现`停止定时器`的方法 + timer.stop = function(self) + if not self.stoped then + self.stoped = true + Timer_stop(self) + end + return true + end + -- 实现定时器回调协程 + timer.co = co_new(function() + while true do + if timer.stoped then + timer.co = nil return end - co_spawn(cb) + co_spawn(callback) co_wait_ex() end end) - return Timer_start(at) + -- 启动定时器 + return Timer_start(timer) end --- 休眠 -- -function Timer.sleep(timeout) - if type(timeout) ~= 'number' or timeout <= 0 then +---comment 休眠当前协程 +---@param nsleep number @休眠时间(毫秒) +function Timer.sleep(nsleep) + if type(nsleep) ~= 'number' or nsleep <= 0 then return end - local sleep = {stoped = false, timeout = timeout, t = Timer_new(), co = nil } - local cco = co_self() - sleep.co = co_new(function () - Timer_stop(sleep) - return co_wakeup(cco) + -- 创建`Timer`对象 + local timer = { t = Timer_new(), main_co = co_self(), timeout = nsleep } + -- 实现定时器回调协程 + timer.co = co_new(function () + Timer_stop(timer) + timer.co = nil + return co_wakeup(timer.main_co) end) - Timer_start(sleep) + Timer_start(timer) return co_wait() end +---comment 计算缓存的定时器对象数量 +---@return integer +function Timer.count() + return #Tlist +end + return Timer \ No newline at end of file From ec4e392530e237ec2f7058fed5ee741850072eb6 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 12 May 2021 17:02:24 +0800 Subject: [PATCH 736/956] =?UTF-8?q?crypt=E5=BA=93=E5=A2=9E=E5=8A=A0rc4?= =?UTF-8?q?=E7=AE=97=E6=B3=95=E5=AF=B9=E7=A7=B0=E5=8A=A0=E5=AF=86/?= =?UTF-8?q?=E8=A7=A3=E5=AF=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/Makefile | 2 +- luaclib/src/lcrypt/lcrypt.c | 1 + luaclib/src/lcrypt/lcrypt.h | 3 +++ luaclib/src/lcrypt/rc.c | 28 ++++++++++++++++++++++++++++ lualib/crypt/init.lua | 3 +++ lualib/crypt/rc4.lua | 31 +++++++++++++++++++++++++++++++ 6 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 luaclib/src/lcrypt/rc.c create mode 100644 lualib/crypt/rc4.lua diff --git a/luaclib/src/lcrypt/Makefile b/luaclib/src/lcrypt/Makefile index 9d373613..0874c90f 100644 --- a/luaclib/src/lcrypt/Makefile +++ b/luaclib/src/lcrypt/Makefile @@ -16,5 +16,5 @@ INCLUDES = -I../../../src -I/usr/local/include LIBS = -L../ -L../../ -L../../../ -L/usr/local/lib build: - @$(CC) -o lcrypt.so lcrypt.c aes.c des.c dh.c rsa.c sha.c hmac.c hmac_ex.c b64.c crc.c url.c hex.c uuid.c sm.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) + @$(CC) -o lcrypt.so lcrypt.c aes.c des.c dh.c rsa.c sha.c hmac.c hmac_ex.c b64.c crc.c url.c hex.c uuid.c sm.c rc.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) @mv *.so ../../ diff --git a/luaclib/src/lcrypt/lcrypt.c b/luaclib/src/lcrypt/lcrypt.c index b07e18e4..e0f069f1 100644 --- a/luaclib/src/lcrypt/lcrypt.c +++ b/luaclib/src/lcrypt/lcrypt.c @@ -126,6 +126,7 @@ LUAMOD_API int luaopen_lcrypt(lua_State *L) { { "aes_ofb_decrypt", laes_ofb_decrypt }, { "aes_ctr_decrypt", laes_ctr_decrypt }, { "aes_gcm_decrypt", laes_gcm_decrypt }, + { "rc4", lrc4 }, // DES加密/解密 { "desencode", ldesencode }, { "desdecode", ldesdecode }, diff --git a/luaclib/src/lcrypt/lcrypt.h b/luaclib/src/lcrypt/lcrypt.h index f37a6138..489e20f3 100644 --- a/luaclib/src/lcrypt/lcrypt.h +++ b/luaclib/src/lcrypt/lcrypt.h @@ -1,6 +1,7 @@ #define LUA_LIB #include +#include #include #include #include @@ -54,6 +55,8 @@ int lhmac_sha256(lua_State *L); int lhmac_sha384(lua_State *L); int lhmac_sha512(lua_State *L); +int lrc4(lua_State *L); + int laes_ecb_encrypt(lua_State *L); int laes_cbc_encrypt(lua_State *L); int laes_cfb_encrypt(lua_State *L); diff --git a/luaclib/src/lcrypt/rc.c b/luaclib/src/lcrypt/rc.c new file mode 100644 index 00000000..c720b996 --- /dev/null +++ b/luaclib/src/lcrypt/rc.c @@ -0,0 +1,28 @@ +#include "lcrypt.h" + +// RC4 对称加密/解密算法 +int lrc4(lua_State *L) { + size_t ksize = 0; size_t tsize = 0; + + const uint8_t* key = (const uint8_t*)luaL_checklstring(L, 1, &ksize); + if (!key || ksize <= 0) + return luaL_error(L, "Invalid rc4 key."); + + const uint8_t* text = (const uint8_t*)luaL_checklstring(L, 2, &tsize); + if (!text || tsize <= 0) + return luaL_error(L, "Invalid rc4 text."); + + char *buffer = lua_newuserdata(L, tsize); + memset(buffer, 0x00, tsize); + + RC4_KEY rc4key; + // 设置密钥 + RC4_set_key(&rc4key, ksize, key); + // 加密/解密数据 + RC4(&rc4key, tsize, text, (uint8_t *)buffer); + + // 返回解密/解密的数据 + lua_pushlstring(L, buffer, tsize); + + return 1; +} \ No newline at end of file diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index ee2c7d6e..316787c7 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -24,6 +24,9 @@ require "crypt.crc"(crypt) -- Base64编码/解码算法 require "crypt.b64"(crypt) +-- RC4算法 +require "crypt.rc4"(crypt) + -- AES对称加密算法 require "crypt.aes"(crypt) diff --git a/lualib/crypt/rc4.lua b/lualib/crypt/rc4.lua new file mode 100644 index 00000000..9246a2f8 --- /dev/null +++ b/lualib/crypt/rc4.lua @@ -0,0 +1,31 @@ +local CRYPT = require "lcrypt" +local hexencode = CRYPT.hexencode +local hexdecode = CRYPT.hexdecode +local rc4 = CRYPT.rc4 + +local RC4 = {} + +-- `RC4`加密 +function RC4.rc4_encrypt(key, text, hex) + local hash = rc4(key, text) + if hash and hex then + return hexencode(hash) + end + return hash +end + +-- `RC4`解密 +function RC4.rc4_decrypt(key, cipher, hex) + if cipher and hex then + cipher = hexdecode(cipher) + end + return rc4(key, cipher) +end + +-- 初始化函数 +return function (t) + for k, v in pairs(RC4) do + t[k] = v + end + return RC4 +end \ No newline at end of file From 1119ef2d01379766a32ef35d861668d17fc3c3e2 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 12 May 2021 17:03:00 +0800 Subject: [PATCH 737/956] =?UTF-8?q?=E8=B0=83=E6=95=B4TCP=E5=AF=B9=E7=9B=91?= =?UTF-8?q?=E5=90=AC=E7=AB=AF=E5=8F=A3=E7=9A=84=E5=88=A4=E6=96=AD.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index f91ec76e..b20aa9a3 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -171,15 +171,18 @@ static int create_server_fd(const char *ip, int port, int backlog){ SA.sin6_addr = in6addr_any; struct in6_addr addr; - /* 如果填写的是 ::1 */ + /* 如果填写的是`::1` */ if (!strcmp(ip, "::1") && inet_pton(AF_INET6, "::1", &addr) == 1){ SA.sin6_addr = addr; - /* 如果填写的是 127.0.0.1 */ + /* 如果填写的是`127.0.0.1` */ } else if (!strcmp(ip, "127.0.0.1") && inet_pton(AF_INET6, "::ffff:127.0.0.1", &addr) == 1) { SA.sin6_addr = addr; - /* 如果填写的是 其它IPv6地址 */ + /* 如果填写的是其它`IPv6`地址 */ } else if (inet_pton(AF_INET6, ip, &addr) == 1) { SA.sin6_addr = addr; + /* 如果填写的是 `0.0.0.0` 则监听所有地址 */ + } else if (!strcmp(ip, "0.0.0.0")) { + SA.sin6_addr = in6addr_any; /* 检查IPv4地址或者是非法IP地址 */ } else { struct in_addr addr4; From bd27a502119813da809138ef6ae13c2af28aa4ce Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 12 May 2021 17:03:49 +0800 Subject: [PATCH 738/956] =?UTF-8?q?aio=E5=BA=93=E5=A2=9E=E5=8A=A0popen?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E4=B8=8Ekill=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/Makefile | 2 +- luaclib/src/laio.c | 91 +++++++++++++++++++++++++++++++++++++++++++++ lualib/aio/init.lua | 90 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 182 insertions(+), 1 deletion(-) diff --git a/luaclib/Makefile b/luaclib/Makefile index 098489b3..e955be6b 100644 --- a/luaclib/Makefile +++ b/luaclib/Makefile @@ -25,7 +25,7 @@ internal : @echo "CC - ltimer" @$(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore @echo "CC - laio" - @$(CC) -o laio.so src/laio.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -leio + @$(CC) -o laio.so src/laio.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -leio -lev @echo "CC - ltcp" @$(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore diff --git a/luaclib/src/laio.c b/luaclib/src/laio.c index 80cb200e..a9dc9d21 100644 --- a/luaclib/src/laio.c +++ b/luaclib/src/laio.c @@ -644,6 +644,95 @@ static int laio_rmdir(lua_State* L) { return 1; } +static void CHILD_CB (core_loop *loop, ev_child *w, int revents){ + lua_State *co = (lua_State *)core_get_watcher_userdata(w); + if (lua_status(co) == LUA_YIELD || lua_status(co) == LUA_OK){ + lua_pushinteger(co, w->rstatus); + int status = CO_RESUME(co, NULL, 1); + if (status != LUA_YIELD && status != LUA_OK) + LOG("ERROR", lua_tostring(co, -1)); + } + // 停止继续监听 + ev_child_stop(loop, w); +} + +// 实现异步`system`方法. +static pid_t laio_system(lua_State *L, const char* command, int pfd) { + pid_t pid = fork(); + if (pid < 0) + return (pid_t)-1; + if (pid < 1) { + // 子进程需要设置为独立的输入输出管道; + (void)dup2(pfd, STDIN_FILENO); + (void)dup2(pfd, STDOUT_FILENO); + (void)dup2(pfd, STDERR_FILENO); + // 子进程需要进与父子进程的的上下文分离 + if (execl("/bin/sh", "sh", "-c", command, NULL)) + write(STDOUT_FILENO, strerror(errno), strlen(strerror(errno))); + // 正常执行完毕是不会走到这里, 所以只能是执行失败. + exit(-1); // exit(EXIT_FAILURE); + } + return pid; +} + +// 自定义创建进程 +static int laio_popen(lua_State *L) { + size_t clen = 0; + const char *command = luaL_checklstring(L, 1, &clen); + if (!command || clen == 0) + return luaL_error(L, "Invalid command.\n"); + + lua_State *co = lua_tothread(L, 2); + + int std[] = { -1, -1 }; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, std) < 0) + return luaL_error(L, "Cand't create pipe.\n"); + + pid_t pid = laio_system(L, command, std[1]); + if (pid < 1) { + close(std[0]); close(std[1]); + return luaL_error(L, "Cand't create subprocess.\n"); + } + + lua_createtable(L, 0, 2); + // 记录子进程的`PID`. + lua_pushliteral(L, "pid"); + lua_pushinteger(L, pid); + lua_rawset(L, -3); + + // 记录双向通信用到的`管道`; + lua_pushliteral(L, "pipe"); + lua_createtable(L, 2, 0); + // 创建`stdin` + lua_pushinteger(L, std[0]); + lua_rawseti(L, -2, 1); + // 创建`stdout` + lua_pushinteger(L, std[1]); + lua_rawseti(L, -2, 2); + + lua_rawset(L, -3); + + // 监听`子进程`的退出事件 + lua_pushliteral(L, "child"); + ev_child *w = lua_newuserdata(L, sizeof(ev_child)); + core_set_watcher_userdata(w, co); + ev_child_init(w, CHILD_CB, pid, 0); + ev_child_start(core_default_loop(), w); + lua_rawset(L, -3); + // 返回一个包含`pipe`、`ev_child`指针的`table`. + return 1; +} + +// 根据子进程的`pid`杀死子进程 +static int laio_kill(lua_State *L){ + lua_Integer pid = luaL_checkinteger(L, 1); + if (pid > 1 && getpid() != pid) + kill(pid, SIGKILL); + lua_pushboolean(L, 1); + return 1; +} + + LUAMOD_API int luaopen_laio(lua_State* L){ // printf("主线程ID为: %d\n", pthread_self()); luaL_checkversion(L); @@ -671,6 +760,8 @@ LUAMOD_API int luaopen_laio(lua_State* L){ { "create", laio_create }, { "fflush", laio_fflush }, { "remove", laio_remove }, + { "popen", laio_popen }, + { "kill", laio_kill }, {NULL, NULL}, }; luaL_newlib(L, aio_libs); diff --git a/lualib/aio/init.lua b/lualib/aio/init.lua index 596fec53..7bb013d9 100644 --- a/lualib/aio/init.lua +++ b/lualib/aio/init.lua @@ -1,5 +1,9 @@ local class = require "class" +local tcp = require "tcp" +local tcp_read = tcp.read +local tcp_close = tcp.close + local sys = require "sys" local new_tab = sys.new_tab @@ -25,10 +29,13 @@ local aio_rename = laio.rename local aio_readdir = laio.readdir local aio_readpath = laio.readpath local aio_truncate = laio.truncate +local aio_popen = laio.popen +local aio_kill = laio.kill local type = type local assert = assert local toint = math.tointeger +local tconcat = table.concat local aio = new_tab(0, 1 << 16) @@ -458,4 +465,87 @@ function aio.remove(filename) return co_wait() end +-- `pfile`对象 +local pfile = { __name = "__PFILE__" } + +pfile.__index = pfile + +function pfile:read(bytes) + if not self.fd then + return false, "fd closed." + end + bytes = toint(bytes) + if bytes and bytes > 0 then + return tcp_read(self.fd, bytes) + end + bytes = 65535 + local buffers = {} + while true do + local buf = tcp_read(self.fd, bytes) + if not buf then + self:close() + break + end + buffers[#buffers+1] = buf + end + return tconcat(buffers) +end + +function pfile:__gc() + self:close() +end + +function pfile:close() + if self.fd then + tcp_close(self.fd) + self.fd = nil + end +end + +---comment @`io.popen`的非阻塞版实现 +---@param command string @`command`是一个`string`类型的参数; +---@param timeout number @`timeout`是一个`Number`类型的参数(可选), 可以指定合适的超时时间来控制进程运行时长. +---@return table|boolean @子进程在退出后此方法才会返回; 正常退出返回`pfile`对象, 异常退出与错误退出返回`false`与出错信息; +---@return nil|string @需要注意的是`pfile`对象必须开发者手动关闭, 否则可能会造成`fd`泄漏的问题. +function aio.popen(command, timeout) + local ok, obj, co_timer, killed + local co = co_self() + local co_cb = co_new(function (id) + -- 正常结束返回`0`, 异常结束返回`进程id`, 超时`kill`返回信号代码(9); + -- print("进程结束: ", id) + if co_timer then + co_timer:stop() + end + return co_wakeup(co, id == 0 and true or false) + end) + ok, obj = pcall(aio_popen, command, co_cb) + if not ok then + return false, "[AIO_POPEN ERROR] : " .. obj + end + co_timer = cf.timeout(tonumber(timeout), function () + aio_kill(obj.pid) + killed = true + co_timer = nil + end) + tcp_close(obj.pipe[2]) + local f = setmetatable({ fd = obj.pipe[1] }, pfile) + -- 等待子进程运行结束: 如果运行失败则返回错误信息, 如果运行成功则返回需要自己读取与手动关闭的`pfile`对象. + if not co_wait() then + local errinfo = f:read "*a" + if killed then + errinfo = "command timeout killed." + end + f:close() + return false, "[AIO_POPEN ERROR] : " .. errinfo + end + return f +end + +---comment `kill`指定进程 +---@param pid integer 指定进程的`PID` +function aio.kill(pid) + aio_kill(pid) + return true +end + return aio From 681c297eaf4a7b4d7afca8d4c816fddbc1e00665 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 15 May 2021 20:29:50 +0800 Subject: [PATCH 739/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0lua=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E5=88=B05.4.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sh b/build.sh index 85384454..c499017b 100755 --- a/build.sh +++ b/build.sh @@ -16,8 +16,8 @@ current=`pwd` rm -rf build && mkdir build && cd build -# git clone https://gitee.com/CandyMi/lua -b v5.4.2 -git clone https://github.com/CandyMi/lua -b v5.4.2 +# git clone https://gitee.com/CandyMi/lua -b v5.4.3 +git clone https://github.com/CandyMi/lua -b v5.4.3 # git clone https://gitee.com/CandyMi/libev -b v4.33 git clone https://github.com/CandyMi/libev -b v4.33 From ba8bf45448b9c830f68072f2fcca8a7eea9b2a17 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 15 May 2021 20:34:26 +0800 Subject: [PATCH 740/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0ripemd160=E7=AD=89?= =?UTF-8?q?=E7=BD=95=E8=A7=81=E6=91=98=E8=A6=81=E7=AE=97=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/hmac.c | 40 +++++++++++++++++++++++++++++++++++++ luaclib/src/lcrypt/lcrypt.c | 4 ++++ luaclib/src/lcrypt/lcrypt.h | 6 ++++++ luaclib/src/lcrypt/sha.c | 22 ++++++++++++++++++++ lualib/crypt/hmac.lua | 27 +++++++++++++++++++++++++ lualib/crypt/sha.lua | 27 +++++++++++++++++++++++++ 6 files changed, 126 insertions(+) diff --git a/luaclib/src/lcrypt/hmac.c b/luaclib/src/lcrypt/hmac.c index 7eeec369..096386b8 100644 --- a/luaclib/src/lcrypt/hmac.c +++ b/luaclib/src/lcrypt/hmac.c @@ -9,6 +9,27 @@ # define SHA512_DIGEST_LENGTH 64 */ +int lhmac_md4(lua_State *L) { + size_t key_sz = 0; + size_t text_sz = 0; + + const char * key = luaL_checklstring(L, 1, &key_sz); + if (!key || key_sz <= 0) + return luaL_error(L, "Invalid key value."); + + const char * text = luaL_checklstring(L, 2, &text_sz); + if (!text || text_sz <= 0) + return luaL_error(L, "Invalid text value."); + + uint32_t result_len = MD4_DIGEST_LENGTH; + unsigned char result[result_len]; + + HMAC(EVP_md4(), key, key_sz, (const unsigned char*)text, text_sz, result, &result_len); + + lua_pushlstring(L, (const char *)result, result_len); + return 1; +}; + int lhmac_md5(lua_State *L) { size_t key_sz = 0; size_t text_sz = 0; @@ -127,4 +148,23 @@ int lhmac_sha512(lua_State *L) { HMAC(EVP_sha512(), key, key_sz, (const unsigned char*)text, text_sz, result, &result_len); lua_pushlstring(L, (const char *)result, result_len); return 1; +}; + +int lhmac_ripemd160(lua_State *L) { + size_t key_sz = 0; + size_t text_sz = 0; + + const char * key = luaL_checklstring(L, 1, &key_sz); + if (!key || key_sz <= 0) + return luaL_error(L, "Invalid key value."); + + const char * text = luaL_checklstring(L, 2, &text_sz); + if (!text || text_sz <= 0) + return luaL_error(L, "Invalid text value."); + + uint32_t result_len = RIPEMD160_DIGEST_LENGTH; + unsigned char result[result_len]; + HMAC(EVP_ripemd160(), key, key_sz, (const unsigned char*)text, text_sz, result, &result_len); + lua_pushlstring(L, (const char *)result, result_len); + return 1; }; \ No newline at end of file diff --git a/luaclib/src/lcrypt/lcrypt.c b/luaclib/src/lcrypt/lcrypt.c index e0f069f1..706fb73f 100644 --- a/luaclib/src/lcrypt/lcrypt.c +++ b/luaclib/src/lcrypt/lcrypt.c @@ -84,6 +84,7 @@ LUAMOD_API int luaopen_lcrypt(lua_State *L) { { "urlencode", lurlencode }, { "urldecode", lurldecode }, // SHA + { "md4", lmd4 }, { "md5", lmd5 }, { "crc32", lcrc32 }, { "crc64", lcrc64 }, @@ -93,7 +94,9 @@ LUAMOD_API int luaopen_lcrypt(lua_State *L) { { "sha256", lsha256 }, { "sha384", lsha384 }, { "sha512", lsha512 }, + { "ripemd160", lripemd160}, // HMAC + { "hmac_md4", lhmac_md4 }, { "hmac_md5", lhmac_md5 }, { "hmac_sha1", lhmac_sha128 }, { "hmac_sha128", lhmac_sha128 }, @@ -102,6 +105,7 @@ LUAMOD_API int luaopen_lcrypt(lua_State *L) { { "hmac_sha384", lhmac_sha384 }, { "hmac_sha512", lhmac_sha512 }, { "hmac_hash", lhmac_hash }, + { "hmac_ripemd160", lhmac_ripemd160}, { "xor_str", lxor_str }, // 公钥加密 -> 私钥解密 { "rsa_public_key_encode", lrsa_public_key_encode }, diff --git a/luaclib/src/lcrypt/lcrypt.h b/luaclib/src/lcrypt/lcrypt.h index 489e20f3..4e136d59 100644 --- a/luaclib/src/lcrypt/lcrypt.h +++ b/luaclib/src/lcrypt/lcrypt.h @@ -1,7 +1,9 @@ #define LUA_LIB #include +#include #include +#include #include #include #include @@ -41,19 +43,23 @@ int lhmac_hash(lua_State *L); int lhmac64(lua_State *L); int lhmac64_md5(lua_State *L); +int lmd4(lua_State *L); int lmd5(lua_State *L); int lsha128(lua_State *L); int lsha224(lua_State *L); int lsha256(lua_State *L); int lsha384(lua_State *L); int lsha512(lua_State *L); +int lripemd160(lua_State *L); +int lhmac_md4(lua_State *L); int lhmac_md5(lua_State *L); int lhmac_sha128(lua_State *L); int lhmac_sha224(lua_State *L); int lhmac_sha256(lua_State *L); int lhmac_sha384(lua_State *L); int lhmac_sha512(lua_State *L); +int lhmac_ripemd160(lua_State *L); int lrc4(lua_State *L); diff --git a/luaclib/src/lcrypt/sha.c b/luaclib/src/lcrypt/sha.c index 365e9176..52aa0ba9 100644 --- a/luaclib/src/lcrypt/sha.c +++ b/luaclib/src/lcrypt/sha.c @@ -9,6 +9,17 @@ # define SHA512_DIGEST_LENGTH 64 */ +int lmd4(lua_State *L) { + size_t sz = 0; + const char * text = luaL_checklstring(L, 1, &sz); + if (!text || sz <= 0) + return luaL_error(L, "Invalid text value."); + unsigned char result[MD4_DIGEST_LENGTH]; + MD4((const unsigned char*) text, sz, result); + lua_pushlstring(L, (const char *)result, MD4_DIGEST_LENGTH); + return 1; +}; + int lmd5(lua_State *L) { size_t sz = 0; const char * text = luaL_checklstring(L, 1, &sz); @@ -74,3 +85,14 @@ int lsha512(lua_State *L) { lua_pushlstring(L, (const char *)result, SHA512_DIGEST_LENGTH); return 1; }; + +int lripemd160(lua_State *L) { + size_t sz = 0; + const char * text = luaL_checklstring(L, 1, &sz); + if (!text || sz <= 0) + return luaL_error(L, "Invalid text value."); + unsigned char result[RIPEMD160_DIGEST_LENGTH]; + RIPEMD160((const unsigned char*) text, sz, result); + lua_pushlstring(L, (const char *)result, RIPEMD160_DIGEST_LENGTH); + return 1; +}; \ No newline at end of file diff --git a/lualib/crypt/hmac.lua b/lualib/crypt/hmac.lua index d7882271..78978c93 100644 --- a/lualib/crypt/hmac.lua +++ b/lualib/crypt/hmac.lua @@ -1,6 +1,8 @@ local CRYPT = require "lcrypt" local hmac64 = CRYPT.hmac64 local hmac_hash = CRYPT.hmac_hash +local hmac_md2 = CRYPT.hmac_md2 +local hmac_md4 = CRYPT.hmac_md4 local hmac_md5 = CRYPT.hmac_md5 local hmac64_md5 = CRYPT.hmac64_md5 @@ -9,6 +11,7 @@ local hmac_sha224 = CRYPT.hmac_sha224 local hmac_sha256 = CRYPT.hmac_sha256 local hmac_sha384 = CRYPT.hmac_sha384 local hmac_sha512 = CRYPT.hmac_sha512 +local hmac_ripemd160 = CRYPT.hmac_ripemd160 local hexencode = CRYPT.hexencode @@ -38,6 +41,22 @@ function HMAC.hmac_hash(key, text, hex) return hash end +function HMAC.hmac_md2(key, text, hex) + local hash = hmac_md2(key, text) + if hash and hex then + return hexencode(hash) + end + return hash +end + +function HMAC.hmac_md4(key, text, hex) + local hash = hmac_md4(key, text) + if hash and hex then + return hexencode(hash) + end + return hash +end + function HMAC.hmac_md5(key, text, hex) local hash = hmac_md5(key, text) if hash and hex then @@ -94,6 +113,14 @@ function HMAC.hmac_sha512(key, text, hex) return hash end +function HMAC.hmac_ripemd(key, text, hex) + local hash = hmac_ripemd160(key, text) + if hash and hex then + return hexencode(hash) + end + return hash +end + -- 初始化函数 return function (t) for k, v in pairs(HMAC) do diff --git a/lualib/crypt/sha.lua b/lualib/crypt/sha.lua index e0770b08..24d3a770 100644 --- a/lualib/crypt/sha.lua +++ b/lualib/crypt/sha.lua @@ -1,14 +1,33 @@ local CRYPT = require "lcrypt" +local md2 = CRYPT.md2 +local md4 = CRYPT.md4 local md5 = CRYPT.md5 local sha1 = CRYPT.sha1 local sha224 = CRYPT.sha224 local sha256 = CRYPT.sha256 local sha384 = CRYPT.sha384 local sha512 = CRYPT.sha512 +local ripemd160 = CRYPT.ripemd160 local hexencode = CRYPT.hexencode local SHA = {} +function SHA.md2(text, hex) + local hash = md2(text) + if hash and hex then + return hexencode(hash) + end + return hash +end + +function SHA.md4(text, hex) + local hash = md4(text) + if hash and hex then + return hexencode(hash) + end + return hash +end + function SHA.md5(text, hex) local hash = md5(text) if hash and hex then @@ -65,6 +84,14 @@ function SHA.sha512(text, hex) return hash end +function SHA.ripemd(text, hex) + local hash = ripemd160(text) + if hash and hex then + return hexencode(hash) + end + return hash +end + -- 初始化函数 return function (t) for k, v in pairs(SHA) do From e63098ac0a3b02d446c81a1639cb24b137577f7c Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 15 May 2021 20:44:43 +0800 Subject: [PATCH 741/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ac418366..18d7e46d 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@

                  -### preview +### Preview

                  @@ -70,13 +70,13 @@

                  -### feedback +### Feedback * [ISSUES](https://github.com/CandyMi/cfadmin/issues) * [QQ Group](https://shang.qq.com/wpa/qunwpa?idkey=5cc977ebaf4eb17391b2c6b03eb0ee36e3d3c1871bc95ba3c96ffc426a9dc907) -### support +### Support **Encourage the price of a cup of coffee.** @@ -85,6 +85,10 @@

                  +### List of contributors + + * [suzaichuan](https://github.com/suzaichuan) - `Test` and `Problem Feedback`. + ### Users |Project Name|Project Logo| From 7912458d9e2b36378957849863400ddd8a9a2176 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 15 May 2021 20:49:29 +0800 Subject: [PATCH 742/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 --------- README_zh-cn.md | 9 ++------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 18d7e46d..14bfd516 100644 --- a/README.md +++ b/README.md @@ -76,15 +76,6 @@ * [QQ Group](https://shang.qq.com/wpa/qunwpa?idkey=5cc977ebaf4eb17391b2c6b03eb0ee36e3d3c1871bc95ba3c96ffc426a9dc907) -### Support - - **Encourage the price of a cup of coffee.** - -

                  - - -

                  - ### List of contributors * [suzaichuan](https://github.com/suzaichuan) - `Test` and `Problem Feedback`. diff --git a/README_zh-cn.md b/README_zh-cn.md index ff69d8be..7bfc24dd 100644 --- a/README_zh-cn.md +++ b/README_zh-cn.md @@ -72,14 +72,9 @@ * [我要加群](https://shang.qq.com/wpa/qunwpa?idkey=5cc977ebaf4eb17391b2c6b03eb0ee36e3d3c1871bc95ba3c96ffc426a9dc907) -### 支持 +### 贡献者名单 - **如果您使用的还算顺手, 可以支持一杯咖啡予以鼓励.** - -

                  - - -

                  + * [suzaichuan](https://github.com/suzaichuan) - `积极测试`与`问题反馈`. ### 用户列表 From 7ad7d08303560b6d677905ec14732e2c655c2d14 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 16 May 2021 00:51:43 +0800 Subject: [PATCH 743/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0adler32=E7=AE=97?= =?UTF-8?q?=E6=B3=95=E5=B9=B6=E6=9B=B4=E6=96=B0=E6=96=87=E4=BB=B6=E5=91=BD?= =?UTF-8?q?=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/crc.c | 27 +++++++++++++++++++++++++++ luaclib/src/lcrypt/lcrypt.c | 1 + luaclib/src/lcrypt/lcrypt.h | 2 ++ lualib/crypt/checksum.lua | 26 ++++++++++++++++++++++++++ lualib/crypt/crc.lua | 21 --------------------- lualib/crypt/init.lua | 4 ++-- 6 files changed, 58 insertions(+), 23 deletions(-) create mode 100644 lualib/crypt/checksum.lua delete mode 100644 lualib/crypt/crc.lua diff --git a/luaclib/src/lcrypt/crc.c b/luaclib/src/lcrypt/crc.c index 2cdd73d8..9b7e9e1e 100644 --- a/luaclib/src/lcrypt/crc.c +++ b/luaclib/src/lcrypt/crc.c @@ -1,3 +1,13 @@ +/* + * Copyright (C) 2020, CandyMi. + * Adler-32校验和简单算法: + 1)通过求解两个16位的数值Adler、sum实现,并将结果连结成一个32位整数; + 2)Adler就是字符串中每个字节的和,而sum是Adler在相加时每一步的阶段值之和; + 3)在Adler-32开始求值前,adler初始化为1,然后加上每一个字节值; sum初始化为0,然后加上第一个步骤的Adler的值; + 4)Adler与Sum每次求值的结果都需要对65521取模, 最终结果为: Adler | (sum << 16); + * 注意: 最终结果为int32或uint32(正负值), 目前只取正值. +*/ + #include "lcrypt.h" #include @@ -233,3 +243,20 @@ int lcrc64(lua_State *L){ lua_pushlstring(L, (const char*)buf, 20); return 1; }; + +int ladler32(lua_State *L){ + size_t len = 0; + const uint8_t *buf = (const uint8_t *)luaL_checklstring(L, 1, &len); + if (!buf || len < 1) + return luaL_error(L, "invalid string."); + + uint64_t adler = 1; + uint64_t sum = 0; + size_t index; + for (index = 0; index < len; index++) { + adler = (adler + buf[index]) % 65521; + sum = (sum + adler) % 65521; + } + lua_pushinteger(L, (uint32_t)(adler | (sum << 16))); + return 1; +} diff --git a/luaclib/src/lcrypt/lcrypt.c b/luaclib/src/lcrypt/lcrypt.c index 706fb73f..4e46c6b0 100644 --- a/luaclib/src/lcrypt/lcrypt.c +++ b/luaclib/src/lcrypt/lcrypt.c @@ -88,6 +88,7 @@ LUAMOD_API int luaopen_lcrypt(lua_State *L) { { "md5", lmd5 }, { "crc32", lcrc32 }, { "crc64", lcrc64 }, + { "adler32", ladler32 }, { "sha1", lsha128 }, { "sha128", lsha128 }, { "sha224", lsha224 }, diff --git a/luaclib/src/lcrypt/lcrypt.h b/luaclib/src/lcrypt/lcrypt.h index 4e136d59..a480fe75 100644 --- a/luaclib/src/lcrypt/lcrypt.h +++ b/luaclib/src/lcrypt/lcrypt.h @@ -22,6 +22,8 @@ int lfromhex(lua_State *L); int lcrc32(lua_State *L); int lcrc64(lua_State *L); +int ladler32(lua_State *L); + int lb64encode(lua_State *L); int lb64decode(lua_State *L); diff --git a/lualib/crypt/checksum.lua b/lualib/crypt/checksum.lua new file mode 100644 index 00000000..f77e39d1 --- /dev/null +++ b/lualib/crypt/checksum.lua @@ -0,0 +1,26 @@ +local CRYPT = require "lcrypt" +local crc32 = CRYPT.crc32 +local crc64 = CRYPT.crc64 +local adler32 = CRYPT.adler32 + +local CHECKSUM = {} + +function CHECKSUM.crc32(text) + return crc32(text) +end + +function CHECKSUM.crc64(text) + return crc64(text) +end + +function CHECKSUM.adler32(text) + return adler32(text) +end + +-- 初始化函数 +return function (t) + for k, v in pairs(CHECKSUM) do + t[k] = v + end + return CHECKSUM +end \ No newline at end of file diff --git a/lualib/crypt/crc.lua b/lualib/crypt/crc.lua deleted file mode 100644 index 066a948e..00000000 --- a/lualib/crypt/crc.lua +++ /dev/null @@ -1,21 +0,0 @@ -local CRYPT = require "lcrypt" -local crc32 = CRYPT.crc32 -local crc64 = CRYPT.crc64 - -local CRC = {} - -function CRC.crc32(text) - return crc32(text) -end - -function CRC.crc64(text) - return crc64(text) -end - --- 初始化函数 -return function (t) - for k, v in pairs(CRC) do - t[k] = v - end - return CRC -end \ No newline at end of file diff --git a/lualib/crypt/init.lua b/lualib/crypt/init.lua index 316787c7..c8f39cbf 100644 --- a/lualib/crypt/init.lua +++ b/lualib/crypt/init.lua @@ -18,8 +18,8 @@ require "crypt.sha"(crypt) -- 哈希消息认证码算法 require "crypt.hmac"(crypt) --- 循环冗余校验算法 -require "crypt.crc"(crypt) +-- 冗余校验算法 +require "crypt.checksum"(crypt) -- Base64编码/解码算法 require "crypt.b64"(crypt) From 1f5ed698bedf96036113bcc6394baed90a435ea2 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 19 May 2021 00:08:49 +0800 Subject: [PATCH 744/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E5=AE=8F=E5=88=A4=E6=96=AD=E4=B8=8E=E5=8E=BB=E9=99=A4luaclib?= =?UTF-8?q?=E7=9A=84=E6=A0=B9=E7=9B=AE=E5=BD=95C=E5=BA=93=E6=9F=A5?= =?UTF-8?q?=E6=89=BE=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 18 ++++++++---------- src/core_ev.c | 15 ++++++--------- src/core_ev.h | 7 ++++--- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/core.c b/src/core.c index 0771695c..101ce3de 100644 --- a/src/core.c +++ b/src/core.c @@ -1,19 +1,15 @@ #include "core.h" #define LUALIBS_PATH \ - "lualib/?.lua;;lualib/?/init.lua;;" \ - "3rd/?.lua;;3rd/?/init.lua;;" \ - "script/?.lua;;script/?/init.lua;;" + "lualib/?.lua;;lualib/?/init.lua;;" \ + "script/?.lua;;script/?/init.lua;;" \ + "3rd/?.lua;;3rd/?/init.lua;;" #define LUACLIBS_PATH \ "luaclib/?.so;;luaclib/lib?.so;;" \ "luaclib/?.dylib;;luaclib/lib?.dylib;;" \ "luaclib/?.dll;;luaclib/msys-?.dll;;" \ \ - "./?.so;;./lib?.so;;" \ - "./?.dylib;;./lib?.dylib;;" \ - "./?.dll;;./msys-?.dll;;" \ - \ "3rd/?.so;;3rd/lib?.so;;" \ "3rd/?.dylib;;3rd/lib?.dylib;;" \ "3rd/?.dll;;3rd/msys-?.dll;;" @@ -195,17 +191,19 @@ int core_master_run(pid_t *pids, int* pidcount) { core_ev_set_allocator(EV_ALLOC); /* hook 事件循环错误信息 */ core_ev_set_syserr_cb(ERROR_CB); + /* 初始化事件循环对象 */ + core_loop *loop = core_loop_fork(core_default_loop()); /* 初始化信号 */ signal_init(pidcount); /* 设置pid */ - ev_set_userdata(core_default_loop(), pids); + ev_set_userdata(loop, pids); /* 注册子进程监听 */ ev_child childs[*pidcount]; int index; for (index = 0; index < *pidcount; index++) { ev_child_init(&childs[index], CHILD_CB, pids[index], 0); - ev_child_start(core_default_loop(), &childs[index]); + ev_child_start(loop, &childs[index]); } /* 初始化主进程 */ - return core_start(core_loop_fork(core_default_loop()), 0); + return core_start(loop, 0); } \ No newline at end of file diff --git a/src/core_ev.c b/src/core_ev.c index 2b4aca62..1c211ca0 100644 --- a/src/core_ev.c +++ b/src/core_ev.c @@ -30,7 +30,7 @@ void core_io_start(core_loop *loop, core_io *io){ void core_io_stop(core_loop *loop, core_io *io){ if (io->events || io->fd){ ev_io_stop(loop ? loop : CORE_LOOP, io); - io->fd = io->events = 0x0; + io->fd = io->events = -1; } } /* =========== IO =========== */ @@ -64,24 +64,21 @@ void core_signal_start(core_loop *loop, core_signal *signal){ core_loop* core_loop_fork(core_loop *loop) { - ev_loop_fork(loop); + // ev_loop_fork(loop); return loop; } core_loop* core_default_loop(){ - int BEST_BACKEND = 0; - -#if defined(__MSYS__) - BEST_BACKEND |= EVBACKEND_SELECT | EVFLAG_SIGNALFD; +#if defined(__MSYS__) || defined(__CYGWIN__) + BEST_BACKEND |= EVBACKEND_POLL | EVFLAG_SIGNALFD | EVFLAG_NOINOTIFY; #elif defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) - BEST_BACKEND |= EVBACKEND_KQUEUE; + BEST_BACKEND |= EVBACKEND_KQUEUE | EVFLAG_NOINOTIFY | EVFLAG_NOTIMERFD; #elif defined(linux) || defined(__linux) || defined(__linux__) BEST_BACKEND |= EVBACKEND_EPOLL | EVFLAG_SIGNALFD; #else - BEST_BACKEND |= EVBACKEND_SELECT; + BEST_BACKEND |= EVBACKEND_SELECT | EVFLAG_NOINOTIFY | EVFLAG_NOTIMERFD; #endif - return ev_default_loop( BEST_BACKEND | EVFLAG_FORKCHECK ); } diff --git a/src/core_ev.h b/src/core_ev.h index a972bcc7..7c9f86b2 100644 --- a/src/core_ev.h +++ b/src/core_ev.h @@ -23,9 +23,10 @@ #define EV_NO_SMP 1 #define EV_NO_THREADS 1 -#if defined(linux) || defined(__linux__) || defined(__MSYS__) - #if defined(__MSYS__) - #define EV_USE_SELECT 1 +#if defined(linux) || defined(__linux__) || defined(__MSYS__) || defined(__CYGWIN__) + #if defined(__MSYS__) || defined(__CYGWIN__) + #define EV_USE_POLL 1 + // #define EV_USE_SELECT 1 #else #define EV_USE_EPOLL 1 #define EV_USE_INOTIFY 1 From fbc22afc21624bbead76056b5614acea7286cd65 Mon Sep 17 00:00:00 2001 From: "Mr.che" <869646063@qq.com> Date: Fri, 28 May 2021 11:30:39 +0800 Subject: [PATCH 745/956] Update ua.lua MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加Edge与Chrome 90版本的UA --- lualib/protocol/http/ua.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lualib/protocol/http/ua.lua b/lualib/protocol/http/ua.lua index 573b5c10..d14736cc 100644 --- a/lualib/protocol/http/ua.lua +++ b/lualib/protocol/http/ua.lua @@ -23,6 +23,8 @@ local ua = { "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36 Maxthon/5.3.8.2000", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36 OPR/68.0.3618.63", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36 Edg/90.0.818.66", -- Apple "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:86.0) Gecko/20100101 Firefox/86.0", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:85.0) Gecko/20100101 Firefox/85.0", @@ -57,4 +59,4 @@ function ua.get_user_agent() return ua[random(1, #ua)] end -return ua \ No newline at end of file +return ua From 401d1a28fc5373cf35dcef83ba0140ba2140fdd0 Mon Sep 17 00:00:00 2001 From: "Mr.che" <869646063@qq.com> Date: Wed, 2 Jun 2021 10:35:22 +0800 Subject: [PATCH 746/956] Update core_sys.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加系统类型的宏判断 --- src/core_sys.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core_sys.h b/src/core_sys.h index 94d3c08c..25ca044b 100644 --- a/src/core_sys.h +++ b/src/core_sys.h @@ -28,7 +28,7 @@ #include #include -#if defined(__MSYS__) +#if defined(__MSYS__) || defined(__CYGWIN__) #define __OS__ ("Windows") #elif defined(__APPLE__) #define __OS__ ("Apple") From 2f3a47c7627fecda48918e3bb110c3615d85065c Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 3 Jun 2021 23:32:07 +0800 Subject: [PATCH 747/956] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96websocket?= =?UTF-8?q?=E6=97=B6=E5=A2=9E=E5=8A=A0args=E4=B8=8Eheaders?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 4 ++-- lualib/protocol/websocket/server.lua | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index 151f947a..151a57e3 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -297,7 +297,7 @@ local function Switch_Protocol(http, cls, sock, header, method, version, path, i return end http:tolog(101, path, header['X-Real-IP'] or ip, X_Forwarded_FORMAT(header['X-Forwarded-For'] or ip), method, req_time(start_time)) - return wsserver.start { cls = cls, sock = sock, ext = ext } + return wsserver.start { cls = cls, args = form_argsencode(path), headers = header, sock = sock, ext = ext } end local function send_header (sock, header) @@ -522,7 +522,7 @@ function HTTP_PROTOCOL.DISPATCH(sock, opt, http) end insert(header, 1, REQUEST_STATUCODE_RESPONSE(statucode)) elseif typ == HTTP_PROTOCOL.WS then - local ok, msg = safe_call(Switch_Protocol, http, cls, sock, HEADER, METHOD, VERSION, PATH, HEADER['X-Real-IP'] or ipaddr, start) + local ok, msg = safe_call(Switch_Protocol, http, cls, sock, tab_copy(HEADER), METHOD, VERSION, PATH, HEADER['X-Real-IP'] or ipaddr, start) if not ok then Log:ERROR(msg) end diff --git a/lualib/protocol/websocket/server.lua b/lualib/protocol/websocket/server.lua index d4ead236..2d8a7760 100644 --- a/lualib/protocol/websocket/server.lua +++ b/lualib/protocol/websocket/server.lua @@ -93,7 +93,7 @@ function Websocket.start(opt) local ext = opt.ext local w = ws:new { sock = sock, ext = ext } - local cls = opt.cls:new { ws = w } + local cls = opt.cls:new { ws = w, args = opt.args, headers = opt.headers } local on_open = assert(type(cls.on_open) == 'function' and cls.on_open, "'on_open' method is not implemented.") local on_message = assert(type(cls.on_message) == 'function' and cls.on_message, "'on_message' method is not implemented.") local on_error = assert(type(cls.on_error) == 'function' and cls.on_error, "'on_error' method is not implemented.") From 330ea7a12ff6cf3df526bc918b7ae0aa5767c470 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 3 Jun 2021 23:34:35 +0800 Subject: [PATCH 748/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0websocket=E6=8E=A5?= =?UTF-8?q?=E5=85=A5=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/ws.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/script/ws.lua b/script/ws.lua index cf523ccd..731612c1 100644 --- a/script/ws.lua +++ b/script/ws.lua @@ -1,14 +1,19 @@ local class = require "class" local cf = require "cf" -local json = require "json" +require "utils" local websocket = class("websocket") + function websocket:ctor(opt) self.ws = opt.ws -- websocket对象 + self.args = opt.args -- GET传递的参数 + self.headers = opt.headers -- HTTP请求头部 self.send_masked = false -- 掩码(默认为false, 不建议修改或者使用) self.max_payload_len = 65535 -- 最大有效载荷长度(默认为65535, 不建议修改或者使用) self.timeout = 15 -- 默认为一直等待, 非number类型会导致异常. self.count = 0 + var_dump(self.args) + var_dump(self.headers) end function websocket:on_open() From 8b69b7851cdeedb2496de2e894032676dd82327d Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 7 Jun 2021 21:38:37 +0800 Subject: [PATCH 749/956] =?UTF-8?q?=E5=8D=95=E8=BF=9B=E7=A8=8B=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E7=8B=AC=E5=8D=A0=E6=A8=A1=E5=BC=8F=E8=80=8C=E9=9D=9E?= =?UTF-8?q?master->slave=E6=A8=A1=E5=BC=8F.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 116 ++++++++++++++++----------- src/core_start.c | 205 +++++++++++++++++++++++++++++------------------ 2 files changed, 198 insertions(+), 123 deletions(-) diff --git a/src/core.c b/src/core.c index 101ce3de..cab1ff91 100644 --- a/src/core.c +++ b/src/core.c @@ -1,19 +1,27 @@ #include "core.h" #define LUALIBS_PATH \ - "lualib/?.lua;;lualib/?/init.lua;;" \ - "script/?.lua;;script/?/init.lua;;" \ - "3rd/?.lua;;3rd/?/init.lua;;" + "lualib/?.lua;;lualib/?/init.lua;;" \ + "lualib/?.lc;;lualib/?/init.lc;;" \ + \ + "3rd/?.lua;;3rd/?/init.lua;;" \ + "3rd/?.lc;;3rd/?/init.lc;;" \ + \ + "script/?.lua;;script/?/init.lua;;" \ + "script/?.lc;;script/?/init.lc;;" + #define LUACLIBS_PATH \ - "luaclib/?.so;;luaclib/lib?.so;;" \ - "luaclib/?.dylib;;luaclib/lib?.dylib;;" \ - "luaclib/?.dll;;luaclib/msys-?.dll;;" \ - \ - "3rd/?.so;;3rd/lib?.so;;" \ - "3rd/?.dylib;;3rd/lib?.dylib;;" \ + "luaclib/?.so;;luaclib/lib?.so;;" \ + "3rd/?.so;;3rd/lib?.so;;" \ + \ + "3rd/?.dylib;;3rd/lib?.dylib;;" \ + "luaclib/?.dylib;;luaclib/lib?.dylib;;" \ + \ + "luaclib/?.dll;;luaclib/msys-?.dll;;" \ "3rd/?.dll;;3rd/msys-?.dll;;" + /* 忽略信号 */ static void SIG_IGNORE(core_loop *loop, core_signal *signal, int revents){ return ; @@ -24,15 +32,15 @@ static void SIG_EXIT(core_loop *loop, core_signal *signal, int revents){ if (ev_userdata(loop) && core_get_watcher_userdata(signal)) { int index; pid_t *pids = (pid_t *)ev_userdata(loop); - int pidcount = *(int*)core_get_watcher_userdata(signal); - for (index = 0; index < pidcount; index++) { + int nprocess = *(int*)core_get_watcher_userdata(signal); + for (index = 0; index < nprocess; index++) { pid_t pid = pids[index]; if (pid > 0) kill(pid, SIGKILL); + pids[index] = -1; } } - _exit(0); - return ; + return exit(EXIT_SUCCESS); } /* 子进程退出则打印异常 */ @@ -40,15 +48,31 @@ static void CHILD_CB (core_loop *loop, ev_child *w, int revents){ ev_child_stop(loop, w); ev_feed_signal_event(loop, SIGQUIT); if (w->rstatus){ - printf ("[WARNING]: sub process %d exited with signal: %d\n", w->rpid, w->rstatus); + printf("[WARNING]: sub process %d exited with signal: %d\n", w->rpid, w->rstatus); fflush(stdout); } } /* 内部异常 */ -static void ERROR_CB(const char *msg){ +static void EV_ERROR_CB(const char *msg){ LOG("ERROR", msg); - return ; + if (core_default_loop()) { + pid_t *pids = (pid_t *)ev_userdata(core_default_loop()); + if (!pids) { + kill(getppid(), SIGKILL); + return; + } + int index; + int nprocess = atoi(getenv("cfadmin_nprocess")) > 1 ? atoi(getenv("cfadmin_nprocess")) : 0; + for (index = 0; index < nprocess; index++) { + pid_t pid = pids[index]; + if (pid > 0) + kill(pid, SIGKILL); + pids[index] = -1; + } + } + /* 这里给SUCCESS的原因是方便时间循环出错后的打印 */ + return exit(EXIT_SUCCESS); } /* 为libev内存hook注入日志 */ @@ -77,33 +101,33 @@ static void* L_ALLOC(void *ud, void *ptr, size_t osize, size_t nsize){ void init_lua_libs(lua_State *L){ /* lua 标准库 */ - luaL_openlibs(L); + luaL_openlibs(L); - lua_pushglobaltable(L); - lua_pushliteral(L, "null"); - lua_pushlightuserdata(L, NULL); - lua_rawset(L, -3); - lua_pushliteral(L, "NULL"); - lua_pushlightuserdata(L, NULL); - lua_rawset(L, -3); + lua_pushglobaltable(L); + lua_pushliteral(L, "null"); + lua_pushlightuserdata(L, NULL); + lua_rawset(L, -3); + lua_pushliteral(L, "NULL"); + lua_pushlightuserdata(L, NULL); + lua_rawset(L, -3); - lua_settop(L, 0); + lua_settop(L, 0); - /* 注入lua搜索域 */ + /* 注入lua搜索域 */ lua_getglobal(L, "package"); - /* 注入lualib搜索路径 */ + /* 注入lualib搜索路径 */ lua_pushliteral(L, LUALIBS_PATH); lua_setfield(L, 1, "path"); - /* 注入luaclib搜索路径 */ - lua_pushliteral(L, LUACLIBS_PATH); + /* 注入luaclib搜索路径 */ + lua_pushliteral(L, LUACLIBS_PATH); lua_setfield(L, 1, "cpath"); lua_settop(L, 0); - /* 优化Lua的GC */ - CO_GCRESET(L); + /* 优化Lua的GC */ + CO_GCRESET(L); } /* 注册需要忽略的信号 */ @@ -134,19 +158,19 @@ static inline void signal_init(int* nprocess){ core_signal_init(&sigterm, SIG_EXIT, SIGTERM); core_signal_start(CORE_LOOP_ &sigterm); if (nprocess) - core_set_watcher_userdata(&sigterm, nprocess); + core_set_watcher_userdata(&sigterm, nprocess); /* INT信号 显示退出 */ core_signal_init(&sigint, SIG_EXIT, SIGINT); core_signal_start(CORE_LOOP_ &sigint); if (nprocess) - core_set_watcher_userdata(&sigint, nprocess); + core_set_watcher_userdata(&sigint, nprocess); /* QUIT信号 显示退出 */ core_signal_init(&sigquit, SIG_EXIT, SIGQUIT); core_signal_start(CORE_LOOP_ &sigquit); if (nprocess) - core_set_watcher_userdata(&sigquit, nprocess); + core_set_watcher_userdata(&sigquit, nprocess); } @@ -154,7 +178,7 @@ int core_worker_run(const char entry[]) { /* hook libev 内存分配 */ core_ev_set_allocator(EV_ALLOC); /* hook 事件循环错误信息 */ - core_ev_set_syserr_cb(ERROR_CB); + core_ev_set_syserr_cb(EV_ERROR_CB); /* 初始化事件循环对象 */ core_loop *loop = core_loop_fork(core_default_loop()); @@ -162,26 +186,26 @@ int core_worker_run(const char entry[]) { lua_State *L = lua_newstate(L_ALLOC, NULL); if (!L) - core_exit(); + core_exit(); init_lua_libs(L); status = luaL_loadfile(L, entry); if (status > 1){ - LOG("ERROR", lua_tostring(L, -1)); - lua_close(L); - core_exit(); + LOG("ERROR", lua_tostring(L, -1)); + lua_close(L); + core_exit(); } status = CO_RESUME(L, NULL, 0); if (status > 1){ - LOG("ERROR", lua_tostring(L, -1)); - lua_close(L); - core_exit(); + LOG("ERROR", lua_tostring(L, -1)); + lua_close(L); + core_exit(); } if (status == LUA_YIELD) - signal_init(NULL); + signal_init(NULL); return core_start(loop, 0); } @@ -190,7 +214,7 @@ int core_master_run(pid_t *pids, int* pidcount) { /* hook libev 内存分配 */ core_ev_set_allocator(EV_ALLOC); /* hook 事件循环错误信息 */ - core_ev_set_syserr_cb(ERROR_CB); + core_ev_set_syserr_cb(EV_ERROR_CB); /* 初始化事件循环对象 */ core_loop *loop = core_loop_fork(core_default_loop()); /* 初始化信号 */ @@ -201,8 +225,8 @@ int core_master_run(pid_t *pids, int* pidcount) { ev_child childs[*pidcount]; int index; for (index = 0; index < *pidcount; index++) { - ev_child_init(&childs[index], CHILD_CB, pids[index], 0); - ev_child_start(loop, &childs[index]); + ev_child_init(&childs[index], CHILD_CB, pids[index], 0); + ev_child_start(loop, &childs[index]); } /* 初始化主进程 */ return core_start(loop, 0); diff --git a/src/core_start.c b/src/core_start.c index 76a178fa..775c4ad3 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -11,11 +11,11 @@ enum { #define __CFADMIN_VERSION__ "1.0" -#define MAX_ENTRY_LENGTH (1 << 10) +#define MAX_FILENAME_LEN (1 << 10) -static char script_entry[MAX_ENTRY_LENGTH] = "script/main.lua"; +static char script_entry[MAX_FILENAME_LEN] = "script/main.lua"; -static char pid_filename[MAX_ENTRY_LENGTH] = "cfadmin.pid"; +static char pid_filename[MAX_FILENAME_LEN] = "cfadmin.pid"; static int nprocess = 1; @@ -48,22 +48,23 @@ void cfadmin_usage_print() { " -w \"Spawn specified number of worker processes.\"\n" \ "\n" \ ); + exit(0); } /* 指定入口文件路径 */ -void cfadmin_specify_entry_file(const char *filename) { - memset(script_entry, 0x0, MAX_ENTRY_LENGTH); +static inline void cfadmin_specify_entry_file(const char *filename) { + memset(script_entry, 0x0, MAX_FILENAME_LEN); memmove(script_entry, filename, strlen(filename)); } /* 指定pid文件路径 */ -void cfadmin_specify_pid_file(const char *filename) { - memset(pid_filename, 0x0, MAX_ENTRY_LENGTH); +static inline void cfadmin_specify_pid_file(const char *filename) { + memset(pid_filename, 0x0, MAX_FILENAME_LEN); memmove(pid_filename, filename, strlen(filename)); } /* 给指定`PID`或包含`PID`的文件发送`SIGQUIT`信号 */ -void cfadmin_specify_kill_process(const char *spid) { +static inline void cfadmin_specify_kill_process(const char *spid) { int pid = atoi(spid); if (pid <= 1) { FILE *fp = NULL; @@ -82,10 +83,11 @@ void cfadmin_specify_kill_process(const char *spid) { remove(pid_filename); } kill(pid, SIGQUIT); + exit(0); } /* 指定子进程数量 */ -void cfadmin_specify_nprocess(const char* w) { +static inline void cfadmin_specify_nprocess(const char* w) { nprocess = atoi(w); if (nprocess <= 0 || nprocess > 255 ) nprocess = 1; @@ -96,7 +98,7 @@ void cfadmin_specify_process_daemon() { daemoned = 1; } -void cfadmin_init_args(int argc, char const *argv[]) { +static inline void cfadmin_init_args(int argc, char const *argv[]) { int opt = -1; int opterr = 0; while ((opt = getopt(argc, (char *const *)argv, "hde:p:k:w:")) != -1) { @@ -115,19 +117,17 @@ void cfadmin_init_args(int argc, char const *argv[]) { continue; case 'k': cfadmin_specify_kill_process(optarg); - _exit(0); case '?': case 'h': default : cfadmin_usage_print(); - exit(0); } } return; } /* 将主进程ID写入到文件内 */ -void cfadmin_write_pid_file(const char *filename, pid_t pid) { +static inline void cfadmin_write_pid_file(const char *filename, pid_t pid) { errno = 0; FILE *f = fopen(filename, "w"); if (!f) { @@ -139,6 +139,7 @@ void cfadmin_write_pid_file(const char *filename, pid_t pid) { fclose(f); } +/* 设置当前进程运行模式 */ static inline void cfadmin_set_parameters(int mode) { if (mode == isMaster){ unsetenv("cfadmin_isWorker"); @@ -154,6 +155,7 @@ static inline void cfadmin_set_parameters(int mode) { } } +/* 初始化参数 */ static inline void cfadmin_unset_parameters() { unsetenv("cfadmin_isMaster"); unsetenv("cfadmin_isWorker"); @@ -161,12 +163,106 @@ static inline void cfadmin_unset_parameters() { unsetenv("cfadmin_script"); } +/* 多进程 - 运行工作进程 */ +static inline int cfadmin_worker_run(const char* entry) { + return core_worker_run(entry); +} + +/* 多进程 - 运行主进程 */ +static inline pid_t cfadmin_master_run() { + /* 获取父进程ID */ + pid_t ppid = getpid(); + /* 所有子进程的PID数组 */ + pid_t npid[nprocess]; + /* 初始化工作进程命令行参数 */ + char *argp[] = { WorkerPrefix, NULL }; + /* 子进程模式 */ + cfadmin_set_parameters(isWorker); + /* 开始创建进程 */ + int i; + for (i = 0; i < nprocess; i++){ + int pid = fork(); + if (pid <= 0) { + if (!pid) { + /* 启动工作进程 */ + int e = execvp("./cfadmin", (char *const *)argp); + if (e < 0) + LOG("ERROR", strerror(errno)); + } + int index; + for (index = 0; index < i; index ++) + kill(npid[index], SIGQUIT); + kill(ppid, SIGQUIT); + return (pid_t)-1; + } + npid[i] = pid; + } + return core_master_run(npid, &nprocess); +} + +/* 单进程模式 */ +static inline int cfadmin_standalone_run(const char* entry) { + return cfadmin_worker_run(entry); +} + +/* 设置为daemon进程, 并指定`entry`文件与`Pid`文件路径. */ +static inline pid_t cfadmin_daemon(int redirect) { + + pid_t pid = fork(); + if (pid != 0) + exit(EXIT_SUCCESS); + + /* 获取`Pid` */ + pid = getpid(); + + /* 设置`SID` */ + setsid(); + + /* 打开空设备 */ + int stdin_fd, stdout_fd; + + // 关闭标准输入输出 + if (!redirect) { + int nfd = open("/dev/null", O_RDWR); + if (nfd < 0) { + LOG("ERROR", strerror(errno)); + exit(-1); + } + stdin_fd = stdout_fd = nfd; + } else { + int rfd = open("/dev/null", O_RDWR); + if (rfd < 0) { + LOG("ERROR", strerror(errno)); + exit(-1); + } + int wfd = open("logs/cfadmin_stdout.log", O_APPEND); + if (wfd < 0) { + LOG("ERROR", strerror(errno)); + exit(-1); + } + stdin_fd = rfd; + stdout_fd = wfd; + } + + /* 关闭资源 */ + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + /* 重定向输入/输出 */ + (void)dup2(stdin_fd, STDIN_FILENO); + (void)dup2(stdout_fd, STDOUT_FILENO); + (void)dup2(stdout_fd, STDERR_FILENO); + + return pid; + +} + int main(int argc, char const *argv[]) { /* 命令行参数初始化 */ cfadmin_init_args(argc, argv); -#if defined(__MSYS__) +#if defined(__MSYS__) || defined(__CYGWIN__) /* Windows下不可使用多进程 */ nprocess = 1; #else @@ -177,72 +273,29 @@ int main(int argc, char const *argv[]) { nprocess = n; #endif - // /* 工作进程执行代码 */ - if(getenv("cfadmin_isWorker") && getenv("cfadmin_script")) { - const char* entry = getenv("cfadmin_script"); - cfadmin_unset_parameters(); - return core_worker_run(entry); - } + /* 工作进程执行代码 */ + if(getenv("cfadmin_isWorker") && getenv("cfadmin_script")) + return cfadmin_worker_run(getenv("cfadmin_script")); /* 主进程执行代码 */ - if (getenv("cfadmin_isMaster")) { - pid_t ppid = getpid(); - /* 所有子进程的PID数组 */ - pid_t npid[nprocess]; - /* 初始化工作进程命令行参数 */ - char *argp[] = { WorkerPrefix, NULL }; - cfadmin_set_parameters(isWorker); - int i; - for (i = 0; i < nprocess; i++){ - int pid = fork(); - if (pid <= 0) { - if (!pid) { - /* 启动工作进程 */ - int e = execvp("./cfadmin", (char *const *)argp); - if (e < 0) - LOG("ERROR", strerror(errno)); - } - int index; - for (index = 0; index < i; index ++) - kill(npid[index], SIGQUIT); - kill(ppid, SIGQUIT); - return -1; - } - npid[i] = pid; - } - /* 执行主进程事件循环 */ - return core_master_run(npid, &nprocess); - } + if (getenv("cfadmin_isMaster")) + return cfadmin_master_run(); - /* 是否需要后台运行 */ - pid_t p = getpid(); - if (daemoned){ - int ok = fork(); - if (ok != 0){ - exit(0); - } - /* 设置会话ID */ - setsid(); - /* 打开空设备 */ - int nfd = open("/dev/null", O_RDWR); - if (nfd < 0) { - LOG("ERROR", strerror(errno)); - exit(-1); - } + /* + 注意: + 下面为`cfadmin`检查配置的相关代码; 根据配置检查的实际情况决定是否需要使用多进程 + 多进程模型目前仅在Linux下可以利用到多核`Accept`, 其他环境仅用多进程来做为`Worker`使用. + */ - // 关闭标准输入输出 - int fd; - for (fd = 0; fd <= 2; fd++) - close(fd); + /* 是否需要后台运行 */ + pid_t p = daemoned ? cfadmin_daemon(1) : getpid() ; - /* 重定向输入输出 */ - (void)dup2(nfd, STDIN_FILENO); - (void)dup2(nfd, STDOUT_FILENO); - (void)dup2(nfd, STDERR_FILENO); + /*将主进程的PID写入到*/ + cfadmin_write_pid_file(pid_filename, p); - /* 获取fork后的进程PID */ - p = getpid(); - } + /* 如果是单进程模型, 就无需继续创建父子进程管理. */ + if (nprocess <= 1) + return cfadmin_standalone_run(script_entry); /* 初始化命令行参数 */ argv[0] = MasterPrefix; @@ -250,9 +303,6 @@ int main(int argc, char const *argv[]) { /* 设置环境变量 */ cfadmin_set_parameters(isMaster); - /*将主进程的PID写入到*/ - cfadmin_write_pid_file(pid_filename, p); - /* 执行代码*/ int e = execvp("./cfadmin", (char *const *)argv); if (e < 0) { @@ -262,4 +312,5 @@ int main(int argc, char const *argv[]) { /* 如果失败就删除pid文件*/ return remove(pid_filename); + } \ No newline at end of file From badb016514df07aac693c4e15b40f7decd1fdbbc Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 7 Jun 2021 21:53:25 +0800 Subject: [PATCH 750/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core.c b/src/core.c index cab1ff91..481dc4a6 100644 --- a/src/core.c +++ b/src/core.c @@ -71,7 +71,7 @@ static void EV_ERROR_CB(const char *msg){ pids[index] = -1; } } - /* 这里给SUCCESS的原因是方便时间循环出错后的打印 */ + /* 减少无效打印, 专注错误提示 */ return exit(EXIT_SUCCESS); } From 6c84cf6f233c6b23d2b001c5949beb5213eab353 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 8 Jun 2021 07:53:13 +0800 Subject: [PATCH 751/956] =?UTF-8?q?=E5=87=8F=E5=B0=91ws=E7=9A=84=E8=B5=84?= =?UTF-8?q?=E6=BA=90=E6=B6=88=E8=80=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 2 +- lualib/protocol/websocket/server.lua | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index 151a57e3..5d409ab4 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -297,7 +297,7 @@ local function Switch_Protocol(http, cls, sock, header, method, version, path, i return end http:tolog(101, path, header['X-Real-IP'] or ip, X_Forwarded_FORMAT(header['X-Forwarded-For'] or ip), method, req_time(start_time)) - return wsserver.start { cls = cls, args = form_argsencode(path), headers = header, sock = sock, ext = ext } + return wsserver.start(sock, cls, form_argsencode(path), header, ext) end local function send_header (sock, header) diff --git a/lualib/protocol/websocket/server.lua b/lualib/protocol/websocket/server.lua index 2d8a7760..bbf40e9d 100644 --- a/lualib/protocol/websocket/server.lua +++ b/lualib/protocol/websocket/server.lua @@ -88,12 +88,10 @@ end local Websocket = { __Version__ = 1.0 } -- Websocket Server 事件循环 -function Websocket.start(opt) - local sock = opt.sock - local ext = opt.ext +function Websocket.start(sock, obj, args, headers, ext) local w = ws:new { sock = sock, ext = ext } - local cls = opt.cls:new { ws = w, args = opt.args, headers = opt.headers } + local cls = obj:new { ws = w, args = args, headers = headers } local on_open = assert(type(cls.on_open) == 'function' and cls.on_open, "'on_open' method is not implemented.") local on_message = assert(type(cls.on_message) == 'function' and cls.on_message, "'on_message' method is not implemented.") local on_error = assert(type(cls.on_error) == 'function' and cls.on_error, "'on_error' method is not implemented.") From 250b8eaff250bd6db93131d9f852847c586cd68c Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 8 Jun 2021 21:17:28 +0800 Subject: [PATCH 752/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9cfadmin=E5=86=85?= =?UTF-8?q?=E7=BD=AE=E5=BA=93=E4=B8=BA=E5=B9=B6=E8=A1=8C=E7=BC=96=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 8 ++++---- luaclib/Makefile | 12 ------------ 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index 0212b47c..100d440c 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ RM = rm -rf default : @echo "=======================================" @echo "Please use 'make build' command to build it.." - @echo "Please use 'make rebuild' command to build it.." + @echo "Please use 'make rebuild' command to rebuild it.." @echo "Please use 'make clean' command to clean all." @echo "=======================================" @@ -15,8 +15,8 @@ default : build : @$(MAKE) -s -C src build - @$(MAKE) -s -C luaclib internal - @$(MAKE) -s -C luaclib 3part + @echo "********** Built-in core modules **********" + @cd luaclib && $(MAKE) -s internal 3part -j4 @$(MAKE) -s -C 3rd build rebuild : @@ -27,4 +27,4 @@ clean : @echo "********** Clean All Files **********" @echo "rm -rf cfadmin libcore luaclib/*.so 3rd/*.so" @$(RM) cfadmin cfadmin.exe libcore.so libcore.dll luaclib/*.so - @$(MAKE) -s -C 3rd clean + @$(MAKE) -s -C 3rd clean \ No newline at end of file diff --git a/luaclib/Makefile b/luaclib/Makefile index e955be6b..b146ebf6 100644 --- a/luaclib/Makefile +++ b/luaclib/Makefile @@ -14,8 +14,6 @@ LIBS += -L./ -L../ -L/usr/local/lib CFLAGS = -Wall -O3 -fPIC --shared -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib internal : - - @echo "********** Built-in modules **********" @echo "CC - lsys" @$(CC) -o sys.so src/lsys.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore @echo "CC - ludp" @@ -30,31 +28,21 @@ internal : @$(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore 3part : - - @echo "********** Third party modules **********" @echo "CC - lz" @$(MAKE) -C src/lz build - @echo "CC - lfs" @$(MAKE) -C src/lfs build - @echo "CC - lffi" @$(MAKE) -C src/lffi build - @echo "CC - lpbc" @$(MAKE) -C src/lpbc build - @echo "CC - lpeg" @$(MAKE) -C src/lpeg build - @echo "CC - lcrypt" @$(MAKE) -C src/lcrypt build - @echo "CC - lcjson" @$(MAKE) -C src/lcjson build - @echo "CC - lmsgpack" @$(MAKE) -C src/lmsgpack build - @echo "CC - lhttpparser" @$(MAKE) -C src/lhttpparser build From 4f6f3ea6b960f98ba8126ff3c7377cb434a91498 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 8 Jun 2021 22:20:55 +0800 Subject: [PATCH 753/956] =?UTF-8?q?=E5=9C=A8=E5=A4=9A=E8=BF=9B=E7=A8=8B?= =?UTF-8?q?=E7=8E=AF=E5=A2=83=E4=B8=8B,=E5=B0=86=E4=B8=BALua=E6=B3=A8?= =?UTF-8?q?=E5=85=A5Worker=E8=A1=A8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/core.c b/src/core.c index 481dc4a6..5e7007a7 100644 --- a/src/core.c +++ b/src/core.c @@ -111,6 +111,25 @@ void init_lua_libs(lua_State *L){ lua_pushlightuserdata(L, NULL); lua_rawset(L, -3); + /* worker */ + if (getenv("cfadmin_isWorker") || getenv("cfadmin_isMaster")) { + lua_pushliteral(L, "worker"); + lua_createtable(L, 0, 3); + lua_pushliteral(L, "id"); + lua_pushinteger(L, getpid() - getppid()); + lua_rawset(L, -3); + lua_pushliteral(L, "pid"); + lua_pushinteger(L, getpid()); + lua_rawset(L, -3); + lua_pushliteral(L, "ppid"); + lua_pushinteger(L, getppid()); + lua_rawset(L, -3); + lua_pushliteral(L, "nprocess"); + lua_pushinteger(L, atoi(getenv("cfadmin_nprocess"))); + lua_rawset(L, -3); + lua_rawset(L, -3); + } + lua_settop(L, 0); /* 注入lua搜索域 */ From 5723895750d5bc1d318abff2fe31d3553cc680f2 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 9 Jun 2021 00:52:46 +0800 Subject: [PATCH 754/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0aio.kill=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/laio.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/luaclib/src/laio.c b/luaclib/src/laio.c index a9dc9d21..a5b6b269 100644 --- a/luaclib/src/laio.c +++ b/luaclib/src/laio.c @@ -727,12 +727,11 @@ static int laio_popen(lua_State *L) { static int laio_kill(lua_State *L){ lua_Integer pid = luaL_checkinteger(L, 1); if (pid > 1 && getpid() != pid) - kill(pid, SIGKILL); + kill(pid, luaL_optinteger(L, 2, SIGKILL)); lua_pushboolean(L, 1); return 1; } - LUAMOD_API int luaopen_laio(lua_State* L){ // printf("主线程ID为: %d\n", pthread_self()); luaL_checkversion(L); From bb1965bdde4c11e7b0900c67b40b42a9556f68bc Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 13 Jun 2021 16:40:39 +0800 Subject: [PATCH 755/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=9D=99=E6=80=81?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=96=87=E4=BB=B6=E8=B7=AF=E5=8A=B2=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Router.lua | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/lualib/httpd/Router.lua b/lualib/httpd/Router.lua index 2f463f84..58894994 100644 --- a/lualib/httpd/Router.lua +++ b/lualib/httpd/Router.lua @@ -23,7 +23,6 @@ local pairs = pairs local error = error local ipairs = ipairs local tonumber = tonumber -local tostring = tostring local slash = '\x2f' -- '/' local slash2 = '\x2f\x2f' -- '//' @@ -56,7 +55,7 @@ function Router:toarray (v, t) return v end local array = new_tab(32, 0) - for str in v:gmatch("[^,%[%]%{%}]+") do + for str in splite(v, "[^,%[%]%{%}]+") do if not t or t == 'string[]' then array[#array+1] = str else @@ -130,16 +129,15 @@ function Router:hex_route (route) end -- 检查是路径回退是否超出静态文件根目录(是否合法路径.) -function Router:is_out_of_directory (paths) - local deep = 1 - for _, p in ipairs(paths) do - if p == point2 then +function Router:is_out_of_directory (path) + local deep = 1 + for r in splite(path, "/([^/#%?]+)") do + if r == point2 then deep = deep - 1 - elseif p ~= point then + elseif r ~= point then deep = deep + 1 end - -- 如果超出目录则直接返回 - if deep <= 0 then + if deep == 0 then return true end end @@ -150,9 +148,9 @@ end function Router:find (method, path) -- 检查是否能O(1)定位普通路由 path = url_decode(split(path, 1, (find(path, '?') or 0) - 1)) - local t = self.routes[self:to_route(path)] - if t then - return t.class, t.type + local r = self.routes[self:to_route(path)] + if r then + return r.class, r.type end -- 检查是否需要查找rest路由 if self.enable_rest then @@ -186,9 +184,8 @@ function Router:find (method, path) if method ~= 'GET' and method ~= 'HEAD' then return end - local tab = self:hex_route(path) -- 凡是超出静态文件根目录返回404. - if self:is_out_of_directory(tab) then + if self:is_out_of_directory(path) then return end -- 构建静态静态文件检查器 From 8a34f8d623a138ed247e0a23662456edc54bcf21 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Fri, 18 Jun 2021 00:25:16 +0800 Subject: [PATCH 756/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E7=9A=84=E9=94=99=E8=AF=AF=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_start.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/core_start.c b/src/core_start.c index 775c4ad3..06769f0e 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -23,6 +23,8 @@ static int daemoned = 0; static int pmode = 0; +static int nostd = 1; + #define MasterPrefix ("cfadmin - Manager Process :") #define WorkerPrefix ("cfadmin - Worker Process") @@ -206,7 +208,7 @@ static inline int cfadmin_standalone_run(const char* entry) { } /* 设置为daemon进程, 并指定`entry`文件与`Pid`文件路径. */ -static inline pid_t cfadmin_daemon(int redirect) { +static inline pid_t cfadmin_daemon(int nostd) { pid_t pid = fork(); if (pid != 0) @@ -222,7 +224,7 @@ static inline pid_t cfadmin_daemon(int redirect) { int stdin_fd, stdout_fd; // 关闭标准输入输出 - if (!redirect) { + if (!nostd) { int nfd = open("/dev/null", O_RDWR); if (nfd < 0) { LOG("ERROR", strerror(errno)); @@ -235,7 +237,7 @@ static inline pid_t cfadmin_daemon(int redirect) { LOG("ERROR", strerror(errno)); exit(-1); } - int wfd = open("logs/cfadmin_stdout.log", O_APPEND); + int wfd = open("logs/cfadmin_stdout.log", O_CREAT | O_TRUNC | O_WRONLY, 0644); if (wfd < 0) { LOG("ERROR", strerror(errno)); exit(-1); @@ -288,7 +290,7 @@ int main(int argc, char const *argv[]) { */ /* 是否需要后台运行 */ - pid_t p = daemoned ? cfadmin_daemon(1) : getpid() ; + pid_t p = daemoned ? cfadmin_daemon(nostd) : getpid() ; /*将主进程的PID写入到*/ cfadmin_write_pid_file(pid_filename, p); From 0c80ff208c4ed8b8facd92954f2bfafa76dae4e7 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Fri, 18 Jun 2021 21:20:47 +0800 Subject: [PATCH 757/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=86=85=E9=83=A8?= =?UTF-8?q?=E5=8D=8F=E7=A8=8B=E8=B0=83=E5=BA=A6=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/Co.lua | 303 ++++++++++++++++++++++++++++++----------- 1 file changed, 220 insertions(+), 83 deletions(-) diff --git a/lualib/internal/Co.lua b/lualib/internal/Co.lua index 66a32931..6436446c 100644 --- a/lualib/internal/Co.lua +++ b/lualib/internal/Co.lua @@ -1,118 +1,255 @@ +-- local task = require "task" +-- local new_tab = require("sys").new_tab + +-- local task_new = task.new +-- local task_stop = task.stop +-- local task_start = task.start + +-- local co_new = coroutine.create +-- local co_start = coroutine.resume +-- local co_wait = coroutine.yield +-- local co_status = coroutine.status +-- local co_self = coroutine.running + +-- local type = type +-- local assert = assert +-- local xpcall = xpcall +-- local error = error + +-- local insert = table.insert +-- local remove = table.remove + +-- local cos = new_tab(0, 1 << 10) + +-- local main_co = co_self() +-- local main_task = task_new() + +-- local TASK_POOL = new_tab(1 << 10, 0) + +-- local function task_pop() +-- return remove(TASK_POOL) or task_new() +-- end + +-- local function task_push(task) +-- return insert(TASK_POOL, task) +-- end + +-- local CO_POOL = new_tab(1 << 10, 0) + +-- local function co_pop(func) +-- local co = remove(CO_POOL) +-- if co then +-- return co +-- end +-- co = co_new(func) +-- co_start(co) +-- return co +-- end + +-- local function co_push(co) +-- return insert(CO_POOL, co) +-- end + +-- local function dbg (info) +-- return print(string.format("[%s] %s", os.date("%Y/%m/%d %H:%M:%S"), debug.traceback(co_self(), info, 2))) +-- end + +-- local function f() +-- while 1 do +-- local func, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 = co_wait() +-- xpcall(func, dbg, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) +-- local co, main = co_self() +-- if not main then +-- task_push(cos[co]) +-- co_push(co) +-- cos[co] = nil +-- end +-- end +-- end + +-- local Co = {} + +-- -- 创建协程 +-- function Co.new(f) +-- return co_new(f) +-- end + +-- -- 查找 +-- function Co.self() +-- return co_self() +-- end + +-- -- 让出 +-- function Co.wait() +-- local co = co_self() +-- assert(cos[co] or co == main_co, "非cf创建的协程不能让出执行权") +-- return co_wait() +-- end + +-- -- 启动 +-- function Co.spawn(func, ...) +-- if type(func) == "function" then +-- local co = co_pop(f) +-- cos[co] = task_pop() +-- return task_start(cos[co], co, func, ...) +-- end +-- error("Co Just Can spawn a Coroutine to run in sometimes.") +-- end + +-- -- 唤醒 +-- function Co.wakeup(co, ...) +-- assert(type(co) == 'thread', "试图传递一个非协程的类型的参数到wakeup内部.") +-- assert(co ~= co_self(), "不能唤醒当前正在执行的协程") +-- if main_co == co then +-- local status = co_status(co) +-- if status ~= 'suspended' then +-- return error('试图唤醒一个状态异常的协程') +-- end +-- return task_start(main_task, main_co, ...) +-- end +-- local t = assert(cos[co], "非cf创建的协程不能由cf来唤醒") +-- return task_start(t, co, ...) +-- end + +-- function Co.count() +-- return #CO_POOL, #TASK_POOL +-- end + +-- return Co + + +local require = require local task = require "task" -local new_tab = require("sys").new_tab - local task_new = task.new -local task_stop = task.stop local task_start = task.start -local co_new = coroutine.create -local co_start = coroutine.resume -local co_wait = coroutine.yield -local co_status = coroutine.status -local co_self = coroutine.running +local sys = require "sys" +local new_tab = sys.new_tab local type = type +local print = print +local ipairs = ipairs local assert = assert -local xpcall = xpcall -local error = error - -local insert = table.insert -local remove = table.remove +local select = select -local cos = new_tab(0, 1 << 10) - -local main_co = co_self() -local main_task = task_new() - -local TASK_POOL = new_tab(1 << 10, 0) - -local function task_pop() - return remove(TASK_POOL) or task_new() -end - -local function task_push(task) - return insert(TASK_POOL, task) -end +local os_date = os.date +local fmt = string.format +local dbg_traceback = debug.traceback -local CO_POOL = new_tab(1 << 10, 0) +local tpack = table.pack +local tunpack = table.unpack -local function co_pop(func) - local co = remove(CO_POOL) - if co then - return co - end - co = co_new(func) - co_start(co) - return co -end - -local function co_push(co) - return insert(CO_POOL, co) +local coroutine = coroutine +local co_new = coroutine.create +local co_start = coroutine.resume +local co_wait = coroutine.yield +local co_status = coroutine.status +local co_self = coroutine.running +local co_close = coroutine.close + +local main_co = nil +local main_task = nil +local empty_args = {} + +local co_num = 0 + +local co_map = new_tab(0, 1024) +co_map[co_self()] = true + +local co_wlist = new_tab(512, 0) + +local function co_wrapper() + return co_new(function () + local co_rlist = co_wlist + co_wlist = new_tab(512, 0) + while true do + for _, obj in ipairs(co_rlist) do + local ok, errinfo = co_start(obj.co, tunpack(obj.args or empty_args)) + -- 如果协程`执行出错`或`执行完毕`, 则去掉引用销毁 + if not ok or co_status(obj.co) ~= 'suspended' then + co_map[obj.co] = nil + -- 如果发生异常,则应该把异常打印出来. + if not ok then + print(fmt("[%s] [coroutine error] %s", os_date("%Y/%m/%d %H:%M:%S"), dbg_traceback(obj.co, errinfo, 1))) + end + -- 如果支持销毁协程, 则可以尝试回收资源. + if co_close then + co_close(obj.co) + end + co_num = co_num - 1 + end + end + -- 如果没有执行对象则应该放弃执行权. + -- 等待有任务之后再次唤醒后再执行 + if #co_wlist == 0 then + co_wait() + end + co_rlist = co_wlist + co_wlist = new_tab(512, 0) + end + end) end -local function dbg (info) - return print(string.format("[%s] %s", os.date("%Y/%m/%d %H:%M:%S"), debug.traceback(co_self(), info, 2))) +local function co_check_init() + -- 如果尚未初始化资源, 则优先初始化. + if not main_task and not main_co then + main_task = task_new() + main_co = co_wrapper() + end + -- 如果协程未启动, 则启动协程开始运行. + if co_status(main_co) == 'suspended' then + task_start(main_task, main_co) + end end -local function f() - while 1 do - local func, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 = co_wait() - xpcall(func, dbg, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) - local co, main = co_self() - if not main then - task_push(cos[co]) - co_push(co) - cos[co] = nil - end - end +local function co_add_queue(co, ...) + local len, args = select("#", ...), nil + if len > 0 then + args = tpack(...) + end + co_num = co_num + 1 + co_wlist[#co_wlist+1] = { co = co, args = args} end local Co = {} -- 创建协程 function Co.new(f) - return co_new(f) + return co_new(f) end --- 查找 +-- 获取协程 function Co.self() - return co_self() + return co_self() end --- 让出 +-- 让出协程 function Co.wait() - local co = co_self() - assert(cos[co] or co == main_co, "非cf创建的协程不能让出执行权") - return co_wait() + assert(co_map[co_self()], "[coroutine error]: This coroutine is not associated internally, so it cannot yield.") + return co_wait() end --- 启动 -function Co.spawn(func, ...) - if type(func) == "function" then - local co = co_pop(f) - cos[co] = task_pop() - return task_start(cos[co], co, func, ...) - end - error("Co Just Can spawn a Coroutine to run in sometimes.") +-- 唤醒协程 +function Co.wakeup(co, ...) + assert(type(co) == 'thread' and co ~= co_self() and co_map[co], "[coroutine error]: Invcalid coroutine.") + co_check_init() + co_add_queue(co, ...) end --- 唤醒 -function Co.wakeup(co, ...) - assert(type(co) == 'thread', "试图传递一个非协程的类型的参数到wakeup内部.") - assert(co ~= co_self(), "不能唤醒当前正在执行的协程") - if main_co == co then - local status = co_status(co) - if status ~= 'suspended' then - return error('试图唤醒一个状态异常的协程') - end - return task_start(main_task, main_co, ...) - end - local t = assert(cos[co], "非cf创建的协程不能由cf来唤醒") - return task_start(t, co, ...) +-- 创建协程 +function Co.spawn(func, ...) + assert(type(func) == "function", "[coroutine error]: Invalid callback.") + -- 创建协程与打包参数 + local co = co_new(func) + co_map[co] = true + co_check_init() + co_add_queue(co, ...) + return co end +-- 计算数量 function Co.count() - return #CO_POOL, #TASK_POOL + return co_num end -return Co +return Co \ No newline at end of file From 326edfd2d6edf14fccf70bcc1d045a5a43b2ef55 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 19 Jun 2021 02:43:14 +0800 Subject: [PATCH 758/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=8D=8F=E7=A8=8B?= =?UTF-8?q?=E8=B0=83=E5=BA=A6=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/Co.lua | 148 +++++------------------------------------ 1 file changed, 17 insertions(+), 131 deletions(-) diff --git a/lualib/internal/Co.lua b/lualib/internal/Co.lua index 6436446c..fa041a0d 100644 --- a/lualib/internal/Co.lua +++ b/lualib/internal/Co.lua @@ -1,123 +1,3 @@ --- local task = require "task" --- local new_tab = require("sys").new_tab - --- local task_new = task.new --- local task_stop = task.stop --- local task_start = task.start - --- local co_new = coroutine.create --- local co_start = coroutine.resume --- local co_wait = coroutine.yield --- local co_status = coroutine.status --- local co_self = coroutine.running - --- local type = type --- local assert = assert --- local xpcall = xpcall --- local error = error - --- local insert = table.insert --- local remove = table.remove - --- local cos = new_tab(0, 1 << 10) - --- local main_co = co_self() --- local main_task = task_new() - --- local TASK_POOL = new_tab(1 << 10, 0) - --- local function task_pop() --- return remove(TASK_POOL) or task_new() --- end - --- local function task_push(task) --- return insert(TASK_POOL, task) --- end - --- local CO_POOL = new_tab(1 << 10, 0) - --- local function co_pop(func) --- local co = remove(CO_POOL) --- if co then --- return co --- end --- co = co_new(func) --- co_start(co) --- return co --- end - --- local function co_push(co) --- return insert(CO_POOL, co) --- end - --- local function dbg (info) --- return print(string.format("[%s] %s", os.date("%Y/%m/%d %H:%M:%S"), debug.traceback(co_self(), info, 2))) --- end - --- local function f() --- while 1 do --- local func, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 = co_wait() --- xpcall(func, dbg, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) --- local co, main = co_self() --- if not main then --- task_push(cos[co]) --- co_push(co) --- cos[co] = nil --- end --- end --- end - --- local Co = {} - --- -- 创建协程 --- function Co.new(f) --- return co_new(f) --- end - --- -- 查找 --- function Co.self() --- return co_self() --- end - --- -- 让出 --- function Co.wait() --- local co = co_self() --- assert(cos[co] or co == main_co, "非cf创建的协程不能让出执行权") --- return co_wait() --- end - --- -- 启动 --- function Co.spawn(func, ...) --- if type(func) == "function" then --- local co = co_pop(f) --- cos[co] = task_pop() --- return task_start(cos[co], co, func, ...) --- end --- error("Co Just Can spawn a Coroutine to run in sometimes.") --- end - --- -- 唤醒 --- function Co.wakeup(co, ...) --- assert(type(co) == 'thread', "试图传递一个非协程的类型的参数到wakeup内部.") --- assert(co ~= co_self(), "不能唤醒当前正在执行的协程") --- if main_co == co then --- local status = co_status(co) --- if status ~= 'suspended' then --- return error('试图唤醒一个状态异常的协程') --- end --- return task_start(main_task, main_co, ...) --- end --- local t = assert(cos[co], "非cf创建的协程不能由cf来唤醒") --- return task_start(t, co, ...) --- end - --- function Co.count() --- return #CO_POOL, #TASK_POOL --- end - --- return Co - - local require = require local task = require "task" local task_new = task.new @@ -149,6 +29,7 @@ local co_close = coroutine.close local main_co = nil local main_task = nil +local main_waited = true local empty_args = {} local co_num = 0 @@ -159,29 +40,33 @@ co_map[co_self()] = true local co_wlist = new_tab(512, 0) local function co_wrapper() + -- 数字索引比字符串索引快. + local CO_INDEX, ARGS_INDEX = 1, 2 return co_new(function () local co_rlist = co_wlist co_wlist = new_tab(512, 0) while true do for _, obj in ipairs(co_rlist) do - local ok, errinfo = co_start(obj.co, tunpack(obj.args or empty_args)) + local co, args = obj[CO_INDEX], obj[ARGS_INDEX] + local ok, errinfo = co_start(co, tunpack(args or empty_args)) -- 如果协程`执行出错`或`执行完毕`, 则去掉引用销毁 - if not ok or co_status(obj.co) ~= 'suspended' then - co_map[obj.co] = nil + if not ok or co_status(co) ~= 'suspended' then -- 如果发生异常,则应该把异常打印出来. if not ok then - print(fmt("[%s] [coroutine error] %s", os_date("%Y/%m/%d %H:%M:%S"), dbg_traceback(obj.co, errinfo, 1))) + print(fmt("[%s] [coroutine error] %s", os_date("%Y/%m/%d %H:%M:%S"), dbg_traceback(co, errinfo, 1))) end -- 如果支持销毁协程, 则可以尝试回收资源. if co_close then - co_close(obj.co) + co_close(co) end + co_map[co] = nil co_num = co_num - 1 end end -- 如果没有执行对象则应该放弃执行权. -- 等待有任务之后再次唤醒后再执行 if #co_wlist == 0 then + main_waited = true co_wait() end co_rlist = co_wlist @@ -197,18 +82,18 @@ local function co_check_init() main_co = co_wrapper() end -- 如果协程未启动, 则启动协程开始运行. - if co_status(main_co) == 'suspended' then + if main_waited then + main_waited = false task_start(main_task, main_co) end end local function co_add_queue(co, ...) - local len, args = select("#", ...), nil - if len > 0 then + local args = nil + if select("#", ...) > 0 then args = tpack(...) end - co_num = co_num + 1 - co_wlist[#co_wlist+1] = { co = co, args = args} + co_wlist[#co_wlist+1] = {co, args} end local Co = {} @@ -238,8 +123,9 @@ end -- 创建协程 function Co.spawn(func, ...) - assert(type(func) == "function", "[coroutine error]: Invalid callback.") + assert(type(func) == 'function', "[coroutine error]: Invalid callback.") -- 创建协程与打包参数 + co_num = co_num + 1 local co = co_new(func) co_map[co] = true co_check_init() From 94ca9e7bb77506c98c2529e0473248d936b11aae Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 19 Jun 2021 10:50:12 +0800 Subject: [PATCH 759/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E7=AB=AF=E5=8F=91=E5=B8=83ALPN=E7=9A=84=E7=89=B9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/TCP.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index ff49595a..08bb7906 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -552,6 +552,7 @@ local function ssl_accept(callback, fd, ipaddr, port, opt) sock:ssl_set_password(opt.pw) end sock.mode = "server" + sock:ssl_set_alpn(opt.alpn) sock:ssl_set_certificate(opt.cert) sock:ssl_set_privatekey(opt.key) tcp_ssl_set_accept_mode(sock.ssl, sock.ssl_ctx) @@ -583,10 +584,11 @@ function TCP:listen_ssl(ip, port, opt, cb) if type(cb) ~= 'function' then return nil, "Listen function was invalid." end + local sslopt = { timeout = self.timeout, alpn = self.alpn, cert = opt.cert, key = opt.key, pw = opt.pw } self.listen_ssl_co = co_new(function (fd, ipaddr, port) while 1 do if fd and ipaddr then - co_spawn(ssl_accept, cb, fd, ipaddr, port, opt) + co_spawn(ssl_accept, cb, fd, ipaddr, port, sslopt) fd, ipaddr, port = co_wait() end end From d7630f7765f2b60b5d080e239a763790af31b944 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 21 Jun 2021 20:49:54 +0800 Subject: [PATCH 760/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=8D=8F=E7=A8=8B?= =?UTF-8?q?=E8=B0=83=E5=BA=A6=E4=B8=8E=E8=B5=84=E6=BA=90=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E6=95=88=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/Co.lua | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/lualib/internal/Co.lua b/lualib/internal/Co.lua index fa041a0d..a37901f7 100644 --- a/lualib/internal/Co.lua +++ b/lualib/internal/Co.lua @@ -8,7 +8,6 @@ local new_tab = sys.new_tab local type = type local print = print -local ipairs = ipairs local assert = assert local select = select @@ -30,7 +29,6 @@ local co_close = coroutine.close local main_co = nil local main_task = nil local main_waited = true -local empty_args = {} local co_num = 0 @@ -40,15 +38,19 @@ co_map[co_self()] = true local co_wlist = new_tab(512, 0) local function co_wrapper() - -- 数字索引比字符串索引快. - local CO_INDEX, ARGS_INDEX = 1, 2 return co_new(function () + -- 使用`数字索引`比`Hash索引`更快. + local CO_INDEX, ARGS_INDEX = 1, 2 + -- 使用数字下标迭代比`ipairs`更快. + local start, total = 1, #co_wlist + -- 使用两级`FIFO`队列交替管理协程的运行与切换, 并且每次预分配的`FIFO`队列的大小与上次执行的协程的数量相关. local co_rlist = co_wlist - co_wlist = new_tab(512, 0) + co_wlist = new_tab((total & ~3) + 4, 0) while true do - for _, obj in ipairs(co_rlist) do + for index = start, total do + local obj = co_rlist[index] local co, args = obj[CO_INDEX], obj[ARGS_INDEX] - local ok, errinfo = co_start(co, tunpack(args or empty_args)) + local ok, errinfo; if args then ok, errinfo = co_start(co, tunpack(args)); else ok, errinfo = co_start(co); end -- 如果协程`执行出错`或`执行完毕`, 则去掉引用销毁 if not ok or co_status(co) ~= 'suspended' then -- 如果发生异常,则应该把异常打印出来. @@ -65,12 +67,14 @@ local function co_wrapper() end -- 如果没有执行对象则应该放弃执行权. -- 等待有任务之后再次唤醒后再执行 - if #co_wlist == 0 then + total = #co_wlist + if total == 0 then main_waited = true co_wait() + total = #co_wlist end co_rlist = co_wlist - co_wlist = new_tab(512, 0) + co_wlist = new_tab((total & ~3) + 4, 0) end end) end From 5d6663db25d02dfa69d425d805650e38994e1e91 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 22 Jun 2021 22:24:50 +0800 Subject: [PATCH 761/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E6=97=B6logs=E7=9B=AE=E5=BD=95=E4=B8=8D?= =?UTF-8?q?=E5=AD=98=E5=9C=A8=E7=9A=84=E9=94=99=E8=AF=AF.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_start.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core_start.c b/src/core_start.c index 06769f0e..50a629eb 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -237,7 +237,7 @@ static inline pid_t cfadmin_daemon(int nostd) { LOG("ERROR", strerror(errno)); exit(-1); } - int wfd = open("logs/cfadmin_stdout.log", O_CREAT | O_TRUNC | O_WRONLY, 0644); + int wfd = open("cfadmin_stdout.log", O_CREAT | O_WRONLY | O_APPEND, 0644); if (wfd < 0) { LOG("ERROR", strerror(errno)); exit(-1); From 6689d4fbd476aced3281fb5a4370e38d3ed79ab7 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 23 Jun 2021 00:49:41 +0800 Subject: [PATCH 762/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=BB=91=E5=AE=9ACPU?= =?UTF-8?q?=E7=AD=96=E7=95=A5.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_start.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/core_start.c b/src/core_start.c index 50a629eb..e408211a 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -29,7 +29,7 @@ static int nostd = 1; #define WorkerPrefix ("cfadmin - Worker Process") /* 打印使用指南 */ -void cfadmin_usage_print() { +static inline void cfadmin_usage_print() { printf("cfadmin System : %s(%s)\n", __OS__, __VERSION__ ); printf("\n"); printf("cfadmin Version : %s\n", __CFADMIN_VERSION__ ); @@ -96,7 +96,7 @@ static inline void cfadmin_specify_nprocess(const char* w) { } /* 后台运行 */ -void cfadmin_specify_process_daemon() { +static inline void cfadmin_specify_process_daemon() { daemoned = 1; } @@ -197,6 +197,14 @@ static inline pid_t cfadmin_master_run() { kill(ppid, SIGQUIT); return (pid_t)-1; } +#ifdef __linux__ + #include + if (nprocess <= sysconf(_SC_NPROCESSORS_ONLN)){ + /* 在多进程环境下, Linux会尝试绑定到多个CPU上. */ + cpu_set_t mask; CPU_ZERO(&mask); CPU_SET((i + 1) % nprocess, &mask); + sched_setaffinity(pid, sizeof(mask), (const cpu_set_t *)&mask); + } +#endif npid[i] = pid; } return core_master_run(npid, &nprocess); From 507b9ff51685f662421b99a8337ea05850c84436 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 23 Jun 2021 00:57:31 +0800 Subject: [PATCH 763/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=9D=A1=E4=BB=B6?= =?UTF-8?q?=E5=AE=8F=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_start.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core_start.c b/src/core_start.c index e408211a..05370501 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -198,6 +198,10 @@ static inline pid_t cfadmin_master_run() { return (pid_t)-1; } #ifdef __linux__ +#ifndef __USE_GNU + /* Linux可能会被忽略此宏导致无法编译通过. */ + #define __USE_GNU 1 +#endif #include if (nprocess <= sysconf(_SC_NPROCESSORS_ONLN)){ /* 在多进程环境下, Linux会尝试绑定到多个CPU上. */ From c192d329af55b20dff0f1407b832bf71b6eaa43d Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 23 Jun 2021 01:04:27 +0800 Subject: [PATCH 764/956] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_start.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core_start.c b/src/core_start.c index 05370501..66efd083 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -204,7 +204,7 @@ static inline pid_t cfadmin_master_run() { #endif #include if (nprocess <= sysconf(_SC_NPROCESSORS_ONLN)){ - /* 在多进程环境下, Linux会尝试绑定到多个CPU上. */ + /* 在Linux会尝试绑定CPU亲缘性以提高进程执行效率. */ cpu_set_t mask; CPU_ZERO(&mask); CPU_SET((i + 1) % nprocess, &mask); sched_setaffinity(pid, sizeof(mask), (const cpu_set_t *)&mask); } From caa5c28c42649d5f9dc5a7f2f0fa744640e43ad7 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 23 Jun 2021 02:19:27 +0800 Subject: [PATCH 765/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_start.c | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/core_start.c b/src/core_start.c index 66efd083..1a1af681 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -157,6 +157,27 @@ static inline void cfadmin_set_parameters(int mode) { } } +/* 设置不同系统下的CPU亲缘性 */ +static inline void cfadmin_set_cpu_affinity(int num, pid_t pid) { +#if defined(__linux__) + #ifndef __USE_GNU + /* Linux可能会被忽略此宏导致无法编译通过. */ + #define __USE_GNU 1 + #endif + #include + if (nprocess <= sysconf(_SC_NPROCESSORS_ONLN)){ + /* 在Linux会尝试绑定CPU亲缘性以提高进程执行效率. */ + cpu_set_t mask; CPU_ZERO(&mask); CPU_SET((num + 1) % nprocess, &mask); + sched_setaffinity(pid, sizeof(mask), (const cpu_set_t *)&mask); + } +#elif defined(__FreeBSD__) + #include + #include + cpuset_t mask; CPU_ZERO(&mask); CPU_SET((num + 1) % nprocess, &mask); + cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(cpuset_t), (const cpuset_t *)&mask); +#endif +} + /* 初始化参数 */ static inline void cfadmin_unset_parameters() { unsetenv("cfadmin_isMaster"); @@ -187,6 +208,7 @@ static inline pid_t cfadmin_master_run() { if (pid <= 0) { if (!pid) { /* 启动工作进程 */ + cfadmin_set_cpu_affinity(i, getpid()); int e = execvp("./cfadmin", (char *const *)argp); if (e < 0) LOG("ERROR", strerror(errno)); @@ -197,18 +219,6 @@ static inline pid_t cfadmin_master_run() { kill(ppid, SIGQUIT); return (pid_t)-1; } -#ifdef __linux__ -#ifndef __USE_GNU - /* Linux可能会被忽略此宏导致无法编译通过. */ - #define __USE_GNU 1 -#endif - #include - if (nprocess <= sysconf(_SC_NPROCESSORS_ONLN)){ - /* 在Linux会尝试绑定CPU亲缘性以提高进程执行效率. */ - cpu_set_t mask; CPU_ZERO(&mask); CPU_SET((i + 1) % nprocess, &mask); - sched_setaffinity(pid, sizeof(mask), (const cpu_set_t *)&mask); - } -#endif npid[i] = pid; } return core_master_run(npid, &nprocess); From e1d026480e4876ed49cf8a129d79da4efd7da009 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 23 Jun 2021 03:02:40 +0800 Subject: [PATCH 766/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AF=B9FreeBSD?= =?UTF-8?q?=E4=B8=8ELinux=E7=9A=84CPU=E4=BA=B2=E7=BC=98=E6=80=A7=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E6=A3=80=E6=9F=A5=E4=B8=8E=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_start.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/core_start.c b/src/core_start.c index 1a1af681..f6dc9870 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -160,21 +160,22 @@ static inline void cfadmin_set_parameters(int mode) { /* 设置不同系统下的CPU亲缘性 */ static inline void cfadmin_set_cpu_affinity(int num, pid_t pid) { #if defined(__linux__) - #ifndef __USE_GNU - /* Linux可能会被忽略此宏导致无法编译通过. */ - #define __USE_GNU 1 - #endif - #include - if (nprocess <= sysconf(_SC_NPROCESSORS_ONLN)){ - /* 在Linux会尝试绑定CPU亲缘性以提高进程执行效率. */ - cpu_set_t mask; CPU_ZERO(&mask); CPU_SET((num + 1) % nprocess, &mask); - sched_setaffinity(pid, sizeof(mask), (const cpu_set_t *)&mask); - } +#ifndef __USE_GNU + /* Linux可能会被忽略此宏导致无法编译通过. */ + #define __USE_GNU 1 +#endif + #include + if (nprocess <= sysconf(_SC_NPROCESSORS_ONLN)){ + /* 在Linux会尝试绑定CPU亲缘性以提高进程执行效率. */ + cpu_set_t mask; CPU_ZERO(&mask); CPU_SET((num + 1) % nprocess, &mask); + sched_setaffinity(pid, sizeof(mask), (const cpu_set_t *)&mask); + } #elif defined(__FreeBSD__) #include #include cpuset_t mask; CPU_ZERO(&mask); CPU_SET((num + 1) % nprocess, &mask); - cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(cpuset_t), (const cpuset_t *)&mask); + cpuset_setaffinity(CPU_LEVEL_W + HICH, CPU_WHICH_PID, -1, sizeof(cpuset_t), (const cpuset_t *)&mask); #endif } From ed833501c51f4e1c2f22972e657e1098103fc243 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 24 Jun 2021 00:54:58 +0800 Subject: [PATCH 767/956] =?UTF-8?q?=E8=B0=83=E6=95=B4zlib=E5=BA=93?= =?UTF-8?q?=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lz/lzlib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/luaclib/src/lz/lzlib.c b/luaclib/src/lz/lzlib.c index 4c62abe4..476e73eb 100644 --- a/luaclib/src/lz/lzlib.c +++ b/luaclib/src/lz/lzlib.c @@ -36,7 +36,7 @@ static int luncompress(lua_State *L) { if (in_size <= 0) return 0; - size_t out_size = in_size; + size_t out_size = in_size * 4; /* 若能传递压缩前的大小, 优先使用此数值 */ int is_sum = 0; @@ -44,7 +44,7 @@ static int luncompress(lua_State *L) { if (is_sum && before_size > 0 ) out_size = before_size; - size_t offset = 1; + size_t offset = 4; size_t top = lua_gettop(L); do { From 2e622714fd0f04805acd80d8689543b191e146e9 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 24 Jun 2021 00:57:18 +0800 Subject: [PATCH 768/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_zlib.lua | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/script/test_zlib.lua b/script/test_zlib.lua index 9319b45b..a9cd1f89 100644 --- a/script/test_zlib.lua +++ b/script/test_zlib.lua @@ -48,8 +48,19 @@ local raw_text = z.uncompress(cp_text) assert(raw_text == text, "测试LZ77压缩/解压方法失败") -LOG:DEBUG("压缩前的文本长度为:" .. #raw_text, "压缩后的文本长度为:" .. #cp_text) +LOG:DEBUG("compress压缩前的文本长度为:" .. #raw_text, "压缩后的文本长度为:" .. #cp_text) +-- 分割线 --- + +local cp_text = z.compress2(text) + +local raw_text = z.uncompress2(cp_text) + +assert(raw_text == text, "测试gzip压缩/解压失败") + +LOG:DEBUG("compress2压缩前的文本长度为:" .. #raw_text, "压缩后的文本长度为:" .. #cp_text) + +-- 分割线 --- local cp_text = z.gzcompress(text) @@ -57,4 +68,4 @@ local raw_text = z.gzuncompress(cp_text) assert(raw_text == text, "测试gzip压缩/解压失败") -LOG:DEBUG("压缩前的文本长度为:" .. #raw_text, "压缩后的文本长度为:" .. #cp_text) \ No newline at end of file +LOG:DEBUG("gzcompress压缩前的文本长度为:" .. #raw_text, "压缩后的文本长度为:" .. #cp_text) \ No newline at end of file From 0990488abdd1e7b52aac11e248ec26ac8a04d190 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 24 Jun 2021 01:05:58 +0800 Subject: [PATCH 769/956] =?UTF-8?q?=E4=BC=98=E5=8C=96CPU=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E8=A1=A8=E8=BE=BE=E5=BC=8F=E4=B8=8E=E6=B3=A8=E9=87=8A=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_start.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core_start.c b/src/core_start.c index f6dc9870..5b7d9406 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -173,9 +173,9 @@ static inline void cfadmin_set_cpu_affinity(int num, pid_t pid) { #elif defined(__FreeBSD__) #include #include + /* 在FreeBSD内支持CPU绑定的方式有差异 */ cpuset_t mask; CPU_ZERO(&mask); CPU_SET((num + 1) % nprocess, &mask); - cpuset_setaffinity(CPU_LEVEL_W - HICH, CPU_WHICH_PID, -1, sizeof(cpuset_t), (const cpuset_t *)&mask); + cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(cpuset_t), (const cpuset_t *)&mask); #endif } @@ -246,7 +246,7 @@ static inline pid_t cfadmin_daemon(int nostd) { /* 打开空设备 */ int stdin_fd, stdout_fd; - // 关闭标准输入输出 + /* 关闭标准输入输出 */ if (!nostd) { int nfd = open("/dev/null", O_RDWR); if (nfd < 0) { From 47d91b4ba251edb2c3834d813479f727b3a5e650 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 24 Jun 2021 01:09:20 +0800 Subject: [PATCH 770/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=8D=E5=90=8C?= =?UTF-8?q?=E5=B9=B3=E5=8F=B0=E4=B8=8B=E7=9A=84CPU=E4=BA=B2=E7=BC=98?= =?UTF-8?q?=E6=80=A7=E7=BB=91=E5=AE=9A=E5=88=A4=E6=96=AD=E4=B8=8D=E4=B8=80?= =?UTF-8?q?=E8=87=B4=E7=9A=84=E9=97=AE=E9=A2=98.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_start.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/core_start.c b/src/core_start.c index 5b7d9406..4a68d58c 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -173,9 +173,11 @@ static inline void cfadmin_set_cpu_affinity(int num, pid_t pid) { #elif defined(__FreeBSD__) #include #include - /* 在FreeBSD内支持CPU绑定的方式有差异 */ - cpuset_t mask; CPU_ZERO(&mask); CPU_SET((num + 1) % nprocess, &mask); - cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(cpuset_t), (const cpuset_t *)&mask); + if (nprocess <= sysconf(_SC_NPROCESSORS_ONLN)){ + /* FreeBSD的CPU绑定的方式有函数与头文件的差异 */ + cpuset_t mask; CPU_ZERO(&mask); CPU_SET((num + 1) % nprocess, &mask); + cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(cpuset_t), (const cpuset_t *)&mask); + } #endif } From e6a20d827e47ec1fea988cd5fd23699fca7c233a Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 28 Jun 2021 21:44:57 +0800 Subject: [PATCH 771/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0Class=E5=BA=93?= =?UTF-8?q?=E7=9A=84=E4=B8=80=E4=BA=9B=E7=89=B9=E6=80=A7.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/class/init.lua | 55 +++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/lualib/class/init.lua b/lualib/class/init.lua index de896b1c..8d916a54 100644 --- a/lualib/class/init.lua +++ b/lualib/class/init.lua @@ -1,30 +1,35 @@ local type = type +local assert = assert +local getmetatable = getmetatable local setmetatable = setmetatable -local new_tab = require "sys".new_tab --- 一个精简版的类实现 -return function (cls_name) - local cls = {} - cls.__name = cls_name - cls.__index = cls - cls.__call = function (c, ...) - local call = c[cls_name] - if type(call) ~= 'function' then - return - end - return call(c, ...) - end - cls.new = function (c, ...) - if cls ~= c then - return print("Please use ':' to create new object :)") - end - local obj = new_tab(0, 16) - local ctor = cls.ctor - if not ctor then - return print("Can't find ctor to init.") - end - ctor(obj, ...) - return setmetatable(obj, cls) +-- 所有对象的基类 +local META = { __META_CLASS__ = true } + +META.__index = META + +META.new = function (M, ...) + assert(META == getmetatable(M), "[Lua-CLASS ERROR]: Must use `:` to create object.") + local ctor = M.ctor + local obj = setmetatable({}, M) + assert(ctor and type(ctor) == 'function' and ctor, "[Lua-CLASS ERROR]: Can't find `ctor` to init.")(obj, ...) + return obj +end + +META.__call = function (M, ...) + local meta = getmetatable(M) + if meta == META then + return M:new(...); end - return cls + return assert(getmetatable(M) == META and M['__name'], "[Lua-CLASS ERROR]: Invalid class arguments.")(M, ...); +end + +---comment `Class`是内部的所有对象用来实现面向对象的方法. +---@param cname? string @自定义类名 +---@param meta? table | function | nil @自定义类行为 +---@return table @返回一个类 +return function (cname, meta) + local cls = { __name = cname } + cls.__index = meta or cls + return setmetatable(cls, META) end \ No newline at end of file From e3e6487e50cfb6c1d8ab33df65c1ed54063704bc Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 28 Jun 2021 22:34:49 +0800 Subject: [PATCH 772/956] =?UTF-8?q?=E4=BC=98=E5=8C=96Logging=E5=BA=93?= =?UTF-8?q?=E7=9A=84=E8=BE=93=E5=87=BA=E5=BD=A2=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/logging/init.lua | 53 ++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 32 deletions(-) diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua index fd6eb62e..516a5d22 100644 --- a/lualib/logging/init.lua +++ b/lualib/logging/init.lua @@ -60,42 +60,31 @@ local function debuginfo () end -- 格式化 -local function table_format(t) - local tab = new_tab(16, 0) - while 1 do - local mt = getmetatable(t) - for key, value in pairs(t) do - local k, v - if type(key) == 'number' then - k = concat({'[', key, ']'}) - elseif type(key) == 'string' then - k = concat({'["', key, '"]'}) - else - k = concat({'[', tostring(key), ']'}) - end - if type(value) == 'table' then - if t ~= value then - v = table_format(value) - else - if type(value) == 'table' then - v = table_format(value) - else - v = tostring(value) - end - end - elseif type(value) == 'string' then - v = concat({'"', value, '"'}) - else - v = tostring(value) +local function table_format(tab) + local list = {} + for key, value in pairs(tab) do + local k, v + if type(key) == 'number' then + k = concat({'[', key, ']'}) + elseif type(key) == 'string' then + k = concat({'["', key, '"]'}) + else + k = concat({'[', tostring(key), ']'}) + end + if type(value) == 'table' then + if key ~= '__index' then + v = table_format(value) end - tab[#tab+1] = concat({k, '=', v}) + elseif type(value) == 'string' then + v = concat({'"', value, '"'}) + elseif value then + v = tostring(value) end - if not mt or mt == t then - break + if k and v then + list[#list+1] = concat({k, '=', v}) end - t = mt end - return concat({'{', concat(tab, ', '), '}'}) + return concat({tab.__name or "", '{', concat(list, ', '), '}'}) end local function info_fmt(...) From 2c61bcaede8792c5cb230add09e48c192fed938e Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 29 Jun 2021 09:13:41 +0800 Subject: [PATCH 773/956] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E6=97=A0=E7=94=A8?= =?UTF-8?q?=E7=9A=84=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/logging/init.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua index 516a5d22..228a6325 100644 --- a/lualib/logging/init.lua +++ b/lualib/logging/init.lua @@ -18,7 +18,6 @@ local select = select local assert = assert local pairs = pairs local tostring = tostring -local getmetatable = getmetatable local modf = math.modf local toint = math.tointeger From e45c1ea1bc547ed2a0449b33ee7646bbab1b43c7 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 30 Jun 2021 22:35:29 +0800 Subject: [PATCH 774/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E7=89=B9=E6=AE=8A=E6=83=85=E5=86=B5=E4=B8=8B=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E8=8E=B7=E5=8F=96debug=E4=BF=A1=E6=81=AF=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/logging/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua index 228a6325..63bd0a38 100644 --- a/lualib/logging/init.lua +++ b/lualib/logging/init.lua @@ -54,7 +54,7 @@ end -- LOG函数的调用信息 local function debuginfo () - local info = debug_getinfo(3, 'Sln') + local info = debug_getinfo(3, 'Sln') or debug_getinfo(2, 'Sln') or debug_getinfo(1, 'Sln') return concat({'[', info.source, ':', info.currentline, ']'}) end From 9fbd0a769b9fb78a10c2607f2257396ba61788fb Mon Sep 17 00:00:00 2001 From: "Mr.che" <869646063@qq.com> Date: Thu, 1 Jul 2021 16:58:20 +0800 Subject: [PATCH 775/956] Update laio.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `aio.popen`需要清除这边继承的一些信号与会话 --- luaclib/src/laio.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/luaclib/src/laio.c b/luaclib/src/laio.c index a5b6b269..8a9ffa0c 100644 --- a/luaclib/src/laio.c +++ b/luaclib/src/laio.c @@ -662,6 +662,12 @@ static pid_t laio_system(lua_State *L, const char* command, int pfd) { if (pid < 0) return (pid_t)-1; if (pid < 1) { + // 完整独立进程会话ID + setsid(); + // 重置子进程的信号掩码 + signal(SIGINT, SIG_DFL); signal(SIGHUP, SIG_DFL); + signal(SIGTERM, SIG_DFL); signal(SIGPIPE, SIG_DFL); + signal(SIGTSTP, SIG_DFL); signal(SIGQUIT, SIG_DFL); // 子进程需要设置为独立的输入输出管道; (void)dup2(pfd, STDIN_FILENO); (void)dup2(pfd, STDOUT_FILENO); From 086e9dd91ccf62cc93c9fc6acbaa0aa260eafa6c Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 1 Jul 2021 23:09:36 +0800 Subject: [PATCH 776/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0connectx=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E8=BF=9E=E6=8E=A5=E5=88=B0unix=20domain=20socket.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 53 +++++++++++++++++++++++++++++++++++++---- lualib/internal/TCP.lua | 32 ++++++++++++++++--------- 2 files changed, 70 insertions(+), 15 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index b20aa9a3..50df916c 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -252,7 +252,7 @@ static int create_client_fd(const char *ipaddr, int port){ static int create_server_unixsock(const char* path, size_t path_len, int backlog) { errno = 0; - int sockfd = socket(AF_LOCAL, SOCK_STREAM, IPPROTO_IP); + int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0); if (0 >= sockfd){ LOG("ERROR", strerror(errno)); return -1; @@ -285,6 +285,32 @@ static int create_server_unixsock(const char* path, size_t path_len, int backlog return sockfd; } +static int create_client_unixsock(const char* path, size_t path_len) { + errno = 0; + int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0); + if (0 >= sockfd){ + LOG("ERROR", strerror(errno)); + return -1; + } + + struct sockaddr_un UN; + memset(&UN, 0x0, sizeof(UN)); + + UN.sun_family = AF_LOCAL; + memmove(UN.sun_path, path, path_len); + + non_blocking(sockfd); + + int ret = connect(sockfd, (struct sockaddr*)&UN, sizeof(UN)); + if (0 > ret) { + LOG("ERROR", strerror(errno)); + close(sockfd); + return -1; + } + + return sockfd; +} + static void TCP_IO_CB(CORE_P_ core_io *io, int revents) { if (revents & EV_ERROR) { LOG("ERROR", "Recevied a core_io object internal error from libev."); @@ -697,10 +723,10 @@ static int new_client_fd(lua_State *L){ return 1; } -static int new_unixsock_fd(lua_State *L) { +static int new_server_unixsock_fd(lua_State *L) { size_t size = 0; const char* path = luaL_checklstring(L, 1, &size); - if (!path) + if (!path || size < 2) return 0; /* 传递rm为非nil与false值, 删除已经存在的文件 */ @@ -722,6 +748,24 @@ static int new_unixsock_fd(lua_State *L) { return 1; } +static int new_client_unixsock_fd(lua_State *L){ + size_t size = 0; + const char* path = luaL_checklstring(L, 1, &size); + if (!path || size < 2) + return 0; + + // 如果文件不存在返回失败 + if (access(path, F_OK)) + return 0; + + int fd = create_client_unixsock(path, size); + if (fd <= 0) + return 0; + + lua_pushinteger(L, fd); + return 1; +} + static int tcp_listen(lua_State *L){ core_io *io = (core_io *) luaL_testudata(L, 1, "__TCP__"); if (!io) @@ -1158,7 +1202,8 @@ LUAMOD_API int luaopen_tcp(lua_State *L){ {"free_ssl", ssl_free}, {"new_server_fd", new_server_fd}, {"new_client_fd", new_client_fd}, - {"new_unixsock_fd", new_unixsock_fd}, + {"new_server_unixsock_fd", new_server_unixsock_fd}, + {"new_client_unixsock_fd", new_client_unixsock_fd}, {"sendfile", tcp_sendfile}, {"ssl_verify", ssl_verify}, {"ssl_set_alpn", ssl_set_alpn}, diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index 08bb7906..cffe74b2 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -48,7 +48,8 @@ local tcp_sslpeek = tcp.sslpeek local tcp_new_client_fd = tcp.new_client_fd local tcp_new_server_fd = tcp.new_server_fd -local tcp_new_unixsock_fd = tcp.new_unixsock_fd +local tcp_new_sever_unixsock_fd = tcp.new_server_unixsock_fd +local tcp_new_client_unixsock_fd = tcp.new_client_unixsock_fd local tcp_ssl_verify = tcp.ssl_verify local tcp_ssl_set_fd = tcp.ssl_set_fd @@ -599,7 +600,7 @@ end function TCP:listen_ex(unix_domain_path, removed, cb) self.mode = "server" self.LISTEN_EX_IO = tcp_pop() - self.ufd = tcp_new_unixsock_fd(unix_domain_path, removed or true, self._backlog or 128) + self.ufd = tcp_new_sever_unixsock_fd(unix_domain_path, removed or true, self._backlog or 128) if not self.ufd then return nil, "Listen_ex unix domain socket failed. Please check the domain_path was exists and access." end @@ -617,15 +618,24 @@ function TCP:listen_ex(unix_domain_path, removed, cb) return true, tcp_listen_ex(self.LISTEN_EX_IO, self.ufd, self.listen_ex_co) end +function TCP:connectx(path) + self.mode = "client" + self.fd = tcp_new_client_unixsock_fd(assert(type(path) == 'string' and path, "Invalid unix domain path.")) + if not self.fd then + return nil, "Connect to unix domain socket failed." + end + return true +end + function TCP:connect(domain, port) self.mode = "client" local ok, IP = dns_resolve(domain) if not ok then - return nil, "Can't resolve this domain or ip:"..(domain or IP or "") + return nil, "Can't resolve this domain or ip:" .. (domain or IP or "") end self.fd = tcp_new_client_fd(IP, port) if not self.fd then - return nil, "Connect This host fault! "..(domain or "no domain")..":"..(port or "no port") + return nil, "Connect This host fault! "..(domain or "no domain")..":"..(port or "no port") end local co = co_self() self.CONNECT_IO = tcp_pop() @@ -643,13 +653,13 @@ function TCP:connect(domain, port) return co_wakeup(co, connected, errinfo) end) self.timer = ti_timeout(self._timeout, function () - tcp_push(self.CONNECT_IO) - tcp_stop(self.CONNECT_IO) - self.timer = nil - self.CONNECT_IO = nil - self.connect_co = nil - self.connect_current_co = nil - return co_wakeup(co, nil, 'connect timeout.') + tcp_push(self.CONNECT_IO) + tcp_stop(self.CONNECT_IO) + self.timer = nil + self.CONNECT_IO = nil + self.connect_co = nil + self.connect_current_co = nil + return co_wakeup(co, nil, 'connect timeout.') end) tcp_connect(self.CONNECT_IO, self.fd, self.connect_co) return co_wait() From 7fd282785b5c605ab5875cd7f3d8d5962a7c2c1c Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 1 Jul 2021 23:11:06 +0800 Subject: [PATCH 777/956] =?UTF-8?q?=E7=BB=9F=E4=B8=80unix=20domain=20socke?= =?UTF-8?q?t=E4=BD=BF=E7=94=A8=E6=96=B9=E6=B3=95=E5=91=BD=E5=90=8D?= =?UTF-8?q?=E5=90=8E=E7=BC=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/TCP.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index cffe74b2..843063f6 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -618,7 +618,7 @@ function TCP:listen_ex(unix_domain_path, removed, cb) return true, tcp_listen_ex(self.LISTEN_EX_IO, self.ufd, self.listen_ex_co) end -function TCP:connectx(path) +function TCP:connect_ex(path) self.mode = "client" self.fd = tcp_new_client_unixsock_fd(assert(type(path) == 'string' and path, "Invalid unix domain path.")) if not self.fd then From 562e365f7a30ad23c2ee54e46c9712c4b6fae841 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Fri, 2 Jul 2021 00:05:47 +0800 Subject: [PATCH 778/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0DB=E5=92=8CCache?= =?UTF-8?q?=E7=9A=84unix=20domain=E6=94=AF=E6=8C=81.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/Cache/init.lua | 27 +++++++++++++-------------- lualib/DB/mssql.lua | 11 +---------- lualib/DB/mysql.lua | 1 + lualib/DB/pgsql.lua | 11 +---------- lualib/protocol/mssql.lua | 9 +++------ lualib/protocol/mysql.lua | 7 ++++--- lualib/protocol/pgsql.lua | 11 +++++------ lualib/protocol/redis.lua | 17 +++++++---------- 8 files changed, 35 insertions(+), 59 deletions(-) diff --git a/lualib/Cache/init.lua b/lualib/Cache/init.lua index 5bcb93b2..fbf9590a 100644 --- a/lualib/Cache/init.lua +++ b/lualib/Cache/init.lua @@ -45,24 +45,22 @@ end -- 创建Cache函数 local function CREATE_CACHE(opt) - local times = 1 local rds while 1 do - rds = redis:new(opt):set_timeout(3) - local ok, err = rds:connect() - if ok then - if not opt.INITIALIZATION then - local opok, ret = assert(rds:cmd("CONFIG", "GET", "TIMEOUT")) - if opok and ret[2] ~= '0' then - assert(rds:cmd("CONFIG SET", "TIMEOUT", "0"), "SET TIMEOUT faild.") - end + rds = redis:new(opt):set_timeout(3) + local ok, err = rds:connect() + if ok then + if not opt.INITIALIZATION then + local opok, ret = assert(rds:cmd("CONFIG", "GET", "TIMEOUT")) + if opok and ret[2] ~= '0' then + assert(rds:cmd("CONFIG SET", "TIMEOUT", "0"), "SET TIMEOUT faild.") end - break end - Log:WARN('第'..tostring(times)..'次连接失败:'..err.." 3 秒后尝试再次连接") - rds:close() - times = times + 1 - timer.sleep(3) + break + end + Log:WARN("The connection failed. The reasons are: [" .. err .. "], Try to reconnect after 3 seconds") + rds:close() + timer.sleep(3) end return rds:set_timeout(0) end @@ -165,6 +163,7 @@ local Cache = class("Cache") function Cache:ctor (opt) self.host = opt.host self.port = opt.port + self.unixdomain = opt.unixdomain self.db = opt.db self.auth = opt.auth self.max = opt.max or 50 diff --git a/lualib/DB/mssql.lua b/lualib/DB/mssql.lua index 1a62dfd6..f96cb24b 100644 --- a/lualib/DB/mssql.lua +++ b/lualib/DB/mssql.lua @@ -18,20 +18,12 @@ local co_wakeup = co.wakeup local type = type local error = error local xpcall = xpcall -local ipairs = ipairs local assert = assert -local tostring = tostring -local tonumber = tonumber local fmt = string.format local insert = table.insert local remove = table.remove -local concat = table.concat - --- 空闲连接时间 -local WAIT_TIMEOUT = 31536000 -local INTERACTIVE_TIMEOUT = 31536000 -- 数据库连接创建函数 local function DB_CREATE (opt) @@ -41,8 +33,6 @@ local function DB_CREATE (opt) db:set_timeout(3) local connect, err = db:connect() if connect then - -- assert(db:query(fmt('SET wait_timeout=%u', WAIT_TIMEOUT))) - -- assert(db:query(fmt('SET interactive_timeout=%u', INTERACTIVE_TIMEOUT))) db:set_timeout(0) break end @@ -107,6 +97,7 @@ local DB = class("DB") function DB:ctor(opt) self.host = opt.host self.port = opt.port + self.unixdomain = opt.unixdomain self.username = opt.username self.password = opt.password self.database = opt.database diff --git a/lualib/DB/mysql.lua b/lualib/DB/mysql.lua index 2ef29e47..44f3e2d1 100644 --- a/lualib/DB/mysql.lua +++ b/lualib/DB/mysql.lua @@ -100,6 +100,7 @@ local DB = class("DB") function DB:ctor(opt) self.host = opt.host self.port = opt.port + self.unixdomain = opt.unixdomain self.username = opt.username self.password = opt.password self.database = opt.database diff --git a/lualib/DB/pgsql.lua b/lualib/DB/pgsql.lua index 5a506ed7..e968bfdd 100644 --- a/lualib/DB/pgsql.lua +++ b/lualib/DB/pgsql.lua @@ -18,20 +18,12 @@ local co_wakeup = co.wakeup local type = type local error = error local xpcall = xpcall -local ipairs = ipairs local assert = assert -local tostring = tostring -local tonumber = tonumber local fmt = string.format local insert = table.insert local remove = table.remove -local concat = table.concat - --- 空闲连接时间 -local WAIT_TIMEOUT = 31536000 -local INTERACTIVE_TIMEOUT = 31536000 -- 数据库连接创建函数 local function DB_CREATE (opt) @@ -41,8 +33,6 @@ local function DB_CREATE (opt) db:set_timeout(3) local connect, err = db:connect() if connect then - -- assert(db:query(fmt('SET wait_timeout=%u', WAIT_TIMEOUT))) - -- assert(db:query(fmt('SET interactive_timeout=%u', INTERACTIVE_TIMEOUT))) db:set_timeout(0) break end @@ -107,6 +97,7 @@ local DB = class("DB") function DB:ctor(opt) self.host = opt.host self.port = opt.port + self.unixdomain = opt.unixdomain self.username = opt.username self.password = opt.password self.database = opt.database diff --git a/lualib/protocol/mssql.lua b/lualib/protocol/mssql.lua index 7ee76d5c..610e08d9 100644 --- a/lualib/protocol/mssql.lua +++ b/lualib/protocol/mssql.lua @@ -28,11 +28,8 @@ local strgsub = string.gsub local strsub = string.sub local toint = math.tointeger -local ceil = math.ceil -local random = math.random local os_date = os.date -local os_time = os.time local tabconcat = table.concat @@ -877,6 +874,7 @@ function mssql:ctor(opt) self.sock = tcp:new() self.host = opt.host or "localhost" self.port = opt.port or 1433 + self.unixdomain = opt.unixdomain self.TSQL = opt.TSQL == 1 and 1 or 0 self.max_packet_size = opt.max_packet_size or 10240 self.database = opt.database or "master" @@ -916,9 +914,8 @@ function mssql:connect( ... ) return nil, "Connection failed: please recreate the socket object." end - local ok, err = self.sock:connect(self.host, self.port) - if not ok then - return nil, err + if not self.sock:connect_ex(self.unixdomain or "") and not self.sock:connect(self.host, self.port) then + return nil, "MSSQL Server Connect failed." end -- 发送TDS-7.0登录协议 diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua index faf2965a..17b083dd 100644 --- a/lualib/protocol/mysql.lua +++ b/lualib/protocol/mysql.lua @@ -452,8 +452,8 @@ local function send_packet (self, request) end local function mysql_login (self) - local sock = self.sock - if not sock or not sock:connect(self.host, self.port) then + + if not self.sock:connect_ex(self.unixdomain or "") and not self.sock:connect(self.host, self.port) then return nil, "MySQL Server Connect failed." end @@ -592,6 +592,7 @@ function mysql:ctor (opt) self.sock = tcp:new() self.host = opt.host or "localhost" self.port = opt.port or 3306 + self.unixdomain = opt.unixdomain self.max_packet_size = 16777215 self.charset = opt.charset or 33 self.database = opt.database or "mysql" @@ -604,7 +605,7 @@ end function mysql:connect () local sock = self.sock if not sock then - return nil, "not initialized" + return nil, "not initialized" end return mysql_login(self) end diff --git a/lualib/protocol/pgsql.lua b/lualib/protocol/pgsql.lua index 13b9eb90..c6f0f827 100644 --- a/lualib/protocol/pgsql.lua +++ b/lualib/protocol/pgsql.lua @@ -12,7 +12,6 @@ local sys = require "sys" local new_tab = sys.new_tab local null = null -local tostring = tostring local tonumber = tonumber local fmt = string.format @@ -22,7 +21,6 @@ local tconcat = table.concat local string = string local strsub = string.sub local strbyte = string.byte -local strchar = string.char local strpack = string.pack local strunpack = string.unpack local strgmatch = string.gmatch @@ -430,12 +428,14 @@ local pgsql = class("pgsql") function pgsql:ctor(opt) self.sock = tcp:new() self.host = opt.host or "localhost" - self.port = opt.port or 3306 + self.port = opt.port or 5432 + self.unixdomain = opt.unixdomain self.database = opt.database or "postgres" self.username = opt.username or "postgres" self.password = opt.password or "postgres" self.charset = opt.charset or "UTF8" self.application_name = opt.application_name or "cfadmin" + -- self.state = "connected" end function pgsql:read(bytes) @@ -477,9 +477,8 @@ end function pgsql:connect() - local ok, err = self.sock:connect(self.host, self.port) - if not ok then - return nil, err + if not self.sock:connect_ex(self.unixdomain or "") and not self.sock:connect(self.host, self.port) then + return nil, "MySQL Server Connect failed." end -- 发送启动协议 diff --git a/lualib/protocol/redis.lua b/lualib/protocol/redis.lua index 690331c1..8119a4db 100644 --- a/lualib/protocol/redis.lua +++ b/lualib/protocol/redis.lua @@ -140,6 +140,7 @@ function redis:ctor(opt) self.sock = tcp:new() self.host = opt.host self.port = opt.port + self.unixdomain = opt.unixdomain self.auth = opt.auth self.db = opt.db end @@ -149,18 +150,14 @@ function redis:isconnected() end function redis:connect() - local sock = self.sock - if not sock then - return nil, "Can't Create redis Socket" - end - local ok, err - ok, err = sock:connect(self.host, toint(self.port) or 6379) - if not ok then - return nil, "redis connect error: please check network" + -- 尝试多种连接渠道 + if not self.sock:connect_ex(self.unixdomain or "") and not self.sock:connect(self.host, toint(self.port) or 6379) then + return nil, "redis network connect failed." end - ok, err = redis_login(sock, self.auth, self.db) + -- 登录状态检查 + local ok, err = redis_login(self.sock, self.auth, self.db) if not ok then - return nil, "redis login error:"..(err or 'close') + return nil, "redis login error:" .. (err or 'close') end return true end From e0365defa3b322b6b116261238571fbcb2c5b54f Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Fri, 2 Jul 2021 00:10:31 +0800 Subject: [PATCH 779/956] =?UTF-8?q?aio.kill=E5=A2=9E=E5=8A=A0=E5=8F=AF?= =?UTF-8?q?=E9=80=89=E7=9A=84signum=E5=8F=82=E6=95=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/aio/init.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lualib/aio/init.lua b/lualib/aio/init.lua index 7bb013d9..048da090 100644 --- a/lualib/aio/init.lua +++ b/lualib/aio/init.lua @@ -542,9 +542,10 @@ function aio.popen(command, timeout) end ---comment `kill`指定进程 ----@param pid integer 指定进程的`PID` -function aio.kill(pid) - aio_kill(pid) +---@param pid integer @进程的`PID` +---@param signum integer @信号的`num` +function aio.kill(pid, signum) + aio_kill(pid, signum) return true end From b27a58fbc603ae25d8d621cb2732784ae904e6e9 Mon Sep 17 00:00:00 2001 From: "Mr.che" <869646063@qq.com> Date: Fri, 2 Jul 2021 18:04:38 +0800 Subject: [PATCH 780/956] Update Makefile --- luaclib/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/luaclib/Makefile b/luaclib/Makefile index b146ebf6..5c84b8be 100644 --- a/luaclib/Makefile +++ b/luaclib/Makefile @@ -23,9 +23,9 @@ internal : @echo "CC - ltimer" @$(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore @echo "CC - laio" - @$(CC) -o laio.so src/laio.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -leio -lev + @$(CC) -o laio.so src/laio.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -leio @echo "CC - ltcp" - @$(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lssl -lcrypto -lcore + @$(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -lssl -lcrypto 3part : @echo "CC - lz" From b04f30cb64c778f6ff1be87a54fa4db6aa161a9d Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 3 Jul 2021 00:31:48 +0800 Subject: [PATCH 781/956] =?UTF-8?q?=E5=AD=90=E8=BF=9B=E7=A8=8B=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E5=85=B3=E9=97=AD=E7=BB=A7=E6=89=BF=E7=9A=84fd.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 9 +++++---- src/core_ev.c | 16 ++++++++++++++-- src/core_ev.h | 10 ++++++++++ src/core_sys.h | 7 ++++++- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/core.c b/src/core.c index 5e7007a7..32fadebb 100644 --- a/src/core.c +++ b/src/core.c @@ -56,11 +56,12 @@ static void CHILD_CB (core_loop *loop, ev_child *w, int revents){ /* 内部异常 */ static void EV_ERROR_CB(const char *msg){ LOG("ERROR", msg); + LOG("ERROR", strerror(errno)); if (core_default_loop()) { pid_t *pids = (pid_t *)ev_userdata(core_default_loop()); if (!pids) { kill(getppid(), SIGKILL); - return; + return exit(EXIT_SUCCESS); } int index; int nprocess = atoi(getenv("cfadmin_nprocess")) > 1 ? atoi(getenv("cfadmin_nprocess")) : 0; @@ -241,11 +242,11 @@ int core_master_run(pid_t *pids, int* pidcount) { /* 设置pid */ ev_set_userdata(loop, pids); /* 注册子进程监听 */ - ev_child childs[*pidcount]; + core_child childs[*pidcount]; int index; for (index = 0; index < *pidcount; index++) { - ev_child_init(&childs[index], CHILD_CB, pids[index], 0); - ev_child_start(loop, &childs[index]); + core_child_init(&childs[index], CHILD_CB, pids[index], 0); + core_child_start(loop, &childs[index]); } /* 初始化主进程 */ return core_start(loop, 0); diff --git a/src/core_ev.c b/src/core_ev.c index 1c211ca0..6c6bfdf4 100644 --- a/src/core_ev.c +++ b/src/core_ev.c @@ -48,7 +48,6 @@ void core_task_start(core_loop *loop, core_task *task){ void core_task_stop(core_loop *loop, core_task *task){ ev_idle_stop(loop ? loop : CORE_LOOP, task); } - /* =========== TASK =========== */ /* =========== Signal =========== */ @@ -61,6 +60,19 @@ void core_signal_start(core_loop *loop, core_signal *signal){ } /* =========== Signal =========== */ +/* =========== Child =========== */ +void core_child_init(core_child *w, _CHILD_CB cb, pid_t pid, int trace){ + ev_child_init(w, cb, pid, trace); +} + +void core_child_start(core_loop *loop, core_child *w){ + ev_child_start(loop ? loop : CORE_LOOP, w); +} + +void core_child_stop(core_loop *loop, core_child *w){ + ev_child_stop(loop ? loop : CORE_LOOP, w); +} +/* =========== Child =========== */ core_loop* core_loop_fork(core_loop *loop) { @@ -71,7 +83,7 @@ core_loop* core_loop_fork(core_loop *loop) { core_loop* core_default_loop(){ int BEST_BACKEND = 0; #if defined(__MSYS__) || defined(__CYGWIN__) - BEST_BACKEND |= EVBACKEND_POLL | EVFLAG_SIGNALFD | EVFLAG_NOINOTIFY; + BEST_BACKEND |= EVBACKEND_SELECT | EVFLAG_SIGNALFD | EVFLAG_NOINOTIFY; #elif defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) BEST_BACKEND |= EVBACKEND_KQUEUE | EVFLAG_NOINOTIFY | EVFLAG_NOTIMERFD; #elif defined(linux) || defined(__linux) || defined(__linux__) diff --git a/src/core_ev.h b/src/core_ev.h index 7c9f86b2..43365e1a 100644 --- a/src/core_ev.h +++ b/src/core_ev.h @@ -64,12 +64,14 @@ typedef ev_io core_io; typedef ev_idle core_task; typedef ev_timer core_timer; typedef ev_signal core_signal; +typedef ev_child core_child; typedef struct ev_loop core_loop; typedef void (*_IO_CB)(core_loop *loop, core_io *io, int revents); typedef void (*_TASK_CB)(core_loop *loop, core_task *task, int revents); typedef void (*_TIMER_CB)(core_loop *loop, core_timer *timer, int revents); typedef void (*_SIGNAL_CB)(core_loop *loop, core_signal *signal, int revents); +typedef void (*_CHILD_CB)(core_loop *loop, core_child *w, int revents); /* =========== Timer =========== */ void core_timer_init(core_timer *timer, _TIMER_CB cb); @@ -101,6 +103,14 @@ void core_signal_init(core_signal *signal, _SIGNAL_CB cb, int signum); void core_signal_start(core_loop *loop, core_signal *signal); /* =========== Signal =========== */ +/* =========== Child =========== */ +void core_child_init(core_child *w, _CHILD_CB, pid_t pid, int trace); + +void core_child_start(core_loop *loop, core_child *w); + +void core_child_stop(core_loop *loop, core_child *w); +/* =========== Child =========== */ + void core_break(core_loop *loop, int mode); int core_start(core_loop *loop, int mode); diff --git a/src/core_sys.h b/src/core_sys.h index 25ca044b..fb2e2c32 100644 --- a/src/core_sys.h +++ b/src/core_sys.h @@ -60,8 +60,13 @@ #define EWOULDBLOCK EAGAIN #endif -#define non_blocking(socket) (fcntl(socket, F_SETFL, fcntl(socket, F_GETFL, 0) | O_NONBLOCK)); +// 设置子进程自动关闭进程fd. +#define non_exec(socket) {fcntl(socket, F_SETFD, fcntl(socket, F_GETFL, 0) | FD_CLOEXEC);} +// 设置非阻塞模式 +#define non_blocking(socket) {non_exec(socket); fcntl(socket, F_SETFL, fcntl(socket, F_GETFL, 0) | O_NONBLOCK);} + +// 设置nodelay模式 #define non_delay(socket) ({int Enable = 1; setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, &Enable, sizeof(Enable));}) /* [datetime][level][file][function][line][具体打印内容] */ From ed83a48d6b2c440fa978e3ecba46943d7dc21d74 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 3 Jul 2021 00:32:51 +0800 Subject: [PATCH 782/956] =?UTF-8?q?=E5=AE=8C=E5=96=84aio=E7=9A=84=E8=BF=9B?= =?UTF-8?q?=E7=A8=8B=E7=AE=A1=E7=90=86=E3=80=81=E8=B6=85=E6=97=B6=E5=88=A4?= =?UTF-8?q?=E6=96=AD=E3=80=81=E8=87=AA=E5=8A=A8=E6=A3=80=E6=9F=A5=E6=9C=BA?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/laio.c | 66 ++++++++++++++++++++++++--------------------- lualib/aio/init.lua | 54 ++++++++++++++++++++++++++++++++----- 2 files changed, 82 insertions(+), 38 deletions(-) diff --git a/luaclib/src/laio.c b/luaclib/src/laio.c index 8a9ffa0c..61d128cf 100644 --- a/luaclib/src/laio.c +++ b/luaclib/src/laio.c @@ -644,41 +644,39 @@ static int laio_rmdir(lua_State* L) { return 1; } -static void CHILD_CB (core_loop *loop, ev_child *w, int revents){ +static void CHILD_CB (core_loop *loop, core_child *w, int revents){ lua_State *co = (lua_State *)core_get_watcher_userdata(w); - if (lua_status(co) == LUA_YIELD || lua_status(co) == LUA_OK){ + if (co && (lua_status(co) == LUA_YIELD || lua_status(co) == LUA_OK)){ lua_pushinteger(co, w->rstatus); int status = CO_RESUME(co, NULL, 1); if (status != LUA_YIELD && status != LUA_OK) LOG("ERROR", lua_tostring(co, -1)); } // 停止继续监听 - ev_child_stop(loop, w); + core_child_stop(loop, w); } // 实现异步`system`方法. static pid_t laio_system(lua_State *L, const char* command, int pfd) { pid_t pid = fork(); - if (pid < 0) - return (pid_t)-1; - if (pid < 1) { - // 完整独立进程会话ID - setsid(); - // 重置子进程的信号掩码 - signal(SIGINT, SIG_DFL); signal(SIGHUP, SIG_DFL); - signal(SIGTERM, SIG_DFL); signal(SIGPIPE, SIG_DFL); - signal(SIGTSTP, SIG_DFL); signal(SIGQUIT, SIG_DFL); - // 子进程需要设置为独立的输入输出管道; - (void)dup2(pfd, STDIN_FILENO); - (void)dup2(pfd, STDOUT_FILENO); - (void)dup2(pfd, STDERR_FILENO); - // 子进程需要进与父子进程的的上下文分离 - if (execl("/bin/sh", "sh", "-c", command, NULL)) - write(STDOUT_FILENO, strerror(errno), strlen(strerror(errno))); - // 正常执行完毕是不会走到这里, 所以只能是执行失败. - exit(-1); // exit(EXIT_FAILURE); - } - return pid; + if (pid) + return pid; + + // 完整独立进程会话ID + setsid(); + // 重置子进程的信号掩码 + signal(SIGINT, SIG_DFL); signal(SIGHUP, SIG_DFL); + signal(SIGTERM, SIG_DFL); signal(SIGPIPE, SIG_DFL); + signal(SIGTSTP, SIG_DFL); signal(SIGQUIT, SIG_DFL); + // 子进程需要设置为独立的输入输出管道; + (void)dup2(pfd, STDIN_FILENO); + (void)dup2(pfd, STDOUT_FILENO); + (void)dup2(pfd, STDERR_FILENO); + // 子进程需要进与父子进程的的上下文分离 + if (execl("/bin/sh", "sh", "-c", command, NULL)) + write(STDOUT_FILENO, strerror(errno), strlen(strerror(errno))); + // 正常执行完毕是不会走到这里, 所以只能是执行失败. + exit(EXIT_FAILURE); } // 自定义创建进程 @@ -694,6 +692,9 @@ static int laio_popen(lua_State *L) { if (socketpair(AF_UNIX, SOCK_STREAM, 0, std) < 0) return luaL_error(L, "Cand't create pipe.\n"); + // 设置非阻塞模式 + // non_blocking(std[0]); non_blocking(std[1]); + pid_t pid = laio_system(L, command, std[1]); if (pid < 1) { close(std[0]); close(std[1]); @@ -718,22 +719,25 @@ static int laio_popen(lua_State *L) { lua_rawset(L, -3); - // 监听`子进程`的退出事件 lua_pushliteral(L, "child"); - ev_child *w = lua_newuserdata(L, sizeof(ev_child)); - core_set_watcher_userdata(w, co); - ev_child_init(w, CHILD_CB, pid, 0); - ev_child_start(core_default_loop(), w); + core_child *w = lua_newuserdata(L, sizeof(core_child)); lua_rawset(L, -3); - // 返回一个包含`pipe`、`ev_child`指针的`table`. + + // 监听`子进程`的退出事件 + core_set_watcher_userdata(w, co); + core_child_init(w, CHILD_CB, pid, 0); + core_child_start(core_default_loop(), w); + // 返回一个包含`pipe`、`core_child`指针的`table`. return 1; } // 根据子进程的`pid`杀死子进程 static int laio_kill(lua_State *L){ lua_Integer pid = luaL_checkinteger(L, 1); - if (pid > 1 && getpid() != pid) - kill(pid, luaL_optinteger(L, 2, SIGKILL)); + if (getpid() != pid && pid != 1){ + if (kill(pid, luaL_optinteger(L, 2, SIGKILL))) + LOG("ERROR", strerror(errno)); + } lua_pushboolean(L, 1); return 1; } diff --git a/lualib/aio/init.lua b/lualib/aio/init.lua index 048da090..dfd4e5b3 100644 --- a/lualib/aio/init.lua +++ b/lualib/aio/init.lua @@ -503,32 +503,39 @@ function pfile:close() end ---comment @`io.popen`的非阻塞版实现 ----@param command string @`command`是一个`string`类型的参数; +---@param command string @`command`是一个`string`类型的参数, 它是用于执行的shell命令; ---@param timeout number @`timeout`是一个`Number`类型的参数(可选), 可以指定合适的超时时间来控制进程运行时长. +---@param daemond boolean @`daemond`是一个`boolean`类型的参数(可选), 它用来告诉框架应该计算合适的守护进程`PID`. ---@return table|boolean @子进程在退出后此方法才会返回; 正常退出返回`pfile`对象, 异常退出与错误退出返回`false`与出错信息; ---@return nil|string @需要注意的是`pfile`对象必须开发者手动关闭, 否则可能会造成`fd`泄漏的问题. -function aio.popen(command, timeout) +function aio.popen(command, timeout, daemond) local ok, obj, co_timer, killed local co = co_self() local co_cb = co_new(function (id) -- 正常结束返回`0`, 异常结束返回`进程id`, 超时`kill`返回信号代码(9); -- print("进程结束: ", id) - if co_timer then - co_timer:stop() + if not daemond then + if co_timer then + co_timer:stop() + end + return co_wakeup(co, id == 0 and true or false) end - return co_wakeup(co, id == 0 and true or false) end) ok, obj = pcall(aio_popen, command, co_cb) if not ok then return false, "[AIO_POPEN ERROR] : " .. obj end co_timer = cf.timeout(tonumber(timeout), function () - aio_kill(obj.pid) + aio_kill(obj.pid + (daemond and 1 or 0)) + print(obj.pid, daemond) + if daemond then + co_wakeup(co, false) + end killed = true co_timer = nil end) tcp_close(obj.pipe[2]) - local f = setmetatable({ fd = obj.pipe[1] }, pfile) + local f = setmetatable({ pid = obj.pid + (daemond and 1 or 0), fd = obj.pipe[1] }, pfile) -- 等待子进程运行结束: 如果运行失败则返回错误信息, 如果运行成功则返回需要自己读取与手动关闭的`pfile`对象. if not co_wait() then local errinfo = f:read "*a" @@ -536,11 +543,44 @@ function aio.popen(command, timeout) errinfo = "command timeout killed." end f:close() + obj = nil return false, "[AIO_POPEN ERROR] : " .. errinfo end + obj = nil return f end +---comment @`os.execute`的非阻塞版本实现, 它只执行期间也不会阻塞其它协程. +---@param command string @`command`是一个`string`类型的参数, 它是用于执行的shell命令; +---@param timeout number @`timeout`是一个`Number`类型的参数(可选), 可以指定合适的超时时间来控制进程运行时长. +function aio.execute(command, timeout) + local ok, obj, co_timer + local co = co_self() + local co_cb = co_new(function (id) + -- 正常结束返回`0`, 异常结束返回`进程id`, 超时`kill`返回信号代码(9); + -- print("进程结束: ", id) + if co_timer then + co_timer:stop() + end + return co_wakeup(co, id == 0 and true or false) + end) + ok, obj = pcall(aio_popen, command, co_cb) + if not ok then + return false, "[AIO_EXECUTE ERROR] : " .. obj + end + co_timer = cf.timeout(tonumber(timeout), function () + aio_kill(obj.pid) + co_timer = nil + end) + ok = co_wait() + local reader, writer = setmetatable({ fd = obj.pipe[1] }, pfile), setmetatable({ fd = obj.pipe[2] }, pfile) + writer:close() + print(reader:read "*a") + reader:close(); + obj = nil + return ok, "exit", ok and 0 or 1 +end + ---comment `kill`指定进程 ---@param pid integer @进程的`PID` ---@param signum integer @信号的`num` From e7372205d11f1c7d1458bc84ec7af5face9791bc Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 3 Jul 2021 00:39:03 +0800 Subject: [PATCH 783/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Daio.kill=E4=B8=8Eaio.?= =?UTF-8?q?popen=E7=9A=84=E9=97=AE=E9=A2=98,=E5=A2=9E=E5=8A=A0aio.execute?= =?UTF-8?q?=E6=96=B9=E6=B3=95.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/aio/init.lua | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/lualib/aio/init.lua b/lualib/aio/init.lua index dfd4e5b3..4a6388cc 100644 --- a/lualib/aio/init.lua +++ b/lualib/aio/init.lua @@ -518,7 +518,10 @@ function aio.popen(command, timeout, daemond) if co_timer then co_timer:stop() end - return co_wakeup(co, id == 0 and true or false) + if co then + co_wakeup(co, id == 0 and true or false) + co = nil + end end end) ok, obj = pcall(aio_popen, command, co_cb) @@ -527,9 +530,9 @@ function aio.popen(command, timeout, daemond) end co_timer = cf.timeout(tonumber(timeout), function () aio_kill(obj.pid + (daemond and 1 or 0)) - print(obj.pid, daemond) if daemond then co_wakeup(co, false) + co = nil end killed = true co_timer = nil @@ -553,23 +556,33 @@ end ---comment @`os.execute`的非阻塞版本实现, 它只执行期间也不会阻塞其它协程. ---@param command string @`command`是一个`string`类型的参数, 它是用于执行的shell命令; ---@param timeout number @`timeout`是一个`Number`类型的参数(可选), 可以指定合适的超时时间来控制进程运行时长. -function aio.execute(command, timeout) +---@param daemond boolean @`daemond`是一个`boolean`类型的参数(可选), 它用来告诉框架应该计算合适的守护进程`PID`. +function aio.execute(command, timeout, daemond) local ok, obj, co_timer local co = co_self() local co_cb = co_new(function (id) -- 正常结束返回`0`, 异常结束返回`进程id`, 超时`kill`返回信号代码(9); -- print("进程结束: ", id) - if co_timer then - co_timer:stop() + if not daemond then + if co_timer then + co_timer:stop() + end + if co then + co_wakeup(co, id == 0 and true or false) + co = nil + end end - return co_wakeup(co, id == 0 and true or false) end) ok, obj = pcall(aio_popen, command, co_cb) if not ok then - return false, "[AIO_EXECUTE ERROR] : " .. obj + return false, "[AIO_POPEN ERROR] : " .. obj end co_timer = cf.timeout(tonumber(timeout), function () - aio_kill(obj.pid) + aio_kill(obj.pid + (daemond and 1 or 0)) + if daemond then + co_wakeup(co, false) + co = nil + end co_timer = nil end) ok = co_wait() From 964d5fb76f20805b4e6cf97fee454a4bae8aacb8 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 3 Jul 2021 00:45:49 +0800 Subject: [PATCH 784/956] =?UTF-8?q?=E7=AE=80=E5=8C=96=E4=BF=A1=E5=8F=B7?= =?UTF-8?q?=E5=A4=84=E7=90=86=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 25 ++++--------------------- src/core.h | 7 +++---- 2 files changed, 7 insertions(+), 25 deletions(-) diff --git a/src/core.c b/src/core.c index 32fadebb..54c23515 100644 --- a/src/core.c +++ b/src/core.c @@ -30,15 +30,8 @@ static void SIG_IGNORE(core_loop *loop, core_signal *signal, int revents){ /* 退出信号 */ static void SIG_EXIT(core_loop *loop, core_signal *signal, int revents){ if (ev_userdata(loop) && core_get_watcher_userdata(signal)) { - int index; - pid_t *pids = (pid_t *)ev_userdata(loop); - int nprocess = *(int*)core_get_watcher_userdata(signal); - for (index = 0; index < nprocess; index++) { - pid_t pid = pids[index]; - if (pid > 0) - kill(pid, SIGKILL); - pids[index] = -1; - } + if (ev_userdata(loop)) + kill(0, SIGKILL); } return exit(EXIT_SUCCESS); } @@ -59,18 +52,8 @@ static void EV_ERROR_CB(const char *msg){ LOG("ERROR", strerror(errno)); if (core_default_loop()) { pid_t *pids = (pid_t *)ev_userdata(core_default_loop()); - if (!pids) { - kill(getppid(), SIGKILL); - return exit(EXIT_SUCCESS); - } - int index; - int nprocess = atoi(getenv("cfadmin_nprocess")) > 1 ? atoi(getenv("cfadmin_nprocess")) : 0; - for (index = 0; index < nprocess; index++) { - pid_t pid = pids[index]; - if (pid > 0) - kill(pid, SIGKILL); - pids[index] = -1; - } + if (pids) + kill(0, SIGKILL); } /* 减少无效打印, 专注错误提示 */ return exit(EXIT_SUCCESS); diff --git a/src/core.h b/src/core.h index 3936d74c..53bf3e7c 100644 --- a/src/core.h +++ b/src/core.h @@ -7,10 +7,9 @@ // 用来退出父子进程 static inline void core_exit() { - pid_t ppid = getppid(); - if (ppid > 0) - kill(ppid, SIGQUIT); - return _exit(-1); + if (getppid() > 0) + kill(0, SIGKILL); + return _exit(EXIT_FAILURE); } #endif From 3d22e9dabf686c127ab41b13ebed49cd195a7f38 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 3 Jul 2021 21:23:29 +0800 Subject: [PATCH 785/956] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=AD=90=E8=BF=9B?= =?UTF-8?q?=E7=A8=8B=E7=9A=84=E7=9B=91=E6=8E=A7pid=E7=9A=84=E8=AE=A1?= =?UTF-8?q?=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/laio.c | 2 +- lualib/aio/init.lua | 40 +++++++++++----------------------------- 2 files changed, 12 insertions(+), 30 deletions(-) diff --git a/luaclib/src/laio.c b/luaclib/src/laio.c index 61d128cf..28efcefc 100644 --- a/luaclib/src/laio.c +++ b/luaclib/src/laio.c @@ -704,7 +704,7 @@ static int laio_popen(lua_State *L) { lua_createtable(L, 0, 2); // 记录子进程的`PID`. lua_pushliteral(L, "pid"); - lua_pushinteger(L, pid); + lua_pushinteger(L, pid + luaL_optinteger(L, 3, 0)); lua_rawset(L, -3); // 记录双向通信用到的`管道`; diff --git a/lualib/aio/init.lua b/lualib/aio/init.lua index 4a6388cc..3e052ab7 100644 --- a/lualib/aio/init.lua +++ b/lualib/aio/init.lua @@ -514,31 +514,22 @@ function aio.popen(command, timeout, daemond) local co_cb = co_new(function (id) -- 正常结束返回`0`, 异常结束返回`进程id`, 超时`kill`返回信号代码(9); -- print("进程结束: ", id) - if not daemond then - if co_timer then - co_timer:stop() - end - if co then - co_wakeup(co, id == 0 and true or false) - co = nil - end + if co_timer then + co_timer:stop() end + return co_wakeup(co, id == 0 and true or false) end) - ok, obj = pcall(aio_popen, command, co_cb) + ok, obj = pcall(aio_popen, command, co_cb, daemond and 1 or 0) if not ok then return false, "[AIO_POPEN ERROR] : " .. obj end co_timer = cf.timeout(tonumber(timeout), function () - aio_kill(obj.pid + (daemond and 1 or 0)) - if daemond then - co_wakeup(co, false) - co = nil - end + aio_kill(obj.pid) killed = true co_timer = nil end) tcp_close(obj.pipe[2]) - local f = setmetatable({ pid = obj.pid + (daemond and 1 or 0), fd = obj.pipe[1] }, pfile) + local f = setmetatable({ pid = obj.pid, fd = obj.pipe[1] }, pfile) -- 等待子进程运行结束: 如果运行失败则返回错误信息, 如果运行成功则返回需要自己读取与手动关闭的`pfile`对象. if not co_wait() then local errinfo = f:read "*a" @@ -563,26 +554,17 @@ function aio.execute(command, timeout, daemond) local co_cb = co_new(function (id) -- 正常结束返回`0`, 异常结束返回`进程id`, 超时`kill`返回信号代码(9); -- print("进程结束: ", id) - if not daemond then - if co_timer then - co_timer:stop() - end - if co then - co_wakeup(co, id == 0 and true or false) - co = nil - end + if co_timer then + co_timer:stop() end + return co_wakeup(co, id == 0 and true or false) end) - ok, obj = pcall(aio_popen, command, co_cb) + ok, obj = pcall(aio_popen, command, co_cb, daemond and 1 or 0) if not ok then return false, "[AIO_POPEN ERROR] : " .. obj end co_timer = cf.timeout(tonumber(timeout), function () - aio_kill(obj.pid + (daemond and 1 or 0)) - if daemond then - co_wakeup(co, false) - co = nil - end + aio_kill(obj.pid) co_timer = nil end) ok = co_wait() From 53916fae3437a4810ce80c8aae44ae5a05d0108e Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 3 Jul 2021 21:57:25 +0800 Subject: [PATCH 786/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=BF=9B=E7=A8=8B?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=E7=9A=84=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 4 ++-- src/core.h | 3 --- src/core_start.c | 7 ++----- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/core.c b/src/core.c index 54c23515..3abc8298 100644 --- a/src/core.c +++ b/src/core.c @@ -31,7 +31,7 @@ static void SIG_IGNORE(core_loop *loop, core_signal *signal, int revents){ static void SIG_EXIT(core_loop *loop, core_signal *signal, int revents){ if (ev_userdata(loop) && core_get_watcher_userdata(signal)) { if (ev_userdata(loop)) - kill(0, SIGKILL); + kill(0, SIGQUIT); } return exit(EXIT_SUCCESS); } @@ -224,7 +224,7 @@ int core_master_run(pid_t *pids, int* pidcount) { signal_init(pidcount); /* 设置pid */ ev_set_userdata(loop, pids); - /* 注册子进程监听 */ + /* 监听子进程 */ core_child childs[*pidcount]; int index; for (index = 0; index < *pidcount; index++) { diff --git a/src/core.h b/src/core.h index 53bf3e7c..3aaf423d 100644 --- a/src/core.h +++ b/src/core.h @@ -5,10 +5,7 @@ #include "core_memory.h" #include "core_ev.h" -// 用来退出父子进程 static inline void core_exit() { - if (getppid() > 0) - kill(0, SIGKILL); return _exit(EXIT_FAILURE); } diff --git a/src/core_start.c b/src/core_start.c index 4a68d58c..d958c6e4 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -2,7 +2,7 @@ int core_worker_run(const char entry[]); -int core_master_run(pid_t *pids, int* pidcount); +int core_master_run(int pidcount, pid_t pids); enum { isMaster = 1, @@ -216,10 +216,7 @@ static inline pid_t cfadmin_master_run() { if (e < 0) LOG("ERROR", strerror(errno)); } - int index; - for (index = 0; index < i; index ++) - kill(npid[index], SIGQUIT); - kill(ppid, SIGQUIT); + kill(0, SIGQUIT); return (pid_t)-1; } npid[i] = pid; From ae41582860cdf98e4c9cb93fb7d11e212d4f6188 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 3 Jul 2021 21:59:11 +0800 Subject: [PATCH 787/956] =?UTF-8?q?=E6=B8=85=E9=99=A4=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_start.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core_start.c b/src/core_start.c index d958c6e4..1ad0e8c3 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -2,7 +2,7 @@ int core_worker_run(const char entry[]); -int core_master_run(int pidcount, pid_t pids); +int core_master_run(pid_t *pids, int* pidcount); enum { isMaster = 1, From 32f87a3d093067588619ca1b1fcf45f6241e0249 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 3 Jul 2021 22:00:14 +0800 Subject: [PATCH 788/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0jwt=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_http_auth.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/script/test_http_auth.lua b/script/test_http_auth.lua index 176bb80c..98429066 100644 --- a/script/test_http_auth.lua +++ b/script/test_http_auth.lua @@ -4,10 +4,6 @@ local httpc = require "httpc" local basic_key, basic_value = httpc.basic_authorization("myusername", "mypassword") print(basic_key, basic_value) -local jwt_header_key, jwt_header_value = httpc.jwt("mysecret", [[{"key1":"value1","key2":"value2"}]]) -print(jwt_header_key, jwt_header_value) - - -- 从httpc类库中导入并且使用 local httpc_cls = require "httpc.class" local hc = httpc_cls:new {} @@ -15,5 +11,9 @@ local hc = httpc_cls:new {} local basic_key, basic_value = hc:basic_authorization("myusername", "mypassword") print(basic_key, basic_value) -local jwt_header_key, jwt_header_value = hc:jwt("mysecret", [[{"key1":"value1","key2":"value2"}]]) -print(jwt_header_key, jwt_header_value) \ No newline at end of file +-- 从json.jwt导出 +local jwt = require "json.jwt" +local raw = '{"name":"Hello world."}' +local enc = jwt.encode(raw, "secret", "HS256") +local dec = jwt.decode(enc, "secret", "HS256") +print("jwt test:", raw == dec, enc) \ No newline at end of file From 570c1375d3dd26c9bce703b75013a9f0bc252e9b Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 4 Jul 2021 00:07:37 +0800 Subject: [PATCH 789/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_cf.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/test_cf.lua b/script/test_cf.lua index 09ac0403..feecd9a3 100644 --- a/script/test_cf.lua +++ b/script/test_cf.lua @@ -10,7 +10,7 @@ cf.fork(function() print("fork", "我现在被唤醒了") end) -local times, timer = 0 +local times, timer = 0, nil timer = cf.at(1, function() if times >= 3 then print("循环定时器运行次数到了.") @@ -20,7 +20,7 @@ timer = cf.at(1, function() print("定时器运行次数:", times) end) -local timer = cf.timeout(1, function() +cf.timeout(1, function() print("一次性定时器运行.") end) From 3b8d373d3bad372e501983365bc6779ddcee7769 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 4 Jul 2021 00:07:57 +0800 Subject: [PATCH 790/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E6=8F=90=E7=A4=BA=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/dns.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/protocol/dns.lua b/lualib/protocol/dns.lua index e0d477bc..a55a9626 100644 --- a/lualib/protocol/dns.lua +++ b/lualib/protocol/dns.lua @@ -273,7 +273,7 @@ local function dns_query(domain, ip_version) local question question, nbyte = unpack_question(dns_resp, nbyte) if question.name ~= domain then - local err = "4. Inconsistent query domain." + local err = "4. Inconsistent query domain. " .. question.name check_wait(domain, wlist, nil, err) return nil, err end From 0202394aa7033e9e354ebc846917077a14045bd6 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 4 Jul 2021 01:16:16 +0800 Subject: [PATCH 791/956] =?UTF-8?q?=E4=BC=98=E5=8C=96DNS=E5=88=86=E6=94=AF?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/dns.lua | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/lualib/protocol/dns.lua b/lualib/protocol/dns.lua index a55a9626..0622d1fb 100644 --- a/lualib/protocol/dns.lua +++ b/lualib/protocol/dns.lua @@ -144,20 +144,16 @@ end local function pack_header() local tid = gen_id() - local flag = 0x100 + local flag = 0x120 local QCOUNT = 1 - -- QCount 永远是1, flags 永远是256 + -- QCount 永远是1, flags 永远是288 return pack(">HHHHHH", tid, flag, QCOUNT, 0, 0, 0) end local function pack_question(name, version) -- local Query_Type = QTYPE.A -- IPv4 -- local Query_Type = QTYPE.AAAA -- IPv6 - local qtype = QTYPE.A - if version == 6 then - qtype = QTYPE.AAAA - end - local Query_Type = qtype + local Query_Type = version == 6 and QTYPE.AAAA or QTYPE.A local Query_Class = 0x01 -- IN internet local question = {} for sp in splite(name, "([^%.]*)") do @@ -209,10 +205,7 @@ local function unpack_answer(chunk, nbyte) end local function unpack_rdata(chunk, qtype) - if qtype == QTYPE.AAAA then - return fmt('%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x', unpack(">HHHHHHHH", chunk)) - end - return fmt("%u.%u.%u.%u", unpack(">BBBB", chunk)) + return qtype == QTYPE.AAAA and fmt('%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x', unpack(">HHHHHHHH", chunk)) or fmt("%u.%u.%u.%u", unpack(">BBBB", chunk)) end local cos = {} @@ -262,7 +255,8 @@ local function dns_query(domain, ip_version) check_wait(domain, wlist, nil, err) return nil, err end - if not answer_header.ancount or answer_header.ancount < 1 then + local ancount = answer_header.ancount + if not ancount or ancount < 1 then if not ip_version then -- 如果IPv4无法解析则尝试ipv6, 反之亦然. return dns_query(domain, msg == 4 and 6 or 4) end @@ -279,7 +273,7 @@ local function dns_query(domain, ip_version) end local answer local t = now() - for _ = 1, answer_header.ancount do + for _ = 1, ancount do answer, nbyte = unpack_answer(dns_resp, nbyte) if answer.atype == QTYPE.A or answer.atype == QTYPE.AAAA then answer.ip = unpack_rdata(answer.rdata, answer.atype) From 9ae508a638393ba380a18a71e5789e6de27eca4b Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 4 Jul 2021 01:34:16 +0800 Subject: [PATCH 792/956] =?UTF-8?q?=E5=85=BC=E5=AE=B9=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E5=A4=A7=E5=B0=8F=E5=86=99=E9=97=AE=E9=A2=98.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/dns.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lualib/protocol/dns.lua b/lualib/protocol/dns.lua index 0622d1fb..a83390e6 100644 --- a/lualib/protocol/dns.lua +++ b/lualib/protocol/dns.lua @@ -23,6 +23,7 @@ local insert = table.insert local now = os.time local random = math.random +local lower = string.lower local fmt = string.format local find = string.find local match = string.match @@ -266,7 +267,7 @@ local function dns_query(domain, ip_version) end local question question, nbyte = unpack_question(dns_resp, nbyte) - if question.name ~= domain then + if lower(question.name) ~= lower(domain) then local err = "4. Inconsistent query domain. " .. question.name check_wait(domain, wlist, nil, err) return nil, err From 37c2048cf0cf550b32f82ca401981ea2b765e396 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 4 Jul 2021 02:10:27 +0800 Subject: [PATCH 793/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=9F=A5=E8=A1=A8?= =?UTF-8?q?=E9=80=9F=E5=BA=A6,=20=E9=99=8D=E4=BD=8ECPU=E4=B8=8E=E5=86=85?= =?UTF-8?q?=E5=AD=98=E4=BD=BF=E7=94=A8=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/dns.lua | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/lualib/protocol/dns.lua b/lualib/protocol/dns.lua index a83390e6..1ab7b7dd 100644 --- a/lualib/protocol/dns.lua +++ b/lualib/protocol/dns.lua @@ -70,6 +70,10 @@ local function check_cache(domain) return end +local function add_cache(domain, ip) + dns_cache[domain] = { ip = ip } +end + local function check_ip(ip) if type(ip) == 'string' and ip ~= '' then if check_ipv4(ip) then @@ -220,11 +224,8 @@ local function check_wait(domain, wlist, ...) end local function dns_query(domain, ip_version) - local wlist = cos[domain] - if not wlist then - wlist = new_tab(16, 0) - cos[domain] = wlist - end + local wlist = new_tab(32, 0) + cos[domain] = wlist local dns_client, msg = get_dns_client(ip_version) if not dns_client then check_wait(domain, wlist, nil, msg) @@ -299,6 +300,7 @@ local function dns_query(domain, ip_version) local _, v = check_ip(ip) if v == 4 then ip = prefix..ip + answer.ip = ip end check_wait(domain, wlist, true, ip) return true, ip @@ -330,9 +332,13 @@ function dns.resolve(domain, ip_version) -- 如果是正确的ipv4地址直接返回 local ok, v = check_ip(domain) if ok then + -- 缓存IP->IP的映射, 减少查表与字符串连接次数. + -- 这样可以降低大量的内存与CPU的使用. if 6 == v then + add_cache(domain, domain) return ok, domain end + add_cache(domain, prefix..domain) return ok, prefix..domain end -- 如果有其他协程也正巧在查询这个域名, 那么就加入到等待列表内 From e590da4bbccd34be3e39d8177d734964bdc8c6fb Mon Sep 17 00:00:00 2001 From: "Mr.che" <869646063@qq.com> Date: Wed, 7 Jul 2021 15:18:03 +0800 Subject: [PATCH 794/956] Update laio.c --- luaclib/src/laio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/luaclib/src/laio.c b/luaclib/src/laio.c index 28efcefc..2757c0e7 100644 --- a/luaclib/src/laio.c +++ b/luaclib/src/laio.c @@ -725,7 +725,7 @@ static int laio_popen(lua_State *L) { // 监听`子进程`的退出事件 core_set_watcher_userdata(w, co); - core_child_init(w, CHILD_CB, pid, 0); + core_child_init(w, CHILD_CB, pid + luaL_optinteger(L, 3, 0), 0); core_child_start(core_default_loop(), w); // 返回一个包含`pipe`、`core_child`指针的`table`. return 1; From 9ffa6075776d432db9423bdf531314a215316144 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 8 Jul 2021 01:52:19 +0800 Subject: [PATCH 795/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BB=A3=E7=A0=81aio?= =?UTF-8?q?=E7=9A=84=E8=BF=9B=E7=A8=8B=E6=8E=A7=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/laio.c | 12 +++--------- src/core_ev.c | 4 +++- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/luaclib/src/laio.c b/luaclib/src/laio.c index 2757c0e7..98e6f697 100644 --- a/luaclib/src/laio.c +++ b/luaclib/src/laio.c @@ -664,10 +664,6 @@ static pid_t laio_system(lua_State *L, const char* command, int pfd) { // 完整独立进程会话ID setsid(); - // 重置子进程的信号掩码 - signal(SIGINT, SIG_DFL); signal(SIGHUP, SIG_DFL); - signal(SIGTERM, SIG_DFL); signal(SIGPIPE, SIG_DFL); - signal(SIGTSTP, SIG_DFL); signal(SIGQUIT, SIG_DFL); // 子进程需要设置为独立的输入输出管道; (void)dup2(pfd, STDIN_FILENO); (void)dup2(pfd, STDOUT_FILENO); @@ -686,15 +682,13 @@ static int laio_popen(lua_State *L) { if (!command || clen == 0) return luaL_error(L, "Invalid command.\n"); + // 协程回调 lua_State *co = lua_tothread(L, 2); int std[] = { -1, -1 }; if (socketpair(AF_UNIX, SOCK_STREAM, 0, std) < 0) return luaL_error(L, "Cand't create pipe.\n"); - // 设置非阻塞模式 - // non_blocking(std[0]); non_blocking(std[1]); - pid_t pid = laio_system(L, command, std[1]); if (pid < 1) { close(std[0]); close(std[1]); @@ -704,7 +698,7 @@ static int laio_popen(lua_State *L) { lua_createtable(L, 0, 2); // 记录子进程的`PID`. lua_pushliteral(L, "pid"); - lua_pushinteger(L, pid + luaL_optinteger(L, 3, 0)); + lua_pushinteger(L, pid); lua_rawset(L, -3); // 记录双向通信用到的`管道`; @@ -725,7 +719,7 @@ static int laio_popen(lua_State *L) { // 监听`子进程`的退出事件 core_set_watcher_userdata(w, co); - core_child_init(w, CHILD_CB, pid + luaL_optinteger(L, 3, 0), 0); + core_child_init(w, CHILD_CB, pid, 0); core_child_start(core_default_loop(), w); // 返回一个包含`pipe`、`core_child`指针的`table`. return 1; diff --git a/src/core_ev.c b/src/core_ev.c index 6c6bfdf4..d5209227 100644 --- a/src/core_ev.c +++ b/src/core_ev.c @@ -76,11 +76,13 @@ void core_child_stop(core_loop *loop, core_child *w){ core_loop* core_loop_fork(core_loop *loop) { - // ev_loop_fork(loop); + ev_loop_fork(loop); return loop; } core_loop* core_default_loop(){ + if (ev_default_loop_uc_()) + return ev_default_loop_uc_(); int BEST_BACKEND = 0; #if defined(__MSYS__) || defined(__CYGWIN__) BEST_BACKEND |= EVBACKEND_SELECT | EVFLAG_SIGNALFD | EVFLAG_NOINOTIFY; From 7319fb9990cd5f9f962bcc27c67d6e5275d85476 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 8 Jul 2021 01:52:36 +0800 Subject: [PATCH 796/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0aio=E7=9A=84=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/aio/init.lua | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lualib/aio/init.lua b/lualib/aio/init.lua index 3e052ab7..bf19d1c5 100644 --- a/lualib/aio/init.lua +++ b/lualib/aio/init.lua @@ -505,10 +505,9 @@ end ---comment @`io.popen`的非阻塞版实现 ---@param command string @`command`是一个`string`类型的参数, 它是用于执行的shell命令; ---@param timeout number @`timeout`是一个`Number`类型的参数(可选), 可以指定合适的超时时间来控制进程运行时长. ----@param daemond boolean @`daemond`是一个`boolean`类型的参数(可选), 它用来告诉框架应该计算合适的守护进程`PID`. ---@return table|boolean @子进程在退出后此方法才会返回; 正常退出返回`pfile`对象, 异常退出与错误退出返回`false`与出错信息; ---@return nil|string @需要注意的是`pfile`对象必须开发者手动关闭, 否则可能会造成`fd`泄漏的问题. -function aio.popen(command, timeout, daemond) +function aio.popen(command, timeout) local ok, obj, co_timer, killed local co = co_self() local co_cb = co_new(function (id) @@ -519,7 +518,7 @@ function aio.popen(command, timeout, daemond) end return co_wakeup(co, id == 0 and true or false) end) - ok, obj = pcall(aio_popen, command, co_cb, daemond and 1 or 0) + ok, obj = pcall(aio_popen, command, co_cb) if not ok then return false, "[AIO_POPEN ERROR] : " .. obj end @@ -547,8 +546,7 @@ end ---comment @`os.execute`的非阻塞版本实现, 它只执行期间也不会阻塞其它协程. ---@param command string @`command`是一个`string`类型的参数, 它是用于执行的shell命令; ---@param timeout number @`timeout`是一个`Number`类型的参数(可选), 可以指定合适的超时时间来控制进程运行时长. ----@param daemond boolean @`daemond`是一个`boolean`类型的参数(可选), 它用来告诉框架应该计算合适的守护进程`PID`. -function aio.execute(command, timeout, daemond) +function aio.execute(command, timeout) local ok, obj, co_timer local co = co_self() local co_cb = co_new(function (id) @@ -559,7 +557,7 @@ function aio.execute(command, timeout, daemond) end return co_wakeup(co, id == 0 and true or false) end) - ok, obj = pcall(aio_popen, command, co_cb, daemond and 1 or 0) + ok, obj = pcall(aio_popen, command, co_cb) if not ok then return false, "[AIO_POPEN ERROR] : " .. obj end From 7c221271a078bca79b992e8dc761ff37f6703cd2 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 15 Jul 2021 03:23:51 +0800 Subject: [PATCH 797/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8D=8F=E7=A8=8B?= =?UTF-8?q?=E5=86=85=E9=83=A8=E7=9A=84=E5=BC=82=E5=B8=B8=E6=A3=80=E6=9F=A5?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/cf/init.lua | 51 +++++++++++++++++++++++++----------------- lualib/internal/Co.lua | 36 +++++++++++++++++++++++------ 2 files changed, 59 insertions(+), 28 deletions(-) diff --git a/lualib/cf/init.lua b/lualib/cf/init.lua index dd6c1ebc..de56404e 100644 --- a/lualib/cf/init.lua +++ b/lualib/cf/init.lua @@ -5,63 +5,72 @@ local wait = Co.wait local wakeup = Co.wakeup local Timer = require "internal.Timer" -local at = Timer.at +local time_at = Timer.at local sleep = Timer.sleep local time_out = Timer.timeout +local tonumber = tonumber + local cf = {} --- 创建一个由cf管理的超时器 +---comment 创建一个由cf管理的超时器(只会触发一次) +---@param timeout number @`timeout`大于0才会创建定时器. +---@param func function @时间到期后将会调用此函数. function cf.timeout(timeout, func) return time_out(timeout, func) end --- 创建一个由cf管理的循环定时器 -function cf.at(repeats, func) - return at(repeats, func) +---comment 创建一个由cf管理的循环定时器(需要手动停止) +---@param timeout number @`timeout`大于0才会创建定时器. +---@param func function @间隔时间到期后将会调用此函数. +---@return table @返回一个`timer`对象, 调用`timer:stop()`方法可停止. +function cf.at(timeout, func) + return time_at(timeout, func) end --- 新增主动让出协程执行权的功能 +---comment 让出协程执行权 local function yield () local co = self() - fork(function (...) + fork(function () wakeup(co) end) return wait() end cf.yield = yield --- 协程休眠指定时间 -function cf.sleep(time) - if time == 0 then - return yield() +---comment 让出当前协程执行权并休眠`timeout`秒 +---@param timeout number @`timeout`大于0才会创建定时器. +function cf.sleep(timeout) + timeout = tonumber(timeout) + if timeout and timeout > 0 then + return sleep(timeout) end - return sleep(time) + return yield() end - +---comment 获取调用此方法的协程对象. +---@return thread function cf.self () return self() end --- 让出协程 +---comment 让出协程 function cf.wait() return wait() end --- 创建一个由cf框架调度的协程 +---comment 创建一个由cf框架调度的协程 +---@param func function @协程的执行的函数 +---@return thread 协程对象 function cf.fork(func, ...) return fork(func, ...) end --- 唤醒一个由cf框架创建的协程 +---comment 唤醒一个由cf框架创建的协程 +---@param co thread @被唤醒的协程对象与需要传递给协程的参数 +---@return nil function cf.wakeup(co, ...) return wakeup(co, ...) end --- 使用cf内置dns来解析域名, version表示想得到ipv6回应还是ipv4回应 -function cf.resolve(domain, version) - -- TODO -end - return cf diff --git a/lualib/internal/Co.lua b/lualib/internal/Co.lua index a37901f7..fe01f166 100644 --- a/lualib/internal/Co.lua +++ b/lualib/internal/Co.lua @@ -7,6 +7,7 @@ local sys = require "sys" local new_tab = sys.new_tab local type = type +local error = error local print = print local assert = assert local select = select @@ -15,7 +16,6 @@ local os_date = os.date local fmt = string.format local dbg_traceback = debug.traceback -local tpack = table.pack local tunpack = table.unpack local coroutine = coroutine @@ -33,14 +33,14 @@ local main_waited = true local co_num = 0 local co_map = new_tab(0, 1024) -co_map[co_self()] = true +co_map[co_self()] = {co_self(), nil, false} local co_wlist = new_tab(512, 0) local function co_wrapper() return co_new(function () -- 使用`数字索引`比`Hash索引`更快. - local CO_INDEX, ARGS_INDEX = 1, 2 + local CO_INDEX, ARGS_INDEX, WAKEUP_INDEX = 1, 2, 3 -- 使用数字下标迭代比`ipairs`更快. local start, total = 1, #co_wlist -- 使用两级`FIFO`队列交替管理协程的运行与切换, 并且每次预分配的`FIFO`队列的大小与上次执行的协程的数量相关. @@ -64,6 +64,7 @@ local function co_wrapper() co_map[co] = nil co_num = co_num - 1 end + obj[ARGS_INDEX], obj[WAKEUP_INDEX] = nil, false end -- 如果没有执行对象则应该放弃执行权. -- 等待有任务之后再次唤醒后再执行 @@ -95,9 +96,16 @@ end local function co_add_queue(co, ...) local args = nil if select("#", ...) > 0 then - args = tpack(...) + args = {...} end - co_wlist[#co_wlist+1] = {co, args} + local ctx = co_map[co] + if not ctx then + ctx = {co, args, true} + co_map[co] = ctx + else + ctx[2], ctx[3] = args, true + end + co_wlist[#co_wlist+1] = ctx end local Co = {} @@ -120,7 +128,22 @@ end -- 唤醒协程 function Co.wakeup(co, ...) - assert(type(co) == 'thread' and co ~= co_self() and co_map[co], "[coroutine error]: Invcalid coroutine.") + if type(co) ~= 'thread' then + error("[coroutine error]: Invalid coroutine.") + end + if co == co_self() then + error("[coroutine error]: Cannot wake up a running coroutine.") + end + if co_status(co) ~= 'suspended' then + error("[coroutine error]: Invalid status coroutine. [" .. co_status(co) .. "]") + end + local ctx = co_map[co] + if not ctx then + error("[coroutine error]: This coroutine is not associated internally, so it cannot wakeup.") + end + if ctx[3] then + error("[coroutine error]: Try to wake up a coroutine several times.") + end co_check_init() co_add_queue(co, ...) end @@ -131,7 +154,6 @@ function Co.spawn(func, ...) -- 创建协程与打包参数 co_num = co_num + 1 local co = co_new(func) - co_map[co] = true co_check_init() co_add_queue(co, ...) return co From c6a51409175753e2c6ca435076c77c9e34b735a0 Mon Sep 17 00:00:00 2001 From: "Mr.che" <869646063@qq.com> Date: Mon, 19 Jul 2021 13:08:33 +0800 Subject: [PATCH 798/956] Update lzlib.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优化zlib在内部性能与内存占用问题. --- luaclib/src/lz/lzlib.c | 323 ++++++++++++++++++----------------------- 1 file changed, 141 insertions(+), 182 deletions(-) diff --git a/luaclib/src/lz/lzlib.c b/luaclib/src/lz/lzlib.c index 476e73eb..f2149735 100644 --- a/luaclib/src/lz/lzlib.c +++ b/luaclib/src/lz/lzlib.c @@ -3,240 +3,199 @@ #include #include -#define MAX_COMPRESS_BUF_SIZE_TIMES (1 << 10) - +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +// 压缩模式 +enum zmode { + Z_COMPRESS1_MODE = 0, + Z_COMPRESS2_MODE = 1, + Z_GZCOMPRESS_MODE = 2, +}; + +// 压缩窗口大小 +enum zwsize { + Z_COMPRESS1_WSIZE = MAX_WBITS, + Z_COMPRESS2_WSIZE = -MAX_WBITS, + Z_GZCOMPRESS_WSIZE = MAX_WBITS + 16, +}; + +// 初始化 static inline void stream_init(z_stream *z) { - memset(z, 0x0, sizeof(*z)); + memset(z, 0x0, sizeof(z_stream)); z->zalloc = Z_NULL; z->zfree = Z_NULL; z->opaque = Z_NULL; } -static int lcompress(lua_State *L) { - size_t in_size = 0; - const uint8_t* in = (const uint8_t*)luaL_checklstring(L, 1, &in_size); - if (in_size <= 0) - return 0; +// 压缩 +static inline int stream_deflate(lua_State* L, int mode, const uint8_t* in, size_t in_size) { - size_t out_size = compressBound(in_size); - uint8_t *out = lua_newuserdata(L, out_size); - memset(out, 0x0, out_size); + z_stream z; + stream_init(&z); - if (compress(out, &out_size, in, in_size) != Z_OK) - return 0; + size_t out_size; + uint8_t *out; - lua_pushlstring(L, (const char*)out, out_size); - lua_pushinteger(L, in_size); - return 2; -} + switch (mode) { + case Z_COMPRESS1_MODE: + if (Z_OK != deflateInit2(&z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, Z_COMPRESS1_WSIZE, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY)) + return luaL_error(L, "[ZLIB ERROR]: Compress1 init failed."); -static int luncompress(lua_State *L) { - size_t in_size = 0; - const uint8_t* in = (const uint8_t*)luaL_checklstring(L, 1, &in_size); - if (in_size <= 0) - return 0; - - size_t out_size = in_size * 4; - - /* 若能传递压缩前的大小, 优先使用此数值 */ - int is_sum = 0; - lua_Integer before_size = lua_tointegerx(L, 2, &is_sum); - if (is_sum && before_size > 0 ) - out_size = before_size; - - size_t offset = 4; - size_t top = lua_gettop(L); - - do { - uint8_t *out = lua_newuserdata(L, out_size); - memset(out, 0x0, out_size); - - int ret = uncompress(out, &out_size, in, in_size); - if (ret == Z_OK || ret == Z_BUF_ERROR) { - if (ret == Z_OK){ - lua_pushlstring(L, (const char *)out, out_size); - return 1; - } - lua_settop(L, top); - offset ++; - out_size = in_size << offset; - continue; - } - } while(0); - return 0; -} - -static int lgzip_compress(lua_State *L) { - size_t in_size = 0; - const char* in = luaL_checklstring(L, 1, &in_size); - if (in_size <= 0) - return 0; + // 输出 + out_size = compressBound(in_size); + out = lua_newuserdata(L, out_size); + break; + case Z_COMPRESS2_MODE: + if (Z_OK != deflateInit2(&z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, Z_COMPRESS2_WSIZE, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY)) + return luaL_error(L, "[ZLIB ERROR]: Compress2 init failed."); - z_stream z; - stream_init(&z); + // 输出 + out_size = deflateBound(&z, in_size); + out = lua_newuserdata(L, out_size); + break; + case Z_GZCOMPRESS_MODE: + if (Z_OK != deflateInit2(&z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, Z_GZCOMPRESS_WSIZE, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY)) + return luaL_error(L, "[ZLIB ERROR]: GzCompress init failed."); - int ok = deflateInit2(&z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + 16, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); - if (Z_OK != ok) - return 0; + // 输出 + out_size = deflateBound(&z, in_size); + out = lua_newuserdata(L, out_size); + break; + default: + return luaL_error(L, "[ZLIB ERROR]: Invalid zlib mode."); + } + // 输入 z.next_in = (uint8_t *)in; z.avail_in = in_size; - luaL_Buffer B; - z.avail_out = deflateBound(&z, in_size); - z.next_out = (uint8_t *)luaL_buffinitsize(L, &B, z.avail_out); - memset(z.next_out, 0x0, z.avail_out); + z.next_out = out; + z.avail_out = out_size; int ret = deflate(&z, Z_FINISH); + // 压缩 if (ret != Z_STREAM_END) { - luaL_pushresultsize(&B, 0); deflateEnd(&z); - return 0; + return luaL_error(L, "[ZLIB ERROR]: deflate error(%d).", ret); } + // 清理 if (deflateEnd(&z) != Z_OK){ - luaL_pushresultsize(&B, 0); - return 0; + return luaL_error(L, "[ZLIB ERROR]: deflateEnd error(%d).", ret); } - luaL_pushresultsize(&B, z.total_out); - return 1; + // 结束 + lua_pushlstring(L, (const char*)out, z.total_out); + lua_pushinteger(L, in_size); + return 2; } -static int lgzip_uncompress(lua_State *L) { - size_t in_size = 0; - const char* in = luaL_checklstring(L, 1, &in_size); - if (in_size <= 0) - return 0; - +// 解缩 +static inline int stream_inflate(lua_State* L, int windsize, const uint8_t* in, size_t in_size) { z_stream z; stream_init(&z); - int ok = inflateInit2(&z, MAX_WBITS + 16); - if (Z_OK != ok) + z.next_in = (uint8_t *)in; + z.avail_in = in_size; + + if (Z_OK != inflateInit2(&z, windsize)) return 0; - size_t out_size = in_size << 1; - int top = lua_gettop(L); - - for(;;) { - luaL_Buffer B; - z.next_in = (uint8_t *)in; - z.avail_in = in_size; - z.avail_out = out_size; - z.next_out = (uint8_t *)luaL_buffinitsize(L, &B, out_size); - memset(z.next_out, 0x0, z.avail_out); - - int ret = inflate(&z, Z_FINISH); - if (ret == Z_STREAM_END) { - int ok = inflateEnd(&z); - if (ok != Z_OK) - return 0; - luaL_pushresultsize(&B, z.total_out); + luaL_Buffer B; + luaL_buffinit(L, &B); + + int bszie = 65535; + uint8_t buffer[bszie]; + + uint64_t offset = 0; + + for (;;) { + z.next_out = buffer; + z.avail_out = bszie; + // 始终用最小的内存来解压数据. + int ret = inflate(&z, Z_NO_FLUSH); + // 如果已经到数据流的尾部. + if (ret == Z_STREAM_END){ + luaL_addlstring(&B, (const char*)buffer, z.total_out - offset); + offset = z.total_out; break; } - if (ret != Z_BUF_ERROR) { - luaL_pushresultsize(&B, 0); + // 如果数据出现其他异常情况 + if (ret != Z_OK){ inflateEnd(&z); - return 0; + lua_pushboolean(L, 0); + lua_pushfstring(L, "[ZLIB ERROR]: Invalid inflate buffer. %d", ret); + return 2; } - /* 防止内存溢出 */ - if (out_size > in_size * MAX_COMPRESS_BUF_SIZE_TIMES){ - luaL_pushresultsize(&B, 0); - inflateEnd(&z); - return 0; - } - inflateReset(&z); - out_size <<= 1; - luaL_pushresultsize(&B, 0); - lua_settop(L, top); + // printf("inline : [ret] = %d, isize = [%ld], osize = [%ld]\n", ret, z.total_in, z.total_out); + // 每次迭代都需要把缓冲区的数据添加到内部. + luaL_addlstring(&B, (const char*)buffer, z.total_out - offset); + offset = z.total_out; + } + + int ret = inflateEnd(&z); + if (ret != Z_OK){ + lua_pushboolean(L, 0); + lua_pushfstring(L, "[ZLIB ERROR]: Invalid inflateEnd buffer. %d", ret); + return 2; } + // printf("over: [ret] = %d, isize = [%ld], osize = [%ld]\n", ret, z.total_in, z.total_out); + luaL_pushresult(&B); return 1; } -static int lcompress2(lua_State *L) { +static int lcompress(lua_State *L) { size_t in_size = 0; - const char* in = luaL_checklstring(L, 1, &in_size); + const uint8_t* in = (const uint8_t*)luaL_checklstring(L, 1, &in_size); if (in_size <= 0) - return 0; + return luaL_error(L, "[ZLIB ERROR]: Invalid in buffer."); - z_stream z; - stream_init(&z); + return stream_deflate(L, Z_COMPRESS1_MODE, (const uint8_t*)in, in_size); +} - int ok = deflateInit2(&z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS * -1, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); - if (Z_OK != ok) - return 0; +static int luncompress(lua_State *L) { + size_t in_size = 0; + const uint8_t* in = (const uint8_t*)luaL_checklstring(L, 1, &in_size); + if (in_size <= 0) + return luaL_error(L, "[ZLIB ERROR]: Invalid in buffer."); - z.next_in = (uint8_t *)in; - z.avail_in = in_size; + return stream_inflate(L, Z_COMPRESS1_WSIZE, (const uint8_t*)in, in_size); +} - luaL_Buffer B; - z.avail_out = deflateBound(&z, in_size); - z.next_out = (uint8_t *)luaL_buffinitsize(L, &B, z.avail_out); - memset(z.next_out, 0x0, z.avail_out); +static int lcompress2(lua_State *L) { + size_t in_size = 0; + const uint8_t* in = (const uint8_t*)luaL_checklstring(L, 1, &in_size); + if (in_size <= 0) + return luaL_error(L, "[ZLIB ERROR]: Invalid in buffer."); - int ret = deflate(&z, Z_FINISH); - if (ret != Z_STREAM_END) { - luaL_pushresultsize(&B, 0); - deflateEnd(&z); - return 0; - } - if (deflateEnd(&z) != Z_OK){ - luaL_pushresultsize(&B, 0); - return 0; - } - luaL_pushresultsize(&B, z.total_out); - lua_pushinteger(L, in_size); - return 2; + return stream_deflate(L, Z_COMPRESS2_MODE, (const uint8_t*)in, in_size); } static int luncompress2(lua_State *L) { size_t in_size = 0; - const char* in = luaL_checklstring(L, 1, &in_size); + const uint8_t* in = (const uint8_t*)luaL_checklstring(L, 1, &in_size); if (in_size <= 0) - return 0; + return luaL_error(L, "[ZLIB ERROR]: Invalid in buffer."); - z_stream z; - stream_init(&z); + return stream_inflate(L, Z_COMPRESS2_WSIZE, (const uint8_t*)in, in_size); +} - int ok = inflateInit2(&z, MAX_WBITS * -1); - if (Z_OK != ok) - return 0; +static int lgzip_compress(lua_State *L) { + size_t in_size = 0; + const uint8_t* in = (const uint8_t*)luaL_checklstring(L, 1, &in_size); + if (in_size <= 0) + return luaL_error(L, "[ZLIB ERROR]: Invalid in buffer."); - size_t out_size = in_size * 4; - int top = lua_gettop(L); - - for(;;) { - luaL_Buffer B; - z.next_in = (uint8_t *)in; - z.avail_in = in_size; - z.avail_out = out_size; - z.next_out = (uint8_t *)luaL_buffinitsize(L, &B, out_size); - memset(z.next_out, 0x0, z.avail_out); - - int ret = inflate(&z, Z_FINISH); - if (ret == Z_STREAM_END) { - int ok = inflateEnd(&z); - if (ok != Z_OK) - return 0; - luaL_pushresultsize(&B, z.total_out); - break; - } - if (ret != Z_BUF_ERROR) { - luaL_pushresultsize(&B, 0); - inflateEnd(&z); - return 0; - } - /* 防止内存溢出 */ - if (out_size > in_size * MAX_COMPRESS_BUF_SIZE_TIMES){ - luaL_pushresultsize(&B, 0); - inflateEnd(&z); - return 0; - } - inflateReset(&z); - out_size *= 2; - luaL_pushresultsize(&B, 0); - lua_settop(L, top); - } - return 1; + return stream_deflate(L, Z_GZCOMPRESS_MODE, (const uint8_t*)in, in_size); +} + +static int lgzip_uncompress(lua_State *L) { + size_t in_size = 0; + const uint8_t* in = (const uint8_t*)luaL_checklstring(L, 1, &in_size); + if (in_size <= 0) + return luaL_error(L, "[ZLIB ERROR]: Invalid in buffer."); + + return stream_inflate(L, Z_GZCOMPRESS_WSIZE, (const uint8_t*)in, in_size); } LUAMOD_API int luaopen_lz(lua_State *L){ @@ -255,4 +214,4 @@ LUAMOD_API int luaopen_lz(lua_State *L){ }; luaL_newlib(L, zlib_libs); return 1; -} \ No newline at end of file +} From d9b7e3038d86459d9aed650181c3f834e7ce4125 Mon Sep 17 00:00:00 2001 From: "Mr.che" <869646063@qq.com> Date: Mon, 19 Jul 2021 15:27:54 +0800 Subject: [PATCH 799/956] Update laio.c Resolve some warnings. --- luaclib/src/laio.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/luaclib/src/laio.c b/luaclib/src/laio.c index 98e6f697..c67fdce3 100644 --- a/luaclib/src/laio.c +++ b/luaclib/src/laio.c @@ -669,8 +669,9 @@ static pid_t laio_system(lua_State *L, const char* command, int pfd) { (void)dup2(pfd, STDOUT_FILENO); (void)dup2(pfd, STDERR_FILENO); // 子进程需要进与父子进程的的上下文分离 - if (execl("/bin/sh", "sh", "-c", command, NULL)) - write(STDOUT_FILENO, strerror(errno), strlen(strerror(errno))); + if (execl("/bin/sh", "sh", "-c", command, NULL)){ + int wsize = write(STDOUT_FILENO, strerror(errno), strlen(strerror(errno)));(void)wsize; + } // 正常执行完毕是不会走到这里, 所以只能是执行失败. exit(EXIT_FAILURE); } From a4d1045ad14290b82b04a229535da2982ba28fdf Mon Sep 17 00:00:00 2001 From: "Mr.che" <869646063@qq.com> Date: Tue, 20 Jul 2021 16:42:19 +0800 Subject: [PATCH 800/956] Update core_memory.h --- src/core_memory.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/core_memory.h b/src/core_memory.h index 0096c559..fd68c30a 100644 --- a/src/core_memory.h +++ b/src/core_memory.h @@ -2,9 +2,15 @@ #define __CORE_MEMORY__ #if defined(JEMALLOC) - #include "jemalloc/jemalloc.h" + #include #elif defined(TCMALLOC) - #include "gperftools/tcmalloc.h" + #include + #ifndef EXIT_SUCCESS + #define EXIT_SUCCESS (0) + #endif + #ifndef EXIT_FAILURE + #define EXIT_FAILURE (1) + #endif #define malloc tc_malloc #define calloc tc_calloc #define realloc tc_realloc @@ -28,4 +34,4 @@ void* xrealloc(void* ptr, size_t size); void xfree(void *ptr); -#endif \ No newline at end of file +#endif From b2cd63a2f169d48729406a5f68332725715f8133 Mon Sep 17 00:00:00 2001 From: "Mr.che" <869646063@qq.com> Date: Tue, 20 Jul 2021 19:20:42 +0800 Subject: [PATCH 801/956] Update core_start.c --- src/core_start.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core_start.c b/src/core_start.c index 1ad0e8c3..53f34a48 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -167,7 +167,7 @@ static inline void cfadmin_set_cpu_affinity(int num, pid_t pid) { #include if (nprocess <= sysconf(_SC_NPROCESSORS_ONLN)){ /* 在Linux会尝试绑定CPU亲缘性以提高进程执行效率. */ - cpu_set_t mask; CPU_ZERO(&mask); CPU_SET((num + 1) % nprocess, &mask); + cpu_set_t mask; CPU_ZERO(&mask); CPU_SET((num + 1) % sysconf(_SC_NPROCESSORS_ONLN), &mask); sched_setaffinity(pid, sizeof(mask), (const cpu_set_t *)&mask); } #elif defined(__FreeBSD__) @@ -175,7 +175,7 @@ static inline void cfadmin_set_cpu_affinity(int num, pid_t pid) { #include if (nprocess <= sysconf(_SC_NPROCESSORS_ONLN)){ /* FreeBSD的CPU绑定的方式有函数与头文件的差异 */ - cpuset_t mask; CPU_ZERO(&mask); CPU_SET((num + 1) % nprocess, &mask); + cpuset_t mask; CPU_ZERO(&mask); CPU_SET((num + 1) % sysconf(_SC_NPROCESSORS_ONLN), &mask); cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(cpuset_t), (const cpuset_t *)&mask); } #endif @@ -337,4 +337,4 @@ int main(int argc, char const *argv[]) { /* 如果失败就删除pid文件*/ return remove(pid_filename); -} \ No newline at end of file +} From 5d458cf7b3ea5330c18d3f05599cdd028bef3989 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 21 Jul 2021 00:42:38 +0800 Subject: [PATCH 802/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=B8=B8=E7=94=A8buf?= =?UTF-8?q?fer=E7=9A=84=E6=95=B0=E6=8D=AE=E8=AF=BB=E5=8F=96=E6=B5=81?= =?UTF-8?q?=E7=A8=8B.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 9 +++--- lualib/protocol/mssql.lua | 40 ++++++++++++++------------ lualib/protocol/mysql.lua | 35 +++++++++++++--------- lualib/protocol/pgsql.lua | 35 +++++++++++++--------- lualib/protocol/redis.lua | 35 +++++++++++++--------- lualib/protocol/stomp/protocol.lua | 37 +++++++++++++++--------- lualib/protocol/websocket/protocol.lua | 35 +++++++++++++--------- 7 files changed, 138 insertions(+), 88 deletions(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index 5d409ab4..646141b3 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -139,13 +139,14 @@ local function safe_call (f, ...) end local function readall(sock, bsize, buffers) + local sock_recv = sock.recv while 1 do - local buf = sock:recv(bsize) - if not buf then + local buffer = sock_recv(sock, bsize) + if not buffer then return end - bsize = bsize - #buf - buffers[#buffers + 1] = buf + bsize = bsize - #buffer + insert(buffers, buffer) if bsize == 0 then break end diff --git a/lualib/protocol/mssql.lua b/lualib/protocol/mssql.lua index 610e08d9..ac46007b 100644 --- a/lualib/protocol/mssql.lua +++ b/lualib/protocol/mssql.lua @@ -31,6 +31,7 @@ local toint = math.tointeger local os_date = os.date +local tabinsert = table.insert local tabconcat = table.concat -- TDS公共头部类型 @@ -884,25 +885,28 @@ function mssql:ctor(opt) end function mssql:read( bytes ) - local buffers = new_tab(32, 0) local sock = self.sock - while 1 do - local data - if sock.ssl then - data = sock:ssl_recv(bytes) - else - data = sock:recv(bytes) - end - if not data then - return nil, "server close this session." - end - buffers[#buffers+1] = data - bytes = bytes - #data - if bytes <= 0 then - break - end - end - return tabconcat(buffers) + local buffer = sock:recv(bytes) + if not buffer then + return + end + if #buffer == bytes then + return buffer + end + bytes = bytes - #buffer + local buffers = {buffer} + local sock_read = sock.recv + while 1 do + buffer = sock_read(sock, bytes) + if not buffer then + return + end + bytes = bytes - #buffer + tabinsert(buffers, buffer) + if bytes == 0 then + return tabconcat(buffers) + end + end end function mssql:write(data) diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua index 17b083dd..9d1a634b 100644 --- a/lualib/protocol/mysql.lua +++ b/lualib/protocol/mysql.lua @@ -25,6 +25,7 @@ local assert = assert local select = select local tonumber = tonumber local toint = math.tointeger +local insert = table.insert local concat = table.concat local null = null @@ -125,19 +126,27 @@ end local function sock_read (self, bytes) local sock = self.sock - local buffers = new_tab(32, 0) - while 1 do - local buf = sock:recv(bytes) - if not buf then - return nil, "MySQL Server closed." - end - buffers[#buffers+1] = buf - bytes = bytes - #buf - if bytes == 0 then - break - end - end - return concat(buffers) + local buffer = sock:recv(bytes) + if not buffer then + return + end + if #buffer == bytes then + return buffer + end + bytes = bytes - #buffer + local buffers = {buffer} + local sock_recv = sock.recv + while 1 do + buffer = sock_recv(sock, bytes) + if not buffer then + return + end + bytes = bytes - #buffer + insert(buffers, buffer) + if bytes == 0 then + return concat(buffers) + end + end end -- mysql_native认证 diff --git a/lualib/protocol/pgsql.lua b/lualib/protocol/pgsql.lua index c6f0f827..2a0b5428 100644 --- a/lualib/protocol/pgsql.lua +++ b/lualib/protocol/pgsql.lua @@ -16,6 +16,7 @@ local tonumber = tonumber local fmt = string.format local toint = math.tointeger +local tinsert = table.insert local tconcat = table.concat local string = string @@ -440,19 +441,27 @@ end function pgsql:read(bytes) local sock = self.sock - local buffers = new_tab(32, 0) - while 1 do - local data = sock:recv(bytes) - if not data then - return nil, "server close this session." - end - buffers[#buffers+1] = data - bytes = bytes - #data - if bytes <= 0 then - break - end - end - return tconcat(buffers) + local buffer = sock:recv(bytes) + if not buffer then + return + end + if #buffer == bytes then + return buffer + end + bytes = bytes - #buffer + local buffers = {buffer} + local sock_read = sock.recv + while 1 do + buffer = sock_read(sock, bytes) + if not buffer then + return + end + bytes = bytes - #buffer + tinsert(buffers, buffer) + if bytes == 0 then + return tconcat(buffers) + end + end end function pgsql:write(data) diff --git a/lualib/protocol/redis.lua b/lualib/protocol/redis.lua index 8119a4db..cad89645 100644 --- a/lualib/protocol/redis.lua +++ b/lualib/protocol/redis.lua @@ -16,6 +16,7 @@ local assert = assert local tonumber = tonumber local tostring = tostring +local insert = table.insert local concat = table.concat local unpack = table.unpack @@ -38,19 +39,27 @@ local function read_response(sock) end local function sock_readbytes(sock, bytes) - local buffers = new_tab(16, 0) - while 1 do - local data = sock:recv(bytes) - if not data then - sock.state = false - return nil, 'server close!!' - end - buffers[#buffers+1] = data - if #data == bytes then - return concat(buffers) - end - bytes = bytes - #data - end + local buffer = sock:recv(bytes) + if not buffer then + return + end + if #buffer == bytes then + return buffer + end + bytes = bytes - #buffer + local buffers = {buffer} + local sock_read = sock.recv + while 1 do + buffer = sock_read(sock, bytes) + if not buffer then + return + end + bytes = bytes - #buffer + insert(buffers, buffer) + if bytes == 0 then + return concat(buffers) + end + end end redcmd[36] = function(sock, data) -- '$' diff --git a/lualib/protocol/stomp/protocol.lua b/lualib/protocol/stomp/protocol.lua index d3db98a7..e706a834 100644 --- a/lualib/protocol/stomp/protocol.lua +++ b/lualib/protocol/stomp/protocol.lua @@ -1,6 +1,7 @@ local pairs = pairs local toint = math.tointeger local splite = string.gmatch +local insert = table.insert local concat = table.concat local LF = '\x0a' @@ -42,20 +43,28 @@ local function sock_send (sock, data) return sock:send(data) end -local function sock_read (sock, byte) - local buffers = {} - while true do - local buf = sock:recv(byte) - if not buf then - return - end - buffers[#buffers+1] = buf - byte = byte - #buf - if byte == 0 then - break - end - end - return concat(buffers) +local function sock_read (sock, bytes) + local buffer = sock:recv(bytes) + if not buffer then + return + end + if #buffer == bytes then + return buffer + end + bytes = bytes - #buffer + local buffers = {buffer} + local sock_recv = sock.recv + while 1 do + buffer = sock_recv(sock, bytes) + if not buffer then + return + end + bytes = bytes - #buffer + insert(buffers, buffer) + if bytes == 0 then + return concat(buffers) + end + end end local function sock_readline(sock, sp, nosp) diff --git a/lualib/protocol/websocket/protocol.lua b/lualib/protocol/websocket/protocol.lua index 9b5d65b2..36e315b1 100644 --- a/lualib/protocol/websocket/protocol.lua +++ b/lualib/protocol/websocket/protocol.lua @@ -14,6 +14,7 @@ local strpack = string.pack local strunpack = string.unpack local random = math.random local concat = table.concat +local insert = table.insert local WS_TYPE = { [0x00] = "continuation", @@ -25,19 +26,27 @@ local WS_TYPE = { } local function sock_recv (sock, bytes) - local buffers = new_tab(16, 0) - while true do - local buf = sock:recv(bytes) - if not buf then - return nil - end - buffers[#buffers+1] = buf - bytes = bytes - #buf - if bytes == 0 then - break - end - end - return concat(buffers) + local buffer = sock:recv(bytes) + if not buffer then + return + end + if #buffer == bytes then + return buffer + end + bytes = bytes - #buffer + local buffers = {buffer} + local sock_read = sock.recv + while 1 do + buffer = sock_read(sock, bytes) + if not buffer then + return + end + bytes = bytes - #buffer + insert(buffers, buffer) + if bytes == 0 then + return concat(buffers) + end + end end local function sock_send (sock, data) From e547c05fefb03536784786faa2ebe7738c7836a3 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 21 Jul 2021 01:01:39 +0800 Subject: [PATCH 803/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=A1=86=E6=9E=B6?= =?UTF-8?q?=E8=AF=BB=E5=8F=96buffer=E7=9A=84=E6=95=88=E7=8E=87.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 37 ++++++++++++++++++++++++++----------- luaclib/src/ludp.c | 9 +++++++-- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 50df916c..5f96554f 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -5,6 +5,10 @@ #include #include +#ifndef alloca + #define alloca __alloca +#endif + #define None (-1) #define SERVER (0) #define CLIENT (1) @@ -508,7 +512,11 @@ static int tcp_peek(lua_State *L) { if (0 >= fd) return 0; int bsize = lua_tointeger(L, 2); - char buffer[bsize]; + char* buffer = NULL; + if (bsize <= 65535) + buffer = alloca(65535); + else + buffer = lua_newuserdata(L, bsize); if (0 == lua_toboolean(L, 3)) { lua_pushinteger(L, read(fd, buffer, bsize)); @@ -542,7 +550,11 @@ static int tcp_sslpeek(lua_State *L) { if (!ssl) return 0; int bsize = lua_tointeger(L, 2); - char buffer[bsize]; + char* buffer = NULL; + if (bsize <= 65535) + buffer = alloca(65535); + else + buffer = lua_newuserdata(L, bsize); if (0 == lua_toboolean(L, 3)) { lua_pushinteger(L, SSL_read(ssl, buffer, bsize)); @@ -580,10 +592,12 @@ static int tcp_read(lua_State *L){ return 0; errno = 0; - char* str = alloca(1 << 16); - if (bytes > (1 << 16)){ - str = (char*)lua_newuserdata(L, bytes); - } + char* str = NULL; + if (bytes <= 65535) + str = alloca(65535); + else + str = alloca(1048576); + do { int rsize = read(fd, str, bytes); if (rsize > 0) { @@ -614,12 +628,13 @@ static int tcp_sslread(lua_State *L){ if (0 >= bytes) return 0; - char* str = alloca(1 << 16); - if (bytes > (1 << 16)){ - str = (char*)lua_newuserdata(L, bytes); - } - errno = 0; + char* str = NULL; + if (bytes <= 65535) + str = alloca(65535); + else + str = alloca(1048576); + do { int rsize = SSL_read(ssl, str, bytes); if (0 < rsize) { diff --git a/luaclib/src/ludp.c b/luaclib/src/ludp.c index 94e71f34..0f0c355c 100644 --- a/luaclib/src/ludp.c +++ b/luaclib/src/ludp.c @@ -2,6 +2,10 @@ #include +#ifndef alloca + #define alloca __alloca +#endif + static inline void SETSOCKETOPT(int sockfd) { int Enable = 1; int ret = 0; @@ -101,8 +105,9 @@ static int udp_recv(lua_State *L){ int fd = lua_tointeger(L, 1); if (fd < 0) return 0; - char str[4096] = {0}; - int rsize = read(fd, str, 4096); + int bsize = 65535; + char* str = alloca(65535); + int rsize = read(fd, str, bsize); if (rsize < 0) return 0; lua_pushlstring(L, str, rsize); From 54f4e260ef1ca8fe4b5c271ea4b3de1daf54ebc4 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 21 Jul 2021 01:05:07 +0800 Subject: [PATCH 804/956] =?UTF-8?q?=E8=B0=83=E6=95=B4=E7=BC=93=E5=AD=98?= =?UTF-8?q?=E8=AF=BB=E5=8F=96=E6=97=B6=E7=9A=84=E6=A0=88=E5=86=85=E5=AD=98?= =?UTF-8?q?=E5=88=86=E9=85=8D=E5=A4=A7=E5=B0=8F.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 5f96554f..af7ef3ef 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -596,7 +596,7 @@ static int tcp_read(lua_State *L){ if (bytes <= 65535) str = alloca(65535); else - str = alloca(1048576); + str = alloca(262144); do { int rsize = read(fd, str, bytes); @@ -633,7 +633,7 @@ static int tcp_sslread(lua_State *L){ if (bytes <= 65535) str = alloca(65535); else - str = alloca(1048576); + str = alloca(262144); do { int rsize = SSL_read(ssl, str, bytes); From b7e2dc21f9ee49f5fb2483879a8ed1273b4fa87b Mon Sep 17 00:00:00 2001 From: "Mr.che" <869646063@qq.com> Date: Wed, 21 Jul 2021 12:56:13 +0800 Subject: [PATCH 805/956] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=A0=88=E7=94=B3?= =?UTF-8?q?=E8=AF=B7=E5=A4=A7=E5=B0=8F=E4=B8=8E=E6=9D=A1=E4=BB=B6=E5=88=A4?= =?UTF-8?q?=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index af7ef3ef..f4b7c2d5 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -513,8 +513,8 @@ static int tcp_peek(lua_State *L) { int bsize = lua_tointeger(L, 2); char* buffer = NULL; - if (bsize <= 65535) - buffer = alloca(65535); + if (bsize <= 262144) + buffer = alloca(bsize); else buffer = lua_newuserdata(L, bsize); @@ -551,8 +551,8 @@ static int tcp_sslpeek(lua_State *L) { int bsize = lua_tointeger(L, 2); char* buffer = NULL; - if (bsize <= 65535) - buffer = alloca(65535); + if (bsize <= 262144) + buffer = alloca(bsize); else buffer = lua_newuserdata(L, bsize); @@ -593,10 +593,10 @@ static int tcp_read(lua_State *L){ errno = 0; char* str = NULL; - if (bytes <= 65535) - str = alloca(65535); + if (bytes <= 262144) + str = alloca(bytes); else - str = alloca(262144); + str = lua_newuserdata(L, bytes); do { int rsize = read(fd, str, bytes); @@ -630,10 +630,10 @@ static int tcp_sslread(lua_State *L){ errno = 0; char* str = NULL; - if (bytes <= 65535) - str = alloca(65535); + if (bytes <= 262144) + str = alloca(bytes); else - str = alloca(262144); + str = lua_newuserdata(L, bytes); do { int rsize = SSL_read(ssl, str, bytes); From 63ef407263031744738195e2617e565604ffc92a Mon Sep 17 00:00:00 2001 From: "Mr.che" <869646063@qq.com> Date: Mon, 26 Jul 2021 16:33:30 +0800 Subject: [PATCH 806/956] Update lzlib.c --- luaclib/src/lz/lzlib.c | 37 ++++++++----------------------------- 1 file changed, 8 insertions(+), 29 deletions(-) diff --git a/luaclib/src/lz/lzlib.c b/luaclib/src/lz/lzlib.c index f2149735..80acae39 100644 --- a/luaclib/src/lz/lzlib.c +++ b/luaclib/src/lz/lzlib.c @@ -16,11 +16,13 @@ enum zmode { // 压缩窗口大小 enum zwsize { - Z_COMPRESS1_WSIZE = MAX_WBITS, + Z_COMPRESS1_WSIZE = +MAX_WBITS, Z_COMPRESS2_WSIZE = -MAX_WBITS, Z_GZCOMPRESS_WSIZE = MAX_WBITS + 16, }; +static int zwindow[] = { Z_COMPRESS1_WSIZE, Z_COMPRESS2_WSIZE, Z_GZCOMPRESS_WSIZE }; + // 初始化 static inline void stream_init(z_stream *z) { memset(z, 0x0, sizeof(z_stream)); @@ -38,34 +40,11 @@ static inline int stream_deflate(lua_State* L, int mode, const uint8_t* in, size size_t out_size; uint8_t *out; - switch (mode) { - case Z_COMPRESS1_MODE: - if (Z_OK != deflateInit2(&z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, Z_COMPRESS1_WSIZE, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY)) - return luaL_error(L, "[ZLIB ERROR]: Compress1 init failed."); - - // 输出 - out_size = compressBound(in_size); - out = lua_newuserdata(L, out_size); - break; - case Z_COMPRESS2_MODE: - if (Z_OK != deflateInit2(&z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, Z_COMPRESS2_WSIZE, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY)) - return luaL_error(L, "[ZLIB ERROR]: Compress2 init failed."); + if (Z_OK != deflateInit2(&z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, zwindow[mode], MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY)) + return luaL_error(L, "[ZLIB ERROR]: deflateInit init failed."); - // 输出 - out_size = deflateBound(&z, in_size); - out = lua_newuserdata(L, out_size); - break; - case Z_GZCOMPRESS_MODE: - if (Z_OK != deflateInit2(&z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, Z_GZCOMPRESS_WSIZE, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY)) - return luaL_error(L, "[ZLIB ERROR]: GzCompress init failed."); - - // 输出 - out_size = deflateBound(&z, in_size); - out = lua_newuserdata(L, out_size); - break; - default: - return luaL_error(L, "[ZLIB ERROR]: Invalid zlib mode."); - } + out_size = deflateBound(&z, in_size); + out = lua_newuserdata(L, out_size); // 输入 z.next_in = (uint8_t *)in; @@ -99,7 +78,7 @@ static inline int stream_inflate(lua_State* L, int windsize, const uint8_t* in, z.avail_in = in_size; if (Z_OK != inflateInit2(&z, windsize)) - return 0; + return luaL_error(L, "[ZLIB ERROR]: inflateInit init failed."); luaL_Buffer B; luaL_buffinit(L, &B); From 2098fc83211d78e4dbb33e652fd6efc26252fbe3 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 26 Jul 2021 21:18:29 +0800 Subject: [PATCH 807/956] =?UTF-8?q?=E5=AE=8C=E5=96=84websocket=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E5=93=8D=E5=BA=94=E8=A1=8C=E4=B8=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/websocket/server.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lualib/protocol/websocket/server.lua b/lualib/protocol/websocket/server.lua index bbf40e9d..d7513a99 100644 --- a/lualib/protocol/websocket/server.lua +++ b/lualib/protocol/websocket/server.lua @@ -1,5 +1,6 @@ local cf = require "cf" local cf_fork = cf.fork +local cf_sleep = cf.sleep local new_tab = require"sys".new_tab @@ -81,7 +82,7 @@ end -- 退出 function ws:exit() - self.closed = true + self:close() self:add_to_queue(function () end) end @@ -129,7 +130,7 @@ function Websocket.start(sock, obj, args, headers, ext) cf_fork(on_message, cls, data, typ == 'binary') end end - return + return cf_sleep(0) end return Websocket \ No newline at end of file From ff047fa57425e993228e0d04c7ba3a6b0ae9c310 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 26 Jul 2021 21:35:49 +0800 Subject: [PATCH 808/956] update lzlib.c --- luaclib/src/lz/lzlib.c | 57 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/luaclib/src/lz/lzlib.c b/luaclib/src/lz/lzlib.c index 80acae39..1750bf30 100644 --- a/luaclib/src/lz/lzlib.c +++ b/luaclib/src/lz/lzlib.c @@ -177,6 +177,60 @@ static int lgzip_uncompress(lua_State *L) { return stream_inflate(L, Z_GZCOMPRESS_WSIZE, (const uint8_t*)in, in_size); } +static int lws_compress(lua_State *L) { + size_t in_size = 0; + const uint8_t* in = (const uint8_t*)luaL_checklstring(L, 1, &in_size); + if (in_size <= 0) + return luaL_error(L, "[ZLIB ERROR]: Invalid in buffer."); + + z_stream z; + stream_init(&z); + + if (Z_OK != deflateInit2(&z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, Z_COMPRESS2_WSIZE, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY)) + return luaL_error(L, "[ZLIB ERROR]: deflateInit init failed."); + + size_t out_size = deflateBound(&z, in_size); + uint8_t *out = lua_newuserdata(L, out_size); + + // 输入 + z.next_in = (uint8_t *)in; + z.avail_in = in_size; + + z.next_out = out; + z.avail_out = out_size; + + int ret = deflate(&z, Z_FINISH); + // 压缩 + if (ret != Z_STREAM_END) { + deflateEnd(&z); + return luaL_error(L, "[ZLIB ERROR]: deflate error(%d).", ret); + } + // 清理 + if (deflateEnd(&z) != Z_OK){ + return luaL_error(L, "[ZLIB ERROR]: deflateEnd error(%d).", ret); + } + + out[0] = out[0] - 1; + + // 结束 + lua_pushlstring(L, (const char*)out, z.total_out); + lua_pushinteger(L, in_size); + return 2; +} + +static int lws_uncompress(lua_State *L) { + size_t in_size = 0; + const uint8_t* in = (const uint8_t*)luaL_checklstring(L, 1, &in_size); + if (in_size <= 0) + return luaL_error(L, "[ZLIB ERROR]: Invalid in buffer."); + + char *buf = (char *)in; + buf[0] = buf[0] + 1; + int ret = stream_inflate(L, Z_COMPRESS2_WSIZE, (const uint8_t*)in, in_size); + buf[0] = buf[0] - 1; + return ret; +} + LUAMOD_API int luaopen_lz(lua_State *L){ luaL_checkversion(L); luaL_Reg zlib_libs[] = { @@ -189,6 +243,9 @@ LUAMOD_API int luaopen_lz(lua_State *L){ /* gzip压缩/解压方法 */ {"gzcompress", lgzip_compress}, {"gzuncompress", lgzip_uncompress}, + /* Websocket压缩/解压方法 */ + {"wscompress", lws_compress}, + {"wsuncompress", lws_uncompress}, {NULL, NULL} }; luaL_newlib(L, zlib_libs); From 522bf1982ce77a65f9af6c9594505d40abca4f08 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 26 Jul 2021 21:37:24 +0800 Subject: [PATCH 809/956] =?UTF-8?q?=E4=BC=98=E5=8C=96websocket=E5=8E=8B?= =?UTF-8?q?=E7=BC=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/websocket/protocol.lua | 30 +++++++++----------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/lualib/protocol/websocket/protocol.lua b/lualib/protocol/websocket/protocol.lua index 36e315b1..594b0a27 100644 --- a/lualib/protocol/websocket/protocol.lua +++ b/lualib/protocol/websocket/protocol.lua @@ -2,14 +2,12 @@ local crypt = require "crypt" local xor_str = crypt.xor_str local lz = require "lz" -local compress2 = lz.compress2 -local uncompress2 = lz.uncompress2 +local wscompress = lz.wscompress +local wsuncompress = lz.wsuncompress local new_tab = require("sys").new_tab local error = error -local char = string.char -local byte = string.byte local strpack = string.pack local strunpack = string.unpack local random = math.random @@ -53,20 +51,12 @@ local function sock_send (sock, data) return sock:send(data) end --- 压缩 -local function deflate_compress(data) - local comp = compress2(data) - if not comp then - return nil - end - return concat{char(byte(comp) - 1), comp:sub(2)} - -- return gsub(comp, ".", char(byte(comp) - 1), 1) +local function wsdeflate(data) + return wscompress(data) end --- 解压 -local function deflate_uncompress(data) - -- return uncompress2(gsub(data, ".", char(byte(data) + 1), 1)) - return uncompress2(concat{char(byte(data) + 1), data:sub(2)}) +local function wsinflate(data) + return wsuncompress(data) end @@ -179,7 +169,7 @@ function protocol.recv_frame(sock, max_payload_len, force_masking, buffers) -- 支持 permessage-deflate 必须解压缩数据载荷 if rsv == 0x04 then - local buf = deflate_uncompress(data) + local buf = wsinflate(data) if not buf then return false, 'error', "[WS ERROR] : received Invalid deflate buffers." end @@ -220,7 +210,7 @@ function protocol.send_frame(sock, fin, opcode, payload, max_payload_len, maskin -- 如果有扩展协议则加上扩展响应头部 if (opc ~= 'close' and opc ~= 'ping' and opc ~= 'pong') and payload_len > 125 and ext == 'deflate' then h1 = h1 | 0x40 - payload = deflate_compress(payload) + payload = wsdeflate(payload) payload_len = #payload end @@ -237,7 +227,7 @@ function protocol.send_frame(sock, fin, opcode, payload, max_payload_len, maskin local buffers = new_tab(3, 0) - buffers[#buffers+1] = strpack(">BB", h1, h2) + insert(buffers, strpack(">BB", h1, h2)) if len_ext then buffers[#buffers+1] = len_ext @@ -247,7 +237,7 @@ function protocol.send_frame(sock, fin, opcode, payload, max_payload_len, maskin if masking and payload_len > 0 then masking = strpack(">BBBB", random(255), random(255), random(255), random(255)) payload = xor_str(payload, masking) - buffers[#buffers+1] = masking + insert(buffers, masking) end return sock_send(sock, concat(buffers)) and sock_send(sock, payload) From ba545a18c01ff8534a253515951680f25c90dc67 Mon Sep 17 00:00:00 2001 From: "Mr.che" <869646063@qq.com> Date: Tue, 27 Jul 2021 15:06:01 +0800 Subject: [PATCH 810/956] Update lzlib.c --- luaclib/src/lz/lzlib.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/luaclib/src/lz/lzlib.c b/luaclib/src/lz/lzlib.c index 1750bf30..d4ba6b20 100644 --- a/luaclib/src/lz/lzlib.c +++ b/luaclib/src/lz/lzlib.c @@ -199,21 +199,11 @@ static int lws_compress(lua_State *L) { z.next_out = out; z.avail_out = out_size; - int ret = deflate(&z, Z_FINISH); - // 压缩 - if (ret != Z_STREAM_END) { - deflateEnd(&z); - return luaL_error(L, "[ZLIB ERROR]: deflate error(%d).", ret); - } - // 清理 - if (deflateEnd(&z) != Z_OK){ - return luaL_error(L, "[ZLIB ERROR]: deflateEnd error(%d).", ret); - } - - out[0] = out[0] - 1; + deflate(&z, Z_SYNC_FLUSH); + deflateEnd(&z); // 结束 - lua_pushlstring(L, (const char*)out, z.total_out); + lua_pushlstring(L, (const char*)out, z.total_out - 4); lua_pushinteger(L, in_size); return 2; } From ad06c2f7d7ebd239f8fb582f31f2561729e98944 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 28 Jul 2021 01:08:49 +0800 Subject: [PATCH 811/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0TCP=E5=BA=93=E7=9A=84?= =?UTF-8?q?=E6=96=AD=E8=A8=80=E6=9C=BA=E5=88=B6=E6=9D=A5=E5=87=8F=E5=B0=91?= =?UTF-8?q?=E8=AF=AF=E7=94=A8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/TCP.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index 843063f6..1c935fc3 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -231,6 +231,7 @@ function TCP:send(buf) if not wlen or wlen == #buf then return wlen == #buf end + assert(not self.send_co, "[TCP ERROR]: Try to call the 'send' method multiple times.") -- 缓解发送大量数据集的时候调用频繁的问题 if not self.wsize or self.wsize < #buf then self.wsize = #buf @@ -272,6 +273,7 @@ function TCP:ssl_send(buf) if not wlen or wlen == #buf then return wlen == #buf end + assert(not self.send_co, "[TCP ERROR]: Try to call the 'send' method multiple times.") -- 缓解发送大量数据集的时候调用频繁的问题 if not self.wsize or self.wsize < #buf then self.wsize = #buf @@ -311,6 +313,7 @@ function TCP:readline(sp, nosp) if type(sp) ~= 'string' or #sp < 1 then return nil, "Invalid separator." end + assert(not self.read_co, "[TCP ERROR]: Try to call the 'recv' method multiple times.") local buffer local msize = 65535 while 1 do @@ -370,6 +373,7 @@ function TCP:ssl_readline(sp, nosp) if type(sp) ~= 'string' or #sp < 1 then return nil, "Invalid separator." end + assert(not self.read_co, "[TCP ERROR]: Try to call the 'recv' method multiple times.") local buffer local msize = 65535 -- 开始读取数据 @@ -431,6 +435,7 @@ function TCP:recv(bytes) if type(len) ~= 'number' or len > 0 then return data, len end + assert(not self.read_co, "[TCP ERROR]: Try to call the 'recv' method multiple times.") -- 优化大数据集的调用次数太多的问题 if not self.rsize or self.rsize < bytes then self.rsize = bytes @@ -480,6 +485,7 @@ function TCP:ssl_recv(bytes) if buf then return buf, len end + assert(not self.read_co, "[TCP ERROR]: Try to call the 'recv' method multiple times.") -- 优化大数据集的调用次数太多的问题 if not self.rsize or self.rsize < bytes then self.rsize = bytes From 7d4ebb13f54d4ffe8a1436d486fc6c15d0dfe374 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 28 Jul 2021 22:30:03 +0800 Subject: [PATCH 812/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8D=8F=E7=A8=8B?= =?UTF-8?q?=E7=9A=84(=E6=89=B9=E9=87=8F)=E6=89=A7=E8=A1=8C=E6=96=B9?= =?UTF-8?q?=E6=B3=95.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/cf/init.lua | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/lualib/cf/init.lua b/lualib/cf/init.lua index de56404e..aa39bd2f 100644 --- a/lualib/cf/init.lua +++ b/lualib/cf/init.lua @@ -9,6 +9,9 @@ local time_at = Timer.at local sleep = Timer.sleep local time_out = Timer.timeout +local type = type +local error = error +local ipairs = ipairs local tonumber = tonumber local cf = {} @@ -73,4 +76,32 @@ function cf.wakeup(co, ...) return wakeup(co, ...) end +local function cb(f, ctx) + local ok, errinfo = pcall(f) + ctx.waits = ctx.waits - 1 + if ctx.waits == 0 then + wakeup(ctx.co) + end + if not ok then + return error(errinfo) + end +end + +---comment 并发执行多协程, 指定的协程都结束后返回. +---@param fn function @至少一个或多个任务函数 +---@return nil @始终不存在返回值. +function cf.join(fn, ...) + local qlist = {fn, ...} + local ctx = { co = self(), waits = nil } + local waits = 0 + for _, f in ipairs(qlist) do + if type(f) == 'function' then + fork(cb, f, ctx) + waits = waits + 1 + end + end + ctx.waits = waits + return wait() +end + return cf From 36ecf36648063acee7fc94ec89fa3117fe84306a Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 28 Jul 2021 22:58:37 +0800 Subject: [PATCH 813/956] =?UTF-8?q?=E4=BC=98=E5=8C=96httpc=E7=9A=84?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/init.lua | 94 +++++++++++++-------------------------- lualib/httpc/protocol.lua | 14 ++++-- 2 files changed, 40 insertions(+), 68 deletions(-) diff --git a/lualib/httpc/init.lua b/lualib/httpc/init.lua index 9a3a9ef9..bb458ad6 100644 --- a/lualib/httpc/init.lua +++ b/lualib/httpc/init.lua @@ -1,12 +1,8 @@ -local system = require "system" -local now = system.now -local is_array_member = system.is_array_member +local sys = require "sys" +local now = sys.now local cf = require "cf" -local cf_self = cf.self -local cf_fork = cf.fork -local cf_wait = cf.wait -local cf_wakeup = cf.wakeup +local cf_join = cf.join local ua = require "httpc.ua" local protocol = require "httpc.protocol" @@ -27,11 +23,14 @@ local build_basic_authorization = protocol.build_basic_authorization local type = type local assert = assert +local lower = string.lower local upper = string.upper +local tunpack = table.unpack +local tinsert = table.insert local __TIMEOUT__ = 15 -local methods = {'get', 'post', 'json', 'file'} +local methods = { get = true, post = true, put = true, delete = true, xml = true, json = true, file = true} local function raw( parameter ) local opt, err = splite_protocol(parameter.domain) @@ -39,14 +38,8 @@ local function raw( parameter ) return nil, err end - local method = type(parameter.method) == 'string' and upper(parameter.method) or nil - assert( method and ( - method == 'GET' or - method == 'POST' or - method == 'OPTIONS' or - method == 'DELETE' or - method == 'PUT' - ),"invalide http method.") + local method = assert(type(parameter.method) == 'string' and methods[lower(parameter.method)] and upper(parameter.method), "[HTTPC ERROR]: invalide http method.") + parameter.method = method -- GET方法禁止传递body if parameter.method == "GET" then @@ -250,8 +243,6 @@ local function json(domain, headers, json, timeout) return nil, err end - assert(type(json) == "string" or type(json) == "table", "attempted passed a invalide json string or table.") - opt.json = json opt.headers = headers opt.server = ua.get_user_agent() @@ -289,8 +280,6 @@ local function xml(domain, headers, xml, timeout) return nil, err end - assert(type(xml) == "string" or type(xml) == "table", "attempted passed a invalide json string or table.") - opt.xml = xml opt.headers = headers opt.server = ua.get_user_agent() @@ -350,51 +339,28 @@ local function file(domain, headers, files, timeout) return code, msg, headers end -local function multi_request (opt) - if type(opt) ~= 'table' then - return nil, "1. 错误的参数类型" - end - local len = #opt - if len > 0 then - local co = cf_self() - local response = {} - local wakeuped = false - for index = 1, len do - cf_fork(function () - local t = now() - local req = opt[index] - -- 确认method - local method = req.method and req.method:lower() - if type(method) ~= 'string' or not is_array_member(methods, method) then - response[index] = {nil, '不被支持的请求方法.', now() - t} - if #response >= len and not wakeuped then - wakeuped = true - cf_wakeup(co, nil, response) - end - return - end - - local code, msg - if method == 'get' then - code, msg = get(req.domain, req.headers, req.args, req.timeout) - elseif method == 'post' then - code, msg = post(req.domain, req.headers, req.body, req.timeout) - elseif method == 'json' then - code, msg = json(req.domain, req.headers, req.json, req.timeout) - elseif method == 'file' then - code, msg = file(req.domain, req.headers, req.files, req.timeout) - end - response[index] = {code, msg, now() - t} - if #response >= len and not wakeuped then - wakeuped = true - cf_wakeup(co, true, response) - end - return - end) - end - return cf_wait() +local map = { get = get, post = post, delete = delete, json = json, xml = xml, file = file, put = put } + +local function multi_request (list) + if type(list) ~= 'table' or #list < 1 then + return false, "[HTTPC ERROR]: Invalid request parameter." end - return nil, "2. 错误的参数" + local s = now() + local response = {} + local array = {} + for index, req in ipairs(list) do + local fn = map[req.method and lower(req.method)] + if not fn then + response[index] = {false, '[HTTPC ERROR]: Unsupported request method.', {}, now() - s} + else + tinsert(array, function () + local code, msg, headers = fn(req.domain, req.headers, req.args or req.body or req.json or req.xml or req.files, req.timeout) + response[index] = {code, msg, headers, now() - s} + end) + end + end + cf_join(tunpack(array)) + return true, response end diff --git a/lualib/httpc/protocol.lua b/lualib/httpc/protocol.lua index ec2d0b30..145fbdc9 100644 --- a/lualib/httpc/protocol.lua +++ b/lualib/httpc/protocol.lua @@ -330,11 +330,14 @@ local function build_json_req (opt) if type(opt.json) == 'string' and opt.json ~= '' then insert(request, 'Content-Type: application/json') insert(request, fmt("Content-Length: %s", #opt.json)) - end - if type(opt.json) == 'table' then + elseif type(opt.json) == 'table' then opt.json = json_encode(opt.json) insert(request, 'Content-Type: application/json') insert(request, "Content-Length: " .. #opt.json) + else + opt.json = "" + insert(request, 'Content-Type: application/json') + insert(request, "Content-Length: 0") end return concat(request, CRLF) .. CRLF2 .. opt.json end @@ -357,11 +360,14 @@ local function build_xml_req(opt) if type(opt.xml) == 'string' and opt.xml ~= '' then insert(request, 'Content-Type: application/xml') insert(request, fmt("Content-Length: %s", #opt.xml)) - end - if type(opt.xml) == 'table' then + elseif type(opt.xml) == 'table' then opt.xml = toxml(opt.xml, "xml") insert(request, 'Content-Type: application/xml') insert(request, "Content-Length: " .. #opt.xml) + else + opt.xml = "" + insert(request, 'Content-Type: application/xml') + insert(request, "Content-Length: 0") end return concat(request, CRLF) .. CRLF2 .. opt.xml end From c83a162e82254e4834327685d5c051dcf68b05ba Mon Sep 17 00:00:00 2001 From: "Mr.che" <869646063@qq.com> Date: Mon, 2 Aug 2021 10:51:49 +0800 Subject: [PATCH 814/956] Update ltcp.c --- luaclib/src/ltcp.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index f4b7c2d5..fa67e636 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -9,6 +9,8 @@ #define alloca __alloca #endif +#define MBSIZE (262144) + #define None (-1) #define SERVER (0) #define CLIENT (1) @@ -513,7 +515,7 @@ static int tcp_peek(lua_State *L) { int bsize = lua_tointeger(L, 2); char* buffer = NULL; - if (bsize <= 262144) + if (bsize <= MBSIZE) buffer = alloca(bsize); else buffer = lua_newuserdata(L, bsize); @@ -551,7 +553,7 @@ static int tcp_sslpeek(lua_State *L) { int bsize = lua_tointeger(L, 2); char* buffer = NULL; - if (bsize <= 262144) + if (bsize <= MBSIZE) buffer = alloca(bsize); else buffer = lua_newuserdata(L, bsize); @@ -593,7 +595,7 @@ static int tcp_read(lua_State *L){ errno = 0; char* str = NULL; - if (bytes <= 262144) + if (bytes <= MBSIZE) str = alloca(bytes); else str = lua_newuserdata(L, bytes); @@ -630,7 +632,7 @@ static int tcp_sslread(lua_State *L){ errno = 0; char* str = NULL; - if (bytes <= 262144) + if (bytes <= MBSIZE) str = alloca(bytes); else str = lua_newuserdata(L, bytes); From 79202cea4d5f6631c0c1e8d01e730484afbb92d0 Mon Sep 17 00:00:00 2001 From: "Mr.che" <869646063@qq.com> Date: Mon, 2 Aug 2021 10:52:17 +0800 Subject: [PATCH 815/956] Update ludp.c --- luaclib/src/ludp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/luaclib/src/ludp.c b/luaclib/src/ludp.c index 0f0c355c..7a69247f 100644 --- a/luaclib/src/ludp.c +++ b/luaclib/src/ludp.c @@ -6,6 +6,8 @@ #define alloca __alloca #endif +#define MBSIZE (262144) + static inline void SETSOCKETOPT(int sockfd) { int Enable = 1; int ret = 0; @@ -105,8 +107,8 @@ static int udp_recv(lua_State *L){ int fd = lua_tointeger(L, 1); if (fd < 0) return 0; - int bsize = 65535; - char* str = alloca(65535); + int bsize = MBSIZE; + char* str = alloca(MBSIZE); int rsize = read(fd, str, bsize); if (rsize < 0) return 0; From 8d5609c170c89129be376d3dae4e68baaec96549 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 3 Aug 2021 00:15:40 +0800 Subject: [PATCH 816/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E7=9A=84=E5=86=85=E5=AD=98=E9=87=8A=E6=94=BE=E9=97=AE=E9=A2=98?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_start.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/core_start.c b/src/core_start.c index 53f34a48..b0c0eea6 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -72,14 +72,13 @@ static inline void cfadmin_specify_kill_process(const char *spid) { FILE *fp = NULL; if ((fp = fopen(spid, "rb")) == NULL) { LOG("ERROR", "Invalid Pid or pid file name."); - fclose(fp); - return; + return exit(0); } char pbuf[20]; memset(pbuf, 0x00, 20); if (fread(pbuf, 1, 20, fp) <= 0 || (pid = atoi(pbuf)) <= 1) { LOG("ERROR", "Invalid Pid or File name."); fclose(fp); - return; + return exit(0); } fclose(fp); remove(pid_filename); From ed1affb568d271d43d4f974aefa659ee8f48e92c Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 5 Aug 2021 23:51:23 +0800 Subject: [PATCH 817/956] =?UTF-8?q?=E6=8E=A7=E5=88=B6=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=E8=84=9A=E6=9C=AC=E4=BB=85=E7=94=9F=E6=88=90=E9=9D=99=E6=80=81?= =?UTF-8?q?=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sh b/build.sh index c499017b..5ad5b6a7 100755 --- a/build.sh +++ b/build.sh @@ -26,7 +26,7 @@ git clone https://github.com/CandyMi/libev -b v4.33 git clone https://github.com/CandyMi/libeio echo "========== build libev ==========" && - cd ${current}/build/libev && sh autogen.sh && ./configure --prefix=/usr/local && + cd ${current}/build/libev && sh autogen.sh && ./configure --prefix=/usr/local --enable-shared=no && ## 1. 将头文件与库文件放到cf框架目录下(Put the header files and library files in the cf framework directory) make && cp e*.h ${current}/src && cd .libs && cp $(printf "%s" "`ls | grep libev | grep -v la`") ${current}/ @@ -35,7 +35,7 @@ echo "========== build libev ==========" && # make && make install echo "========== build libeio ==========" && - cd ${current}/build/libeio && sh autogen.sh && ./configure --prefix=/usr/local && + cd ${current}/build/libeio && sh autogen.sh && ./configure --prefix=/usr/local --enable-shared=no && ## 1. 将头文件与库文件放到cf框架目录下(Put the header files and library files in the cf framework directory) make && cp e*.h ${current}/src && cd .libs && cp $(printf "%s" "`ls | grep libeio | grep -v la`") ${current}/ From 81c707ef83dfa70739224b2d57c073232fb18134 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Fri, 6 Aug 2021 00:44:13 +0800 Subject: [PATCH 818/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E6=96=B9=E5=BC=8F=E5=A2=9E=E5=8A=A0=E5=85=BC=E5=AE=B9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 4 ++-- luaclib/Makefile | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.sh b/build.sh index 5ad5b6a7..29211319 100755 --- a/build.sh +++ b/build.sh @@ -26,7 +26,7 @@ git clone https://github.com/CandyMi/libev -b v4.33 git clone https://github.com/CandyMi/libeio echo "========== build libev ==========" && - cd ${current}/build/libev && sh autogen.sh && ./configure --prefix=/usr/local --enable-shared=no && + cd ${current}/build/libev && sh autogen.sh && ./configure --prefix=/usr/local --enable-shared=no --with-pic && ## 1. 将头文件与库文件放到cf框架目录下(Put the header files and library files in the cf framework directory) make && cp e*.h ${current}/src && cd .libs && cp $(printf "%s" "`ls | grep libev | grep -v la`") ${current}/ @@ -35,7 +35,7 @@ echo "========== build libev ==========" && # make && make install echo "========== build libeio ==========" && - cd ${current}/build/libeio && sh autogen.sh && ./configure --prefix=/usr/local --enable-shared=no && + cd ${current}/build/libeio && sh autogen.sh && ./configure --prefix=/usr/local --enable-shared=no --with-pic && ## 1. 将头文件与库文件放到cf框架目录下(Put the header files and library files in the cf framework directory) make && cp e*.h ${current}/src && cd .libs && cp $(printf "%s" "`ls | grep libeio | grep -v la`") ${current}/ diff --git a/luaclib/Makefile b/luaclib/Makefile index 5c84b8be..3e5c51fb 100644 --- a/luaclib/Makefile +++ b/luaclib/Makefile @@ -23,7 +23,7 @@ internal : @echo "CC - ltimer" @$(CC) -o timer.so src/ltimer.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore @echo "CC - laio" - @$(CC) -o laio.so src/laio.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -leio + @$(CC) -o laio.so src/laio.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -leio -lpthread @echo "CC - ltcp" @$(CC) -o tcp.so src/ltcp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore -lssl -lcrypto From fc0f0c527dd0f9c13b6736d586ce997ff0b29d60 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 7 Aug 2021 03:26:43 +0800 Subject: [PATCH 819/956] =?UTF-8?q?Cache=E5=BA=93=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=8F=AF=E9=80=89=E7=9A=84SSDB=E7=BC=93=E5=AD=98=E6=94=AF?= =?UTF-8?q?=E6=8C=81.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/Cache/init.lua | 191 +---------------------------------------- lualib/Cache/redis.lua | 190 ++++++++++++++++++++++++++++++++++++++++ lualib/Cache/ssdb.lua | 189 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 380 insertions(+), 190 deletions(-) create mode 100644 lualib/Cache/redis.lua create mode 100644 lualib/Cache/ssdb.lua diff --git a/lualib/Cache/init.lua b/lualib/Cache/init.lua index fbf9590a..fb3b5a73 100644 --- a/lualib/Cache/init.lua +++ b/lualib/Cache/init.lua @@ -1,190 +1 @@ -local class = require "class" -local log = require "logging" -local Co = require "internal.Co" -local timer = require "internal.Timer" -local redis = require "protocol.redis" - -local co_self = Co.self -local co_wait = Co.wait -local co_wakeup = Co.wakeup - -local ipairs = ipairs -local assert = assert -local tostring = tostring -local setmetatable = setmetatable - -local table = table -local insert = table.insert -local remove = table.remove -local upper = string.upper -local lower = string.lower -local splite = string.gmatch - -local Log = log:new({ dump = true, path = 'Cache'}) - --- 注册命令 -local commands = { - 'sismember', 'exists', 'pipeline' -} - -local function in_command(cmd) - for _, command in ipairs(commands) do - if lower(cmd) == command then - return true - end - end - return false -end - -local keys = {} - --- 注入函数 -local function in_keys(key) - return keys[key] -end - --- 创建Cache函数 -local function CREATE_CACHE(opt) - local rds - while 1 do - rds = redis:new(opt):set_timeout(3) - local ok, err = rds:connect() - if ok then - if not opt.INITIALIZATION then - local opok, ret = assert(rds:cmd("CONFIG", "GET", "TIMEOUT")) - if opok and ret[2] ~= '0' then - assert(rds:cmd("CONFIG SET", "TIMEOUT", "0"), "SET TIMEOUT faild.") - end - end - break - end - Log:WARN("The connection failed. The reasons are: [" .. err .. "], Try to reconnect after 3 seconds") - rds:close() - timer.sleep(3) - end - return rds:set_timeout(0) -end - --- 加入到协程池内 -local function add_wait(self, co) - insert(self.co_pool, co) -end - --- 弹出一个等待协程 -local function pop_wait(self) - return remove(self.co_pool) -end - --- 加入到连接池内 -local function add_cache(self, cache) - insert(self.cache_pool, cache) -end - --- 从连接池内取出一个cache对象 -local function pop_cache(self) - if #self.cache_pool > 0 then - return remove(self.cache_pool) - end - if self.current < self.max then - self.current = self.current + 1 - return CREATE_CACHE(self) - end - add_wait(self, co_self()) - return co_wait() -end - --- 构建Cache对象 -local function setmeta(self) - keys['count'] = self.count - return setmetatable(self, { - __index = function(t, key) - local f = in_keys(key) - if f then - return f - end - if lower(key) == "publish" or lower(key) == "subscribe" or lower(key) == "psubscribe" then - return nil, 'Cache error: Cache不支持在缓存中直接使用此命令.' - end - if in_command(key) then - return function (_, ...) - local ok, ret - local session - while 1 do - session = pop_cache(t) - ok, ret = session[key](session, ...) - if session:isconnected() then - break - end - session:close() - session = nil - end - local co = pop_wait(t) - if co then - co_wakeup(co, session) - return ok, ret - end - add_cache(t, session) - return ok, ret - end - end - return function (_, ...) - local ok, ret - local keys = {} - for k in splite(key, "([^_]+)") do - keys[#keys+1] = k - end - local session - while 1 do - session = pop_cache(t) - if #keys > 1 then - ok, ret = session:cmd(upper(keys[1]), upper(keys[2]), ...) - else - ok, ret = session:cmd(upper(keys[1]), ...) - end - if session:isconnected() then - break - end - session:close() - session = nil - end - local co = pop_wait(t) - if co then - co_wakeup(co, session) - return ok, ret - end - add_cache(t, session) - return ok, ret - end - end}) == self -end - -local Cache = class("Cache") - -function Cache:ctor (opt) - self.host = opt.host - self.port = opt.port - self.unixdomain = opt.unixdomain - self.db = opt.db - self.auth = opt.auth - self.max = opt.max or 50 - self.current = 0 - -- 连接池 - self.cache_pool = {} - -- 协程池 - self.co_pool = {} -end - -function Cache:connect () - if not self.INITIALIZATION then - add_cache(self, pop_cache(self)) - self.INITIALIZATION = true - return setmeta(self) - end - return true -end - -function Cache:count() - return self.current, self.max -end - -return Cache +return require "Cache.redis" \ No newline at end of file diff --git a/lualib/Cache/redis.lua b/lualib/Cache/redis.lua new file mode 100644 index 00000000..2ec67630 --- /dev/null +++ b/lualib/Cache/redis.lua @@ -0,0 +1,190 @@ +local class = require "class" +local log = require "logging" +local Co = require "internal.Co" +local timer = require "internal.Timer" +local redis = require "protocol.redis" + +local co_self = Co.self +local co_wait = Co.wait +local co_wakeup = Co.wakeup + +local ipairs = ipairs +local assert = assert +local tostring = tostring +local setmetatable = setmetatable + +local table = table +local insert = table.insert +local remove = table.remove +local upper = string.upper +local lower = string.lower +local splite = string.gmatch + +local Log = log:new({ dump = true, path = 'Cache'}) + +-- 注册命令 +local commands = { + 'sismember', 'exists', 'pipeline' +} + +local function in_command(cmd) + for _, command in ipairs(commands) do + if lower(cmd) == command then + return true + end + end + return false +end + +local keys = {} + +-- 注入函数 +local function in_keys(key) + return keys[key] +end + +-- 创建Cache函数 +local function CREATE_CACHE(opt) + local rds + while 1 do + rds = redis:new(opt):set_timeout(3) + local ok, err = rds:connect() + if ok then + if not opt.INITIALIZATION then + local opok, ret = assert(rds:cmd("CONFIG", "GET", "TIMEOUT")) + if opok and ret[2] ~= '0' then + assert(rds:cmd("CONFIG SET", "TIMEOUT", "0"), "SET TIMEOUT faild.") + end + end + break + end + Log:WARN("[Cache ERROR]: The connection failed. The reasons are: [" .. err .. "], Try to reconnect after 3 seconds") + rds:close() + timer.sleep(3) + end + return rds:set_timeout(0) +end + +-- 加入到协程池内 +local function add_wait(self, co) + insert(self.co_pool, co) +end + +-- 弹出一个等待协程 +local function pop_wait(self) + return remove(self.co_pool) +end + +-- 加入到连接池内 +local function add_cache(self, cache) + insert(self.cache_pool, cache) +end + +-- 从连接池内取出一个cache对象 +local function pop_cache(self) + if #self.cache_pool > 0 then + return remove(self.cache_pool) + end + if self.current < self.max then + self.current = self.current + 1 + return CREATE_CACHE(self) + end + add_wait(self, co_self()) + return co_wait() +end + +-- 构建Cache对象 +local function setmeta(self) + keys['count'] = self.count + return setmetatable(self, { + __index = function(t, key) + local f = in_keys(key) + if f then + return f + end + if lower(key) == "publish" or lower(key) == "subscribe" or lower(key) == "psubscribe" then + return nil, '[Cache ERROR]: Cache不支持在缓存中直接使用此命令.' + end + if in_command(key) then + return function (_, ...) + local ok, ret + local session + while 1 do + session = pop_cache(t) + ok, ret = session[key](session, ...) + if session:isconnected() then + break + end + session:close() + session = nil + end + local co = pop_wait(t) + if co then + co_wakeup(co, session) + return ok, ret + end + add_cache(t, session) + return ok, ret + end + end + return function (_, ...) + local ok, ret + local keys = {} + for k in splite(key, "([^_]+)") do + keys[#keys+1] = k + end + local session + while 1 do + session = pop_cache(t) + if #keys > 1 then + ok, ret = session:cmd(upper(keys[1]), upper(keys[2]), ...) + else + ok, ret = session:cmd(upper(keys[1]), ...) + end + if session:isconnected() then + break + end + session:close() + session = nil + end + local co = pop_wait(t) + if co then + co_wakeup(co, session) + return ok, ret + end + add_cache(t, session) + return ok, ret + end + end}) == self +end + +local Cache = class("Cache") + +function Cache:ctor (opt) + self.host = opt.host + self.port = opt.port + self.unixdomain = opt.unixdomain + self.db = opt.db + self.auth = opt.auth + self.max = opt.max or 50 + self.current = 0 + -- 连接池 + self.cache_pool = {} + -- 协程池 + self.co_pool = {} +end + +function Cache:connect () + if not self.INITIALIZATION then + add_cache(self, pop_cache(self)) + self.INITIALIZATION = true + return setmeta(self) + end + return true +end + +function Cache:count() + return self.current, self.max +end + +return Cache \ No newline at end of file diff --git a/lualib/Cache/ssdb.lua b/lualib/Cache/ssdb.lua new file mode 100644 index 00000000..a8936793 --- /dev/null +++ b/lualib/Cache/ssdb.lua @@ -0,0 +1,189 @@ +local class = require "class" +local log = require "logging" +local Co = require "internal.Co" +local timer = require "internal.Timer" +local ssdb = require "protocol.redis" + +local co_self = Co.self +local co_wait = Co.wait +local co_wakeup = Co.wakeup + +local ipairs = ipairs +local setmetatable = setmetatable + +local table = table +local insert = table.insert +local remove = table.remove +local upper = string.upper +local lower = string.lower +local splite = string.gmatch + +local Log = log:new({ dump = true, path = 'Cache'}) + +-- 注册命令 +local commands = { 'exists', 'pipeline' } + +local function in_command(cmd) + for _, command in ipairs(commands) do + if lower(cmd) == command then + return true + end + end + return false +end + +local keys = {} + +-- 注入函数 +local function in_keys(key) + return keys[key] +end + +-- 创建Cache函数 +local function CREATE_CACHE(opt) + local rds + opt.db = nil + while 1 do + rds = ssdb:new(opt):set_timeout(3) + local ok, err = rds:connect() + if ok then + -- SSDB 不支持配置命令 + break + end + Log:WARN("[Cache ERROR]: The connection failed. The reasons are: [" .. err .. "], Try to reconnect after 3 seconds") + rds:close() + timer.sleep(3) + end + return rds:set_timeout(0) +end + +-- 加入到协程池内 +local function add_wait(self, co) + insert(self.co_pool, co) +end + +-- 弹出一个等待协程 +local function pop_wait(self) + return remove(self.co_pool) +end + +-- 加入到连接池内 +local function add_cache(self, cache) + insert(self.cache_pool, cache) +end + +-- 从连接池内取出一个cache对象 +local function pop_cache(self) + if #self.cache_pool > 0 then + return remove(self.cache_pool) + end + if self.current < self.max then + self.current = self.current + 1 + return CREATE_CACHE(self) + end + add_wait(self, co_self()) + return co_wait() +end + +-- 构建Cache对象 +local function setmeta(self) + keys['count'] = self.count + return setmetatable(self, { + __index = function(t, key) + local f = in_keys(key) + if f then + return f + end + if lower(key) == "publish" or lower(key) == "subscribe" or lower(key) == "psubscribe" then + return nil, '[Cache ERROR]: Cache不支持在缓存中直接使用此命令.' + end + if in_command(key) then + return function (_, ...) + local ok, ret + local session + while 1 do + session = pop_cache(t) + ok, ret = session[key](session, ...) + if session:isconnected() then + break + end + session:close() + session = nil + end + local co = pop_wait(t) + if co then + co_wakeup(co, session) + return ok, ret + end + add_cache(t, session) + return ok, ret + end + end + return function (_, ...) + local ok, ret + local keys = {} + for k in splite(key, "([^_]+)") do + keys[#keys+1] = k + end + if + key == 'multi_set' or key == 'multi_get' or key == 'multi_del' or + key == 'multi_hget' or key == 'multi_hset' or key == 'multi_hdel' or + key == 'multi_zget' or key == 'multi_zset' or key == 'multi_zdel' + then + keys = {key} + end + local session + while 1 do + session = pop_cache(t) + if #keys > 1 then + ok, ret = session:cmd(upper(keys[1]), upper(keys[2]), ...) + else + ok, ret = session:cmd(upper(keys[1]), ...) + end + if session:isconnected() then + break + end + session:close() + session = nil + end + local co = pop_wait(t) + if co then + co_wakeup(co, session) + return ok, ret + end + add_cache(t, session) + return ok, ret + end + end}) == self +end + +local Cache = class("Cache") + +function Cache:ctor (opt) + self.host = opt.host + self.port = opt.port + self.unixdomain = opt.unixdomain + self.db = opt.db + self.auth = opt.auth + self.max = opt.max or 50 + self.current = 0 + -- 连接池 + self.cache_pool = {} + -- 协程池 + self.co_pool = {} +end + +function Cache:connect () + if not self.INITIALIZATION then + add_cache(self, pop_cache(self)) + self.INITIALIZATION = true + return setmeta(self) + end + return true +end + +function Cache:count() + return self.current, self.max +end + +return Cache \ No newline at end of file From 232a12f76a377a3bb5549e79c2255a7814a69da0 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 7 Aug 2021 12:11:23 +0800 Subject: [PATCH 820/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0SSDB=E5=91=BD?= =?UTF-8?q?=E4=BB=A4=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/Cache/ssdb.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lualib/Cache/ssdb.lua b/lualib/Cache/ssdb.lua index a8936793..24ffcbbf 100644 --- a/lualib/Cache/ssdb.lua +++ b/lualib/Cache/ssdb.lua @@ -128,7 +128,8 @@ local function setmeta(self) if key == 'multi_set' or key == 'multi_get' or key == 'multi_del' or key == 'multi_hget' or key == 'multi_hset' or key == 'multi_hdel' or - key == 'multi_zget' or key == 'multi_zset' or key == 'multi_zdel' + key == 'multi_zget' or key == 'multi_zset' or key == 'multi_zdel' or + key == 'zpop_front' or key == 'zpop_back' or key == 'zpush_front' or key == 'zpush_back' then keys = {key} end From 65627e027aa06093ea9e275767ed8ce0c20ed6eb Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 7 Aug 2021 12:13:13 +0800 Subject: [PATCH 821/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0SSDB=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_SSDB.lua | 153 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 script/test_SSDB.lua diff --git a/script/test_SSDB.lua b/script/test_SSDB.lua new file mode 100644 index 00000000..c8f26b73 --- /dev/null +++ b/script/test_SSDB.lua @@ -0,0 +1,153 @@ +-- 测试redis +local Cache = require "Cache.ssdb" +local cf = require "cf" +local Log = require("logging"):new() + +local opt = { + host = "127.0.0.1", + -- port = 6379, + port = 8888, + -- auth = "rTVB9Fm2l2rctOJlIzVxIN0BreQQoiET", -- 如果需要验证 + -- db = 1, -- SSDB不支持多DB + max = 1, +} + +cf.fork(function ( ... ) + local Cache = Cache:new(opt) + local ok, err = Cache:connect() + if not ok then + return print(err) + end + -- 测试 GET/SET/DEL 命令示例 + local ok, ret = Cache:set("test", 1) + Log:DEBUG(ok, ret) + + local ok, ret = Cache:get("test") + Log:DEBUG(ok, ret) + + local ok, ret = Cache:del("test") + Log:DEBUG(ok, ret) + + -- 测试哈希表命令示例 + local ok, ret = Cache:hmset("website", "google", "www.google.com", "baidu", "www.baidu.com") + Log:DEBUG(ok, ret) + + local ok, ret = Cache:hlen("website") + Log:DEBUG(ok, ret) + + local ok, ret = Cache:hkeys("website") + Log:DEBUG(ok, ret) + + local ok, ret = Cache:hgetall("website") + Log:DEBUG(ok, ret) + + local ok, ret = Cache:hmget("website", "google", "baidu") + Log:DEBUG(ok, ret) + + local ok, ret = Cache:hdel("website", "google", "baidu") + Log:DEBUG(ok, ret) + + -- 测试列表命令示例 + local ok, ret = Cache:lpush("language", "lua", "python", "C", "C++") + Log:DEBUG(ok, ret) + + local ok, ret = Cache:rpush("language", "golang", "java", "ruby", "javascript") + Log:DEBUG(ok, ret) + + local ok, ret = Cache:lrange("language", 0, -1) + Log:DEBUG(ok, ret) + + -- SSDB不支持Ltrim + -- local ok, ret = Cache:ltrim("language" , -1, 0) + -- Log:DEBUG(ok, ret) + + local ok, ret = Cache:llen("language") + Log:DEBUG(ok, ret) + + -- 测试集合命令示例(SSDB 不支持集合) + -- local ok, ret = Cache:smembers("bbs") + -- Log:DEBUG(ok, ret) + + -- local ok, ret = Cache:sadd("bbs", "discuz.cn", "group.google.com", "oschina.net", "csdn.net") + -- Log:DEBUG(ok, ret) + + -- local ok, ret = Cache:sismember("bbs", "oschina.net") + -- Log:DEBUG(ok, ret) + + -- local ok, ret = Cache:srem("bbs", "discuz.cn", "group.google.com", "oschina.net", "csdn.net") + -- Log:DEBUG(ok, ret) + + -- local ok, ret = Cache:sismember("bbs", "oschina.net") + -- Log:DEBUG(ok, ret) + + -- local ok, ret = Cache:sadd("book1", "宝宝的C++", "宝宝的HTML", "宝宝的CSS", "C程序设计") + -- local ok, ret = Cache:sadd("book2", "宝宝的C++", "宝宝的HTML", "宝宝的CSS", "C++从入门到放弃") + -- local ok, ret = Cache:sadd("book3", "宝宝的C++", "宝宝的HTML", "宝宝的CSS", "MySQL从入门到删库跑路") + + -- local ok, ret = Cache:sdiff("book1", "book2", "book3") + -- Log:DEBUG(ok, ret) + + local ok, ret = Cache:del("book1", "book2", "book3") + Log:DEBUG(ok, ret) + + -- 测试有序集合命令示例 + local ok, ret = Cache:zadd("scores", 10, "admin", 20, "Candy", 30, "QQ", 40, "Guest") + Log:DEBUG(ok, ret) + + local ok, ret = Cache:zrange("scores", 0, -1) + -- local ok, ret = Cache:zrange("scores", 0, -1, "WITHSCORES") + Log:DEBUG(ok, ret) + + local ok, ret = Cache:zcount("scores", 10, 100) + Log:DEBUG(ok, ret) + + local ok, ret = Cache:zscore("scores", "QQ") + Log:DEBUG(ok, ret) + + local ok, ret = Cache:zrank("scores", "Candy") + Log:DEBUG(ok, ret) + + local ok, ret = Cache:zrem("scores", "admin", "Candy", "QQ", "Guest") + Log:DEBUG(ok, ret) + + local ok, ret = Cache:del("scores") + Log:DEBUG(ok, ret) + + -- 测试脚本支持 (SSDB 不支持脚本操作) + -- local ok, ret = Cache:script_load("return 10086") + -- Log:DEBUG(ok, ret) + + -- local sha = ret + -- local ok, ret = Cache:script_exists(sha) + -- Log:DEBUG(ok, ret) + + -- local ok, ret = Cache:evalsha(sha, 0) + -- Log:DEBUG(ok, ret) + + -- local ok, ret = Cache:script_flush() + -- Log:DEBUG(ok, ret) + + -- 其它一些特殊方法支持 + -- type, move, rename, keys, randomkey等等 + Log:DEBUG(Cache:count()) + + Log:DEBUG(Cache:multi_set("a1", "1001", "a2", "1002")) + + Log:DEBUG(Cache:multi_get("a1", "a2")) + + Log:DEBUG(Cache:multi_del("a1", "a2")) + + Log:DEBUG(Cache:multi_hset("jmap", "a1", "1001", "a2", "1002")) + + Log:DEBUG(Cache:multi_hget("jmap", "a1", "a2")) + + Log:DEBUG(Cache:multi_hdel("jmap", "a1", "a2")) + + -- 管道命令支持 + local ok, ret = Cache:pipeline { + {"HMSET", "USER_INFO", "name", "Candy", "email", '869646063@qq.com', 'phone', '13000000000'}, + {"HGET", "USER_INFO", "email"}, + {"HGET", "USER_INFO", "phone"}, + } + Log:DEBUG(ok, ret) +end) From 035064e7fd1578d4a22d5d98f8ed1261394d4101 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 7 Aug 2021 19:00:13 +0800 Subject: [PATCH 822/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=8D=8F=E8=AE=AE?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/redis.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lualib/protocol/redis.lua b/lualib/protocol/redis.lua index cad89645..2ed29872 100644 --- a/lualib/protocol/redis.lua +++ b/lualib/protocol/redis.lua @@ -2,6 +2,8 @@ local log = require "logging" local class = require "class" local Co = require "internal.Co" local tcp = require "internal.TCP" +local tcp_recv = tcp.recv +local tcp_readline = tcp.readline local new_tab = require "sys".new_tab @@ -29,7 +31,7 @@ local CRLF = '\x0d\x0a' local redcmd = {} local function read_response(sock) - local result = sock:readline("\r\n") + local result = tcp_readline(sock, CRLF) if not result then sock.state = false return nil, 'server close!!' @@ -39,7 +41,7 @@ local function read_response(sock) end local function sock_readbytes(sock, bytes) - local buffer = sock:recv(bytes) + local buffer = tcp_recv(sock, bytes) if not buffer then return end @@ -48,9 +50,8 @@ local function sock_readbytes(sock, bytes) end bytes = bytes - #buffer local buffers = {buffer} - local sock_read = sock.recv while 1 do - buffer = sock_read(sock, bytes) + buffer = tcp_recv(sock, bytes) if not buffer then return end From 18b94dabe41c024d692f633f048a01b060ca33b4 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 11 Aug 2021 08:04:33 +0800 Subject: [PATCH 823/956] =?UTF-8?q?=E5=AF=B9http=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E7=9A=84=E5=A4=B4=E9=83=A8=E7=B2=BE=E7=AE=80?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index 646141b3..fad8f52b 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -234,19 +234,15 @@ local function ERROR_RESPONSE(http, code, path, ip, forword, method, speed) local response = { REQUEST_STATUCODE_RESPONSE(code), HTTP_DATE(), - 'Accept-Ranges: none', - 'Origin: *', - 'Allow: GET, POST, PUT, HEAD, OPTIONS', 'Connection: keep-alive', 'Server: ' .. (http.__server or SERVER), } local error_page = PAGES[code] if error_page and http.__enable_error_pages then - response[#response+1] = 'Content-length: ' .. #error_page - response[#response+1] = 'Content-length: ' .. #error_page + insert(response, 'Content-Length: ' .. #error_page) return concat({concat(response, CRLF), error_page}, CRLF2) else - response[#response+1] = 'Content-length: 0' + insert(response, 'Content-Length: 0') return concat({concat(response, CRLF), CRLF2}) end end From d194b7599c2c575f15e02b34359951ae0fb25afe Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 11 Aug 2021 08:12:06 +0800 Subject: [PATCH 824/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=B7=A8=E5=9F=9F?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index fad8f52b..a86cf19d 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -154,6 +154,14 @@ local function readall(sock, bsize, buffers) return true end +local function cros_append(header, timeout) + insert(header, 'Access-Control-Allow-Origin: *') + insert(header, 'Access-Control-Allow-Headers: *') + insert(header, 'Access-Control-Allow-Methods: GET, POST, PUT, HEAD, OPTIONS') + insert(header, 'Access-Control-Allow-Credentials: true') + insert(header, 'Access-Control-Max-Age: ' .. (timeout or 86400)) +end + local function PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEADER) local content = new_tab(0, 16) if METHOD == "GET" then @@ -167,7 +175,7 @@ local function PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEA if body_len >= (max_body_size or (1024 * 1024)) then return nil, 413 end - local buffers = new_tab(32, 0) + local buffers = new_tab(16, 0) local bsize = body_len local _, CRLF_END = find(buffer, RE_CRLF2) if #buffer > CRLF_END then @@ -389,11 +397,7 @@ function HTTP_PROTOCOL.DISPATCH(sock, opt, http) res[#res+1] = 'Origin: *' res[#res+1] = 'Allow: GET, POST, PUT, HEAD, OPTIONS' if enable_cros_timeout then - res[#res+1] = 'Access-Control-Allow-Origin: *' - res[#res+1] = 'Access-Control-Allow-Headers: *' - res[#res+1] = 'Access-Control-Allow-Methods: GET, POST, PUT, HEAD, OPTIONS' - res[#res+1] = 'Access-Control-Allow-Credentials: true' - res[#res+1] = 'Access-Control-Max-Age: ' .. enable_cros_timeout + cros_append(res, enable_cros_timeout) end res[#res+1] = 'Server: ' .. server res[#res+1] = 'Connection: keep-alive' @@ -609,11 +613,7 @@ function HTTP_PROTOCOL.DISPATCH(sock, opt, http) end -- 如果启用了跨域, 则开启头部支持. if enable_cros_timeout then - header[#header+1] = 'Access-Control-Allow-Origin: *' - header[#header+1] = 'Access-Control-Allow-Headers: *' - header[#header+1] = 'Access-Control-Allow-Methods: GET, POST, PUT, HEAD, OPTIONS' - header[#header+1] = 'Access-Control-Allow-Credentials: true' - header[#header+1] = 'Access-Control-Max-Age: ' .. enable_cros_timeout + cros_append(header, enable_cros_timeout) end -- 不计算数据传输时间, 仅计算实际回调处理所用时间. tolog(http, statucode, PATH, HEADER['X-Real-IP'] or ipaddr, X_Forwarded_FORMAT(HEADER['X-Forwarded-For'] or ipaddr), METHOD, req_time(start)) @@ -635,7 +635,6 @@ function HTTP_PROTOCOL.DISPATCH(sock, opt, http) end end - function HTTP_PROTOCOL.RAW_DISPATCH(s, opt, http) if type(s) == 'table' then return HTTP_PROTOCOL.DISPATCH(s, opt, http) From 613fead8b8fb43a27929aea7008122b211407ef9 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 11 Aug 2021 23:52:13 +0800 Subject: [PATCH 825/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=85=BC=E5=AE=B9=E6=80=A7.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/Makefile | 2 +- luaclib/src/lcrypt/sm.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/luaclib/src/lcrypt/Makefile b/luaclib/src/lcrypt/Makefile index 0874c90f..fe04f6bf 100644 --- a/luaclib/src/lcrypt/Makefile +++ b/luaclib/src/lcrypt/Makefile @@ -9,7 +9,7 @@ default : CC = cc -CFLAGS = -O3 -Wall -shared -fPIC -fno-strict-aliasing +CFLAGS = -O3 -Wall -shared -fPIC -fno-strict-aliasing -Wno-deprecated-declarations DLL = -lcore -lcrypto INCLUDES = -I../../../src -I/usr/local/include diff --git a/luaclib/src/lcrypt/sm.c b/luaclib/src/lcrypt/sm.c index b1522820..84859fde 100644 --- a/luaclib/src/lcrypt/sm.c +++ b/luaclib/src/lcrypt/sm.c @@ -320,7 +320,7 @@ int lsm2sign(lua_State *L){ const char* text = luaL_checklstring(L, 2, &tsize); EVP_PKEY *sm2key = load_sm2prikey(L); - EVP_PKEY_set_alias_type(sm2key, EVP_PKEY_SM2); + // EVP_PKEY_set_alias_type(sm2key, EVP_PKEY_SM2); size_t osize = EVP_PKEY_size(sm2key); const char *out = lua_newuserdata(L, osize); @@ -348,7 +348,7 @@ int lsm2verify(lua_State *L){ const char* cipher = luaL_checklstring(L, 3, &csize); EVP_PKEY *sm2key = load_sm2pubkey(L); - EVP_PKEY_set_alias_type(sm2key, EVP_PKEY_SM2); + // EVP_PKEY_set_alias_type(sm2key, EVP_PKEY_SM2); EVP_MD_CTX *md_ctx = EVP_MD_CTX_new(); EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(sm2key, NULL); From 74e54b6471cbe70d7137b8ccfc3014e07a4ba3fc Mon Sep 17 00:00:00 2001 From: "Mr.che" <869646063@qq.com> Date: Thu, 12 Aug 2021 15:09:16 +0800 Subject: [PATCH 826/956] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 14bfd516..68e0216b 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@

                  - English | [简体中文](./README_zh-cn.md) + English | [简体中文](README_zh-cn.md)

                  - +

                  Efficient asynchronous comes from design, excellent performance practice truth.

                  From 5fadbcf03c88f41d3ad44cafec9597102b9259d4 Mon Sep 17 00:00:00 2001 From: "Mr.che" <869646063@qq.com> Date: Thu, 12 Aug 2021 15:09:44 +0800 Subject: [PATCH 827/956] Update README_zh-cn.md --- README_zh-cn.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README_zh-cn.md b/README_zh-cn.md index 7bfc24dd..c730cedb 100644 --- a/README_zh-cn.md +++ b/README_zh-cn.md @@ -1,13 +1,13 @@

                  - [English](./README.md) | 简体中文 + [English](README.md) | 简体中文

                  - +

                  高效的异步源于设计, 卓越的性能实践真理.

                  From 52a1bbab5170a7a2e8ee394717d800a01ec95dbd Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 16 Aug 2021 22:27:54 +0800 Subject: [PATCH 828/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9keep-alive=E6=9D=A1?= =?UTF-8?q?=E4=BB=B6=E5=88=A4=E6=96=AD.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index a86cf19d..a394e02a 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -394,8 +394,6 @@ function HTTP_PROTOCOL.DISPATCH(sock, opt, http) local res = new_tab(16, 0) res[#res+1] = REQUEST_STATUCODE_RESPONSE(200) res[#res+1] = HTTP_DATE() - res[#res+1] = 'Origin: *' - res[#res+1] = 'Allow: GET, POST, PUT, HEAD, OPTIONS' if enable_cros_timeout then cros_append(res, enable_cros_timeout) end @@ -557,11 +555,10 @@ function HTTP_PROTOCOL.DISPATCH(sock, opt, http) end header[#header+1] = HTTP_DATE() header[#header+1] = 'Accept-Ranges: none' - header[#header+1] = 'Origin: *' - header[#header+1] = 'Allow: GET, POST, PUT, HEAD, OPTIONS' header[#header+1] = 'Server: ' .. server local Connection = 'Connection: keep-alive' - if not HEADER['Connection'] or lower(HEADER['Connection']) == 'close' then + local keepalive = HEADER['Connection'] or HEADER['connection'] + if not keepalive or lower(keepalive) == 'close' then Connection = 'Connection: close' end header[#header+1] = Connection @@ -642,4 +639,4 @@ function HTTP_PROTOCOL.RAW_DISPATCH(s, opt, http) return HTTP_PROTOCOL.DISPATCH(tcp:new():set_fd(s):timeout(http.__timeout), opt, http) end -return HTTP_PROTOCOL +return HTTP_PROTOCOL \ No newline at end of file From aea0f2b718dba65d3e7d638c3d26e67f279e3c17 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 18 Aug 2021 20:35:17 +0800 Subject: [PATCH 829/956] =?UTF-8?q?=E4=BC=98=E5=8C=96readline=E7=9A=84?= =?UTF-8?q?=E8=AF=BB=E5=8F=96=E6=95=88=E7=8E=87.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/TCP.lua | 64 ++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index 1c935fc3..f40a8d62 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -1,14 +1,15 @@ local class = require "class" -local new_tab = require("sys").new_tab - -local log = require "logging" -local Log = log:new({ dump = true, path = 'internal-TCP' }) +local sys = require "sys" +local new_tab = sys.new_tab local type = type local assert = assert local io_open = io.open +local ssub = string.sub local spack = string.pack +local sfind = string.find +local concat = table.concat local insert = table.insert local remove = table.remove @@ -76,6 +77,12 @@ local function tcp_push(tcp) return insert(POOL, tcp) end +local tab = new_tab(3, 0) +local function buffer_concat(buf_1, buf_2) + tab[1], tab[2] = buf_1, buf_2 + return concat(tab) +end + local TCP = class("TCP") function TCP:ctor(...) @@ -313,12 +320,13 @@ function TCP:readline(sp, nosp) if type(sp) ~= 'string' or #sp < 1 then return nil, "Invalid separator." end - assert(not self.read_co, "[TCP ERROR]: Try to call the 'recv' method multiple times.") - local buffer - local msize = 65535 + assert(not self.read_co, "[TCP ERROR]: Try to call the 'readline' method multiple times.") + local _timeout = self._timeout + local fd, buffer = self.fd, nil + local asize, msize = 0, 1024 while 1 do ::CONTONIE:: - local buf, bsize = tcp_peek(self.fd, msize, true) + local buf, bsize = tcp_peek(fd, msize, true) if not buf then if bsize ~= 0 then return false, bsize @@ -336,7 +344,7 @@ function TCP:readline(sp, nosp) self.read_co = nil return co_wakeup(co, true) end) - self.timer = ti_timeout(self._timeout, function ( ) + self.timer = ti_timeout(_timeout, function ( ) tcp_push(self.READ_IO) tcp_stop(self.READ_IO) self.timer = nil @@ -345,23 +353,24 @@ function TCP:readline(sp, nosp) self.read_current_co = nil return co_wakeup(co, nil, "read timeout") end) - tcp_start(self.READ_IO, self.fd, EVENT_READ, self.read_co) + tcp_start(self.READ_IO, fd, EVENT_READ, self.read_co) local ok, errinfo = co_wait() if not ok then return false, errinfo end goto CONTONIE end - buffer = buffer and (buffer .. buf) or buf - local s, e = buffer:find(sp) + asize = asize + bsize + buffer = buffer and buffer_concat(buffer, buf) or buf + local s, e = sfind(buffer, sp) if s and e then - tcp_peek(self.fd, #buf - (#buffer - e), false) + tcp_peek(fd, bsize - (asize - e), false) if nosp then e = s - 1 end - return buffer:sub(1, e), e + return ssub(buffer, 1, e), e end - tcp_peek(self.fd, bsize, false) + tcp_peek(fd, bsize, false) end end @@ -373,13 +382,14 @@ function TCP:ssl_readline(sp, nosp) if type(sp) ~= 'string' or #sp < 1 then return nil, "Invalid separator." end - assert(not self.read_co, "[TCP ERROR]: Try to call the 'recv' method multiple times.") - local buffer - local msize = 65535 + assert(not self.read_co, "[TCP ERROR]: Try to call the 'ssl_readline' method multiple times.") + local _timeout = self._timeout + local ssl, buffer = self.ssl, nil + local asize, msize = 0, 1024 -- 开始读取数据 while 1 do ::CONTONIE:: - local buf, bsize = tcp_sslpeek(self.ssl, msize, true) + local buf, bsize = tcp_sslpeek(ssl, msize, true) if not buf then if bsize ~= 0 then return false, bsize @@ -397,7 +407,7 @@ function TCP:ssl_readline(sp, nosp) self.read_co = nil return co_wakeup(co, true) end) - self.timer = ti_timeout(self._timeout, function ( ) + self.timer = ti_timeout(_timeout, function ( ) tcp_push(self.READ_IO) tcp_stop(self.READ_IO) self.timer = nil @@ -413,16 +423,17 @@ function TCP:ssl_readline(sp, nosp) end goto CONTONIE end - buffer = buffer and (buffer .. buf) or buf - local s, e = buffer:find(sp) + asize = asize + bsize + buffer = buffer and buffer_concat(buffer, buf) or buf + local s, e = sfind(buffer, sp) if s and e then - tcp_sslpeek(self.ssl, #buf - (#buffer - e), false) + tcp_sslpeek(ssl, bsize - (asize - e), false) if nosp then e = s - 1 end - return buffer:sub(1, e), e + return ssub(buffer, 1, e), e end - tcp_sslpeek(self.ssl, bsize, false) + tcp_sslpeek(ssl, bsize, false) end end @@ -478,7 +489,6 @@ end function TCP:ssl_recv(bytes) local ssl = self.ssl if not ssl then - Log:ERROR("Please use recv method :)") return nil, "Please use recv method :)" end local buf, len = tcp_sslread(ssl, bytes) @@ -500,7 +510,7 @@ function TCP:ssl_recv(bytes) self.READ_IO = tcp_pop() self.read_current_co = co_self() self.read_co = co_new(function ( ) - while true do + while 1 do local buffer, bsize = tcp_sslread(ssl, bytes) if (buffer and bsize) or (not buffer and not bsize) then if self.timer then From f4de7383cd8d06347505f845d564619e07a5ed48 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 19 Aug 2021 22:03:42 +0800 Subject: [PATCH 830/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E5=8D=8F=E8=AE=AE=E8=BF=9E=E6=8E=A5=E5=88=A4=E6=96=AD?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/mssql.lua | 15 ++++++++++++--- lualib/protocol/mysql.lua | 19 ++++++++++++------- lualib/protocol/pgsql.lua | 15 ++++++++++++--- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/lualib/protocol/mssql.lua b/lualib/protocol/mssql.lua index ac46007b..2350a522 100644 --- a/lualib/protocol/mssql.lua +++ b/lualib/protocol/mssql.lua @@ -17,6 +17,7 @@ local null = null local type = type local pcall = pcall local error = error +local tostring = tostring local strpack = string.pack local strunpack = string.unpack @@ -918,8 +919,16 @@ function mssql:connect( ... ) return nil, "Connection failed: please recreate the socket object." end - if not self.sock:connect_ex(self.unixdomain or "") and not self.sock:connect(self.host, self.port) then - return nil, "MSSQL Server Connect failed." + if self.unixdomain then + if not self.sock:connect_ex(self.unixdomain) then + return nil, "MSSQL Server [" .. tostring(self.unixdomain) .. "] Connect failed." + end + elseif self.host and self.port then + if not self.sock:connect(self.host, self.port) then + return nil, "MSSQL Server TCP Connect failed." + end + else + return nil, "MSSQL Server driver Invalid Configure." end -- 发送TDS-7.0登录协议 @@ -1048,4 +1057,4 @@ function mssql:close () end end -return mssql +return mssql \ No newline at end of file diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua index 9d1a634b..ae5255fa 100644 --- a/lualib/protocol/mysql.lua +++ b/lualib/protocol/mysql.lua @@ -12,17 +12,13 @@ local randomkey = crypt.randomkey_ex local rsa_oaep_pkey_encode = crypt.rsa_public_key_oaep_padding_encode local sub = string.sub -local find = string.find local strgsub = string.gsub local strformat = string.format local strbyte = string.byte -local strchar = string.char local strrep = string.rep local strunpack = string.unpack local strpack = string.pack -local setmetatable = setmetatable local assert = assert -local select = select local tonumber = tonumber local toint = math.tointeger local insert = table.insert @@ -31,6 +27,7 @@ local concat = table.concat local null = null local type = type local ipairs = ipairs +local tostring = tostring local io_open = io.open local io_remove = os.remove @@ -462,8 +459,16 @@ end local function mysql_login (self) - if not self.sock:connect_ex(self.unixdomain or "") and not self.sock:connect(self.host, self.port) then - return nil, "MySQL Server Connect failed." + if self.unixdomain then + if not self.sock:connect_ex(self.unixdomain or "") then + return nil, "MySQL Server [" .. tostring(self.unixdomain) .. "] Connect failed." + end + elseif self.host and self.port then + if not self.sock:connect(self.host, self.port) then + return nil, "MySQL Server TCP Connect failed." + end + else + return nil, "MySQL Server driver Invalid Configure." end local len, err = read_head(self) @@ -663,4 +668,4 @@ function mysql:close() return sock:close() end -return mysql +return mysql \ No newline at end of file diff --git a/lualib/protocol/pgsql.lua b/lualib/protocol/pgsql.lua index 2a0b5428..1afe5c3b 100644 --- a/lualib/protocol/pgsql.lua +++ b/lualib/protocol/pgsql.lua @@ -13,6 +13,7 @@ local new_tab = sys.new_tab local null = null local tonumber = tonumber +local tostring = tostring local fmt = string.format local toint = math.tointeger @@ -486,8 +487,16 @@ end function pgsql:connect() - if not self.sock:connect_ex(self.unixdomain or "") and not self.sock:connect(self.host, self.port) then - return nil, "MySQL Server Connect failed." + if self.unixdomain then + if not self.sock:connect_ex(self.unixdomain or "") then + return nil, "PGSQL Server [" .. tostring(self.unixdomain) .. "] Connect failed." + end + elseif self.host and self.port then + if not self.sock:connect(self.host, self.port) then + return nil, "PGSQL Server TCP Connect failed." + end + else + return nil, "PGSQL Server driver Invalid Configure." end -- 发送启动协议 @@ -581,4 +590,4 @@ function pgsql:close() end end -return pgsql +return pgsql \ No newline at end of file From 0126090fc6bcf7890917311223bea4fdfcc3bf48 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 21 Aug 2021 20:32:19 +0800 Subject: [PATCH 831/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0httpc=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E5=88=A4=E6=96=AD.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/class.lua | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lualib/httpc/class.lua b/lualib/httpc/class.lua index 3922493f..efb2a9cc 100644 --- a/lualib/httpc/class.lua +++ b/lualib/httpc/class.lua @@ -72,23 +72,27 @@ function httpc:check_domain(opt) end function httpc:send_request(opt, data) + self.doing = assert(not self.doing, "httpc class cannot be used by multiple coroutines at the same time.") + -- 创建链接或重连 if not self.sock then if not self.reconnect then + self.doing = nil return nil, "httpc class can't connect to server 1 : " .. self.domain end local sock = sock_new():timeout(self.timeout) - local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) - if not ok then + if not sock_connect(sock, opt.protocol, opt.domain, opt.port) then sock:close() + self.doing = nil return nil, "httpc class can't connect to server : " .. self.domain end self.sock = sock end - - local ok, err = sock_send(self.sock, opt.protocol, data) - if not ok then + -- 发送请求数据 + if not sock_send(self.sock, opt.protocol, data) then self.sock:close() + self.sock = nil if not self.reconnect then + self.doing = nil return nil, "httpc class can't connect to server 2 : " .. self.domain end local ok, err @@ -96,25 +100,30 @@ function httpc:send_request(opt, data) ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) if not ok then sock:close() + self.doing = nil return ok, err end ok, err = sock_send(sock, opt.protocol, data) if not ok then sock:close() + self.doing = nil return nil, err end self.sock = sock end + self.doing = nil return true end -- 读取响应 function httpc:read_response(opt) + self.doing = assert(not self.doing, "class cannot be used by multiple coroutines at the same time.") local code, msg, headers = httpc_response(self.sock, opt.protocol) if not code then self.sock:close() self.sock = nil end + self.doing = nil return code, msg, headers end From ba9a897b8c2f44e2afdc4745d17fc7837148b157 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 22 Aug 2021 17:44:02 +0800 Subject: [PATCH 832/956] =?UTF-8?q?=E9=87=8D=E6=96=B0=E5=AE=9E=E7=8E=B0cla?= =?UTF-8?q?ss=E4=B8=8E=E5=A2=9E=E5=8A=A0=E7=BB=A7=E6=89=BF=E7=89=B9?= =?UTF-8?q?=E6=80=A7.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/class/init.lua | 41 ++++++++++++++--------------------------- lualib/class/meta.lua | 26 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 27 deletions(-) create mode 100644 lualib/class/meta.lua diff --git a/lualib/class/init.lua b/lualib/class/init.lua index 8d916a54..07d76249 100644 --- a/lualib/class/init.lua +++ b/lualib/class/init.lua @@ -1,35 +1,22 @@ -local type = type -local assert = assert +local type, pairs = type, pairs local getmetatable = getmetatable local setmetatable = setmetatable --- 所有对象的基类 -local META = { __META_CLASS__ = true } +local metaclass = require "class.meta" -META.__index = META - -META.new = function (M, ...) - assert(META == getmetatable(M), "[Lua-CLASS ERROR]: Must use `:` to create object.") - local ctor = M.ctor - local obj = setmetatable({}, M) - assert(ctor and type(ctor) == 'function' and ctor, "[Lua-CLASS ERROR]: Can't find `ctor` to init.")(obj, ...) - return obj -end - -META.__call = function (M, ...) - local meta = getmetatable(M) - if meta == META then - return M:new(...); - end - return assert(getmetatable(M) == META and M['__name'], "[Lua-CLASS ERROR]: Invalid class arguments.")(M, ...); -end - ----comment `Class`是内部的所有对象用来实现面向对象的方法. +---comment `class`是内部的所有对象用来实现面向对象的方法. ---@param cname? string @自定义类名 ----@param meta? table | function | nil @自定义类行为 +---@param cmeta? table | nil @自定义父类 ---@return table @返回一个类 -return function (cname, meta) +return function (cname, cmeta) local cls = { __name = cname } - cls.__index = meta or cls - return setmetatable(cls, META) + if getmetatable(cmeta) == metaclass then + for k, v in pairs(cmeta) do + if type(v) == 'function' or (k ~= '__name' and k ~= '__index' and k ~= '__metatable') then + cls[k] = v + end + end + end + cls.__index = cls + return setmetatable(cls, metaclass) end \ No newline at end of file diff --git a/lualib/class/meta.lua b/lualib/class/meta.lua new file mode 100644 index 00000000..a5eac0c0 --- /dev/null +++ b/lualib/class/meta.lua @@ -0,0 +1,26 @@ +local assert, type = assert, type +local getmetatable = getmetatable +local setmetatable = setmetatable + +--`Anonymous`是所有对象的基类. +local Anonymous = { __META__ = true, __name = "Anonymous" } + +Anonymous.__index = Anonymous + +Anonymous.new = function (M, ...) + assert(Anonymous == getmetatable(M), "[Lua-CLASS ERROR]: Must use `:` to create object.") + local ctor = M.ctor + local obj = setmetatable({}, M) + assert(ctor and type(ctor) == 'function' and ctor, "[Lua-CLASS ERROR]: Can't find `ctor` to init.")(obj, ...) + return obj +end + +Anonymous.__call = function (M, ...) + local meta = getmetatable(M) + if meta == Anonymous then + return M:new(...); + end + return assert(getmetatable(M) == Anonymous and M['__name'], "[Lua-CLASS ERROR]: Invalid class arguments.")(M, ...); +end + +return Anonymous \ No newline at end of file From 34294c6c9bd0b7f22ca6ba29553e33ff0d5e1333 Mon Sep 17 00:00:00 2001 From: "Mr.che" <869646063@qq.com> Date: Mon, 23 Aug 2021 12:18:16 +0800 Subject: [PATCH 833/956] Update init.lua MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复httpd日志输出的端口显示问题. --- lualib/httpd/init.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lualib/httpd/init.lua b/lualib/httpd/init.lua index a7ae282c..b98760ec 100644 --- a/lualib/httpd/init.lua +++ b/lualib/httpd/init.lua @@ -270,9 +270,9 @@ end function httpd:run() if self.ip and self.port then if self.logging then - self.logging:dump(fmt('[%s] [INFO] httpd listen: %s:%s \n', os_date("%Y/%m/%d %H:%M:%S"), self.ip, self.port)) + self.logging:dump(fmt('[%s] [INFO] httpd listen: %s:%d \n', os_date("%Y/%m/%d %H:%M:%S"), self.ip, self.port)) end - io_write(fmt('\27[32m[%s] [INFO]\27[0m httpd listen: %s:%s \n', os_date("%Y/%m/%d %H:%M:%S"), self.ip, self.port)) + io_write(fmt('\27[32m[%s] [INFO]\27[0m httpd listen: %s:%d \n', os_date("%Y/%m/%d %H:%M:%S"), self.ip, self.port)) end if self.unix_domain_path then @@ -284,9 +284,9 @@ function httpd:run() if self.ssl_ip and self.ssl_port and self.ssl_key and self.ssl_cert then if self.logging then - self.logging:dump(fmt('[%s] [INFO] httpd ssl listen: %s:%s\n', os_date("%Y/%m/%d %H:%M:%S"), self.ssl_ip, self.ssl_port)) + self.logging:dump(fmt('[%s] [INFO] httpd ssl listen: %s:%d\n', os_date("%Y/%m/%d %H:%M:%S"), self.ssl_ip, self.ssl_port)) end - io_write(fmt('\27[32m[%s] [INFO]\27[0m httpd ssl listen: %s:%s\n', os_date("%Y/%m/%d %H:%M:%S"), self.ssl_ip, self.ssl_port)) + io_write(fmt('\27[32m[%s] [INFO]\27[0m httpd ssl listen: %s:%d\n', os_date("%Y/%m/%d %H:%M:%S"), self.ssl_ip, self.ssl_port)) end if self.logging then From ef02b43f75075d255456d92d2d371c174cf4e4fb Mon Sep 17 00:00:00 2001 From: "Mr.che" <869646063@qq.com> Date: Mon, 23 Aug 2021 12:37:47 +0800 Subject: [PATCH 834/956] Update lsys.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加time方法 --- luaclib/src/lsys.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/luaclib/src/lsys.c b/luaclib/src/lsys.c index daee025c..edcd1ff2 100644 --- a/luaclib/src/lsys.c +++ b/luaclib/src/lsys.c @@ -11,6 +11,12 @@ static int lnow(lua_State *L){ return 1; } +// 提供一个精确到毫秒的时间戳 +static int ltime(lua_State *L){ + lua_pushinteger(L, (uint64_t)(now() * 1e3)); + return 1; +} + /* 此方法可用于检查是否为有效ipv4地址*/ static int lipv4(lua_State *L){ size_t str_len = 0; @@ -178,6 +184,7 @@ LUAMOD_API int luaopen_sys(lua_State *L){ luaL_Reg sys_libs[] = { {"os", los}, {"now", lnow}, + {"time", ltime}, {"date", ldate}, {"ipv4", lipv4}, {"ipv6", lipv6}, From dbbca605032938877a7ef388fbe684bbcc7741cb Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 24 Aug 2021 07:27:29 +0800 Subject: [PATCH 835/956] =?UTF-8?q?=E5=87=8F=E5=B0=91=E9=A2=84=E5=88=86?= =?UTF-8?q?=E9=85=8D=E5=86=85=E5=AD=98=E5=8D=A0=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/aio/init.lua | 2 +- lualib/internal/Co.lua | 8 ++++---- lualib/internal/TCP.lua | 2 +- lualib/internal/Timer.lua | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lualib/aio/init.lua b/lualib/aio/init.lua index bf19d1c5..c7598b35 100644 --- a/lualib/aio/init.lua +++ b/lualib/aio/init.lua @@ -37,7 +37,7 @@ local assert = assert local toint = math.tointeger local tconcat = table.concat -local aio = new_tab(0, 1 << 16) +local aio = new_tab(0, 128) local File = class("__AIO__") diff --git a/lualib/internal/Co.lua b/lualib/internal/Co.lua index fe01f166..6a4ea4c2 100644 --- a/lualib/internal/Co.lua +++ b/lualib/internal/Co.lua @@ -32,10 +32,10 @@ local main_waited = true local co_num = 0 -local co_map = new_tab(0, 1024) +local co_map = new_tab(0, 128) co_map[co_self()] = {co_self(), nil, false} -local co_wlist = new_tab(512, 0) +local co_wlist = new_tab(128, 0) local function co_wrapper() return co_new(function () @@ -45,7 +45,7 @@ local function co_wrapper() local start, total = 1, #co_wlist -- 使用两级`FIFO`队列交替管理协程的运行与切换, 并且每次预分配的`FIFO`队列的大小与上次执行的协程的数量相关. local co_rlist = co_wlist - co_wlist = new_tab((total & ~3) + 4, 0) + co_wlist = new_tab(128, 0) while true do for index = start, total do local obj = co_rlist[index] @@ -75,7 +75,7 @@ local function co_wrapper() total = #co_wlist end co_rlist = co_wlist - co_wlist = new_tab((total & ~3) + 4, 0) + co_wlist = new_tab(128, 0) end end) end diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index f40a8d62..8643b544 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -68,7 +68,7 @@ local tcp_ssl_set_userdata_key = tcp.ssl_set_userdata_key local EVENT_READ = 0x01 local EVENT_WRITE = 0x02 -local POOL = new_tab(1 << 10, 0) +local POOL = new_tab(128, 0) local function tcp_pop() return remove(POOL) or tcp_new() end diff --git a/lualib/internal/Timer.lua b/lualib/internal/Timer.lua index c66ebdad..83cefb2c 100644 --- a/lualib/internal/Timer.lua +++ b/lualib/internal/Timer.lua @@ -19,9 +19,9 @@ local co_wait_ex = coroutine.yield local tremove = table.remove local tinsert = table.insert -local Timer = new_tab(0, 1 << 10) +local Timer = new_tab(0, 128) -local Tlist = new_tab(1 << 10, 0) +local Tlist = new_tab(128, 0) -- 创建`Timer`对象 local function Timer_new() From 6bba2497f3794af6c8d5600fa5d8a452d2b6bc4b Mon Sep 17 00:00:00 2001 From: "Mr.che" <869646063@qq.com> Date: Fri, 27 Aug 2021 17:37:46 +0800 Subject: [PATCH 836/956] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E7=9A=84=E9=94=99=E8=AF=AF=E6=97=A5=E5=BF=97.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index fa67e636..11bd6d51 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -309,7 +309,6 @@ static int create_client_unixsock(const char* path, size_t path_len) { int ret = connect(sockfd, (struct sockaddr*)&UN, sizeof(UN)); if (0 > ret) { - LOG("ERROR", strerror(errno)); close(sockfd); return -1; } From a4b76093bb64cf6ee2dfcb232feb91a2824a826b Mon Sep 17 00:00:00 2001 From: "Mr.che" <869646063@qq.com> Date: Tue, 31 Aug 2021 11:35:56 +0800 Subject: [PATCH 837/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0Cookie=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Cookie.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/httpd/Cookie.lua b/lualib/httpd/Cookie.lua index 0ec704a0..ff0362df 100644 --- a/lualib/httpd/Cookie.lua +++ b/lualib/httpd/Cookie.lua @@ -98,7 +98,7 @@ function Cookie.deserialization (cs) Cookie.client[co] = Cookies return end - for name, value in splite(cs, '([^ ;]+)=([^ ;]+)') do + for name, value in splite(cs, '([^ =]+)=([^ ;]+)') do Cookies[name] = value end Cookie.client[co] = Cookies From abfcf744fb6f61b93495fb0777cac291c4c82eea Mon Sep 17 00:00:00 2001 From: "Mr.che" <869646063@qq.com> Date: Wed, 8 Sep 2021 13:27:33 +0800 Subject: [PATCH 838/956] Update dns.lua --- lualib/protocol/dns.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/protocol/dns.lua b/lualib/protocol/dns.lua index 1ab7b7dd..7806b0d5 100644 --- a/lualib/protocol/dns.lua +++ b/lualib/protocol/dns.lua @@ -87,7 +87,7 @@ local function check_ip(ip) end local function gen_cache() - local file = io.open("/etc/hosts", "r") + local file = io.open("/etc/hosts", "r") or io.open("/cygdrive/c/Windows/System32/drivers/etc/hosts", "r") or io.open("/c/Windows/System32/drivers/etc/hosts", "r") if file then for line in file:lines() do if not find(line, "^([%G]*)#") then From c4ec7454e6a844717addde0252e901d1efb15180 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 22 Sep 2021 22:09:16 +0800 Subject: [PATCH 839/956] Update lsys.c --- luaclib/src/lsys.c | 56 +++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/luaclib/src/lsys.c b/luaclib/src/lsys.c index edcd1ff2..276a0ba8 100644 --- a/luaclib/src/lsys.c +++ b/luaclib/src/lsys.c @@ -7,14 +7,14 @@ // 提供一个精确到微秒的时间戳 static int lnow(lua_State *L){ - lua_pushnumber(L, now()); - return 1; + lua_pushnumber(L, now()); + return 1; } // 提供一个精确到毫秒的时间戳 static int ltime(lua_State *L){ - lua_pushinteger(L, (uint64_t)(now() * 1e3)); - return 1; + lua_pushinteger(L, (uint64_t)(now() * 1e3)); + return 1; } /* 此方法可用于检查是否为有效ipv4地址*/ @@ -110,29 +110,29 @@ static int lnew_tab(lua_State *L){ return 1; } -/* 高效替换字符串 */ -static int lstrrep(lua_State *L){ - size_t src_len = 0; - const char *src = (const char *)luaL_checklstring(L, 1, &src_len); - if (!src || src_len == 0) - return luaL_error(L, "Invalid source string."); - - lua_Integer pos = luaL_checkinteger(L, 2); - if (pos < 1 || pos > src_len) - return luaL_error(L, "Invalid source pos."); - - size_t rep_len = 0; - const char *rep = (const char *)luaL_checklstring(L, 3, &rep_len); - if (!rep || rep_len == 0 || rep_len > src_len || rep_len > (src_len - pos + 1)) - return luaL_error(L, "Invalid rep string."); - - luaL_Buffer B; - char* str = luaL_buffinitsize(L, &B, src_len); - /* 将源字符串拷贝到开辟空间 */ - memmove(str, src, src_len); - /* 将替换内容覆盖原先的内存 */ - memmove(str + (pos - 1), rep, rep_len); - luaL_pushresultsize(&B, src_len); +/* 创建表 */ +static int lusage(lua_State *L) { + #include + struct rusage usage; + int ret = getrusage(RUSAGE_SELF , &usage); + if (ret == -1){ + lua_pushnil(L); + lua_pushstring(L, strerror(errno)); + return 2; + } + + lua_createtable(L, 0, 8); + + #define luaL_setki(L, k, i) ({lua_pushstring(L, (k)); lua_pushinteger(L, (i)); lua_rawset(L, -3);}) + #define luaL_setkn(L, k, v) ({lua_pushstring(L, (k)); lua_pushnumber(L, (v)); lua_rawset(L, -3);}) + luaL_setkn(L, "utime", usage.ru_utime.tv_sec + (usage.ru_utime.tv_usec * 1e-6)); + luaL_setkn(L, "ktime", usage.ru_stime.tv_sec + (usage.ru_stime.tv_usec * 1e-6)); + luaL_setki(L, "rss", usage.ru_maxrss); luaL_setki(L, "swap", usage.ru_nswap); + luaL_setki(L, "inblock", usage.ru_inblock); luaL_setki(L, "oublock", usage.ru_oublock); + luaL_setki(L, "hard_page_fault", usage.ru_majflt); luaL_setki(L, "soft_page_fault", usage.ru_minflt); + #undef luaL_setki + #undef luaL_setkn + return 1; } @@ -188,7 +188,7 @@ LUAMOD_API int luaopen_sys(lua_State *L){ {"date", ldate}, {"ipv4", lipv4}, {"ipv6", lipv6}, - {"strrep", lstrrep}, + {"usage", lusage}, {"str2ip", lstr2ip}, {"ip2str", lip2str}, {"hostname", lhostname}, From 7ffc6d2a4779f4510f837fda9ddbdaa061d36f5d Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 28 Sep 2021 23:33:32 +0800 Subject: [PATCH 840/956] =?UTF-8?q?=E9=99=8D=E4=BD=8ETimer=E7=B2=BE?= =?UTF-8?q?=E5=BA=A6,=E5=A2=9E=E5=8A=A0=E5=86=85=E5=AD=98=E5=88=A9?= =?UTF-8?q?=E7=94=A8=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/Timer.lua | 165 ++++++++++++++++++++------------------ 1 file changed, 86 insertions(+), 79 deletions(-) diff --git a/lualib/internal/Timer.lua b/lualib/internal/Timer.lua index 83cefb2c..a9aa5a1c 100644 --- a/lualib/internal/Timer.lua +++ b/lualib/internal/Timer.lua @@ -1,12 +1,12 @@ +local sys = require "sys" +local time = sys.time + local co = require "internal.Co" local ti = require "timer" -local new_tab = require("sys").new_tab - local type = type local ti_new = ti.new local ti_start = ti.start -local ti_stop = ti.stop local co_new = co.new local co_wait = co.wait @@ -14,33 +14,91 @@ local co_spawn = co.spawn local co_wakeup = co.wakeup local co_self = co.self +local co_start = coroutine.resume local co_wait_ex = coroutine.yield -local tremove = table.remove -local tinsert = table.insert +local tab = debug.getregistry() +if tab['__G_TIMER__'] then + return tab['__G_TIMER__'] +end -local Timer = new_tab(0, 128) +local Timer = {} +tab['__G_TIMER__'] = Timer -local Tlist = new_tab(128, 0) +local TMap = {} --- 创建`Timer`对象 -local function Timer_new() - return tremove(Tlist) or ti_new() +local function get_tid(offset) + return (time() + offset * 1e3) * 0.1 // 1 end --- 停止`Timer`对象 -local function Timer_stop(self) - Timer[self] = nil - tinsert(Tlist, self.t) - ti_stop(self.t) - return true +local function get_ctx(tid) + return TMap[tid] +end + +local function set_ctx(tid, ctx) + if not ctx then + TMap[tid] = nil + return + end + local list = TMap[tid] + if not list then + TMap[tid] = {ctx} + else + list[#list+1] = ctx + end + return ctx end --- 启动`Timer`对象 -local function Timer_start(self) - Timer[self] = self - ti_start(self.t, self.timeout or self.repeats, self.co) - return self +local TTimer = ti_new() +Timer.TTimer = TTimer + +Timer.TCo = co_new(function () + local TNow = get_tid(0) + co_wait_ex(co_self()) + while true do + local now = get_tid(0) + -- print("距离", now - TNow ) + for tid = TNow, now, 1 do + local list = get_ctx(tid) + if list then + for idx = 1, #list do + local ctx = list[idx] + if ctx[1] then + co_spawn(ctx[3]) + if ctx[4] then -- 如果需要重复 + set_ctx(get_tid(ctx[2]), ctx) + end + end + end + set_ctx(tid, nil) + end + end + TNow = now + co_wait_ex() + end +end) + +-- 初始化 +co_start(Timer.TCo) +-- 选一个误差最小的间隔 +ti_start(TTimer, 0.01, Timer.TCo) + +---@class Timer @定时器对象 + +---comment 初始化定时器对象 +---@param timeout number @超时时间 +---@param again boolean @是否重复 +---@param func function @回调函数 +---@return Timer +local function Timer_Init(timeout, again, func) + local ctx = set_ctx(get_tid(timeout), { true, timeout, func, again }) + return { + stop = function (self) + if self and ctx and ctx[1] then + ctx[1] = false + end + end + } end ---comment 一次性定时器 @@ -50,26 +108,7 @@ function Timer.timeout(timeout, callback) if type(timeout) ~= 'number' or timeout <= 0 or type(callback) ~= 'function' then return end - local timer = { t = Timer_new(), timeout = timeout, stoped = false } - -- 实现`停止定时器`的方法 - timer.stop = function(self) - if not self.stoped then - self.stoped = true - Timer_stop(self) - end - return true - end - -- 实现定时器回调协程 - timer.co = co_new(function() - if timer.stoped then - timer.co = nil - return - end - co_spawn(callback) - return Timer_stop(timer) - end) - -- 启动定时器 - return Timer_start(timer) + return Timer_Init(timeout, false, callback) end ---comment 重复定时器 @@ -79,52 +118,20 @@ function Timer.at(repeats, callback) if type(repeats) ~= 'number' or repeats <= 0 or type(callback) ~= 'function' then return end - local timer = { t = Timer_new(), repeats = repeats, stoped = false } - -- 实现`停止定时器`的方法 - timer.stop = function(self) - if not self.stoped then - self.stoped = true - Timer_stop(self) - end - return true - end - -- 实现定时器回调协程 - timer.co = co_new(function() - while true do - if timer.stoped then - timer.co = nil - return - end - co_spawn(callback) - co_wait_ex() - end - end) - -- 启动定时器 - return Timer_start(timer) + return Timer_Init(repeats, true, callback) end ---comment 休眠当前协程 ----@param nsleep number @休眠时间(毫秒) +---@param nsleep number @休眠时间(毫秒) function Timer.sleep(nsleep) if type(nsleep) ~= 'number' or nsleep <= 0 then return end - -- 创建`Timer`对象 - local timer = { t = Timer_new(), main_co = co_self(), timeout = nsleep } - -- 实现定时器回调协程 - timer.co = co_new(function () - Timer_stop(timer) - timer.co = nil - return co_wakeup(timer.main_co) + local coctx = co_self() + Timer_Init(nsleep, false, function () + return co_wakeup(coctx) end) - Timer_start(timer) - return co_wait() -end - ----comment 计算缓存的定时器对象数量 ----@return integer -function Timer.count() - return #Tlist + co_wait() end return Timer \ No newline at end of file From f9e2882d466c8d13b790a22af336463ba52410af Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 29 Sep 2021 23:56:07 +0800 Subject: [PATCH 841/956] =?UTF-8?q?=E4=BC=98=E5=8C=96sleep=E5=94=A4?= =?UTF-8?q?=E9=86=92=E6=B5=81=E7=A8=8B=E4=B8=8E=E5=87=8F=E5=B0=91=E6=BD=9C?= =?UTF-8?q?=E5=9C=A8=E7=9A=84=E6=9F=A5=E8=A1=A8=E6=AC=A1=E6=95=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/Timer.lua | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/lualib/internal/Timer.lua b/lualib/internal/Timer.lua index a9aa5a1c..61621fac 100644 --- a/lualib/internal/Timer.lua +++ b/lualib/internal/Timer.lua @@ -53,6 +53,7 @@ local TTimer = ti_new() Timer.TTimer = TTimer Timer.TCo = co_new(function () + local run_idx, time_idx, func_idx, again_idx, async_idx = 1, 2, 3, 4, 5 local TNow = get_tid(0) co_wait_ex(co_self()) while true do @@ -63,35 +64,41 @@ Timer.TCo = co_new(function () if list then for idx = 1, #list do local ctx = list[idx] - if ctx[1] then - co_spawn(ctx[3]) - if ctx[4] then -- 如果需要重复 - set_ctx(get_tid(ctx[2]), ctx) + if ctx[run_idx] then + local cb = ctx[func_idx] + if ctx[async_idx] then + cb() + else + co_spawn(cb) + end + if ctx[again_idx] then -- 如果需要重复 + set_ctx(get_tid(ctx[time_idx]), ctx) end end end set_ctx(tid, nil) end end - TNow = now + TNow = now + 1 co_wait_ex() end end) -- 初始化 co_start(Timer.TCo) --- 选一个误差最小的间隔 -ti_start(TTimer, 0.01, Timer.TCo) +-- 选更小的时间来定期检查. +ti_start(TTimer, 0.005, Timer.TCo) ----@class Timer @定时器对象 +---@class Timer ---comment 初始化定时器对象 ---@param timeout number @超时时间 ---@param again boolean @是否重复 +---@param async boolean @直接调用 ---@param func function @回调函数 ----@return Timer -local function Timer_Init(timeout, again, func) - local ctx = set_ctx(get_tid(timeout), { true, timeout, func, again }) +---@return Timer @定时器对象 +local function Timer_Init(timeout, again, async, func) + local ctx = set_ctx(get_tid(timeout), { true, timeout, func, again, async }) return { stop = function (self) if self and ctx and ctx[1] then @@ -108,7 +115,7 @@ function Timer.timeout(timeout, callback) if type(timeout) ~= 'number' or timeout <= 0 or type(callback) ~= 'function' then return end - return Timer_Init(timeout, false, callback) + return Timer_Init(timeout, false, false, callback) end ---comment 重复定时器 @@ -118,7 +125,7 @@ function Timer.at(repeats, callback) if type(repeats) ~= 'number' or repeats <= 0 or type(callback) ~= 'function' then return end - return Timer_Init(repeats, true, callback) + return Timer_Init(repeats, true, false, callback) end ---comment 休眠当前协程 @@ -128,7 +135,7 @@ function Timer.sleep(nsleep) return end local coctx = co_self() - Timer_Init(nsleep, false, function () + Timer_Init(nsleep, false, true, function () return co_wakeup(coctx) end) co_wait() From a1c67a466cc52ddb42d5ed2ac3c7e2affe0e2df2 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 2 Oct 2021 01:40:55 +0800 Subject: [PATCH 842/956] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E5=8F=AF=E8=83=BD?= =?UTF-8?q?=E5=AD=98=E5=9C=A8=E7=9A=84=E9=93=BE=E6=8E=A5=E3=80=81=E8=BF=90?= =?UTF-8?q?=E8=A1=8C=E4=BE=9D=E8=B5=96=E4=B8=8D=E4=B8=80=E8=87=B4=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/Makefile | 2 +- luaclib/src/lz/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/luaclib/src/lcrypt/Makefile b/luaclib/src/lcrypt/Makefile index fe04f6bf..ac398583 100644 --- a/luaclib/src/lcrypt/Makefile +++ b/luaclib/src/lcrypt/Makefile @@ -9,7 +9,7 @@ default : CC = cc -CFLAGS = -O3 -Wall -shared -fPIC -fno-strict-aliasing -Wno-deprecated-declarations +CFLAGS = -O3 -Wall -shared -fPIC -fno-strict-aliasing -Wno-deprecated-declarations -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib DLL = -lcore -lcrypto INCLUDES = -I../../../src -I/usr/local/include diff --git a/luaclib/src/lz/Makefile b/luaclib/src/lz/Makefile index 7d7a7eb2..3a8a4e3c 100644 --- a/luaclib/src/lz/Makefile +++ b/luaclib/src/lz/Makefile @@ -12,7 +12,7 @@ CC = cc INCLUDES += -I../../../src -I/usr/local/include LIBS = -L../ -L../../ -L../../../ -L/usr/local/lib -CFLAGS = -O3 -Wall -shared -fPIC +CFLAGS = -O3 -Wall -shared -fPIC -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib DLL = -lcore -lz build: From bb5567e1fb3eef36cdc5f154a5cbefaf69bf4def Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 7 Oct 2021 14:44:29 +0800 Subject: [PATCH 843/956] =?UTF-8?q?=E6=89=A9=E5=B1=95string=E4=B8=8Etable?= =?UTF-8?q?=E7=9A=84=E5=86=85=E7=BD=AE=E6=96=B9=E6=B3=95=E6=94=AF=E6=8C=81?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/utils/init.lua | 31 +--- lualib/utils/string.lua | 215 +++++++++++++++++++++++++ lualib/utils/table.lua | 345 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 565 insertions(+), 26 deletions(-) create mode 100644 lualib/utils/string.lua create mode 100644 lualib/utils/table.lua diff --git a/lualib/utils/init.lua b/lualib/utils/init.lua index 0afa994a..02ea5400 100644 --- a/lualib/utils/init.lua +++ b/lualib/utils/init.lua @@ -1,3 +1,6 @@ +require "utils.string" +require "utils.table" + -- 格式化输出(美化) _G["var_dump"] = function (data, showMetatable, lastCount) if type(data) ~= "table" then @@ -24,8 +27,7 @@ _G["var_dump"] = function (data, showMetatable, lastCount) for key,value in pairs(data) do for i = 1,count do io.write(" ") end if type(key) == "string" then - -- io.write("\"", key, "\" = ") - io.write('["', key, '"] = ') + io.write('["', key, '"] = ') elseif type(key) == "number" then io.write("[", key, "] = ") else @@ -42,27 +44,4 @@ _G["var_dump"] = function (data, showMetatable, lastCount) if not lastCount then io.write("\n") end -end - --- local co = require "internal.Co" --- local tcp = require "internal.TCP" --- local Timer = require "internal.Timer" --- co.spawn(function ( ... ) --- while 1 do --- local self = co.self() --- local ti = Timer.timeout(1, function() --- local co_count, task_count = co.count() --- local tcp_count = tcp.count() --- local time_count = Timer.count() --- print("=======================") --- print("co 数量为:", co_count) --- print("tcp 数量为:", tcp_count) --- print("task 数量为:", task_count) --- print("timer 数量为:", time_count) --- print("当前内存为:", collectgarbage('count')) --- print("=======================") --- co.wakeup(self) --- end) --- co.wait() --- end --- end) +end \ No newline at end of file diff --git a/lualib/utils/string.lua b/lualib/utils/string.lua new file mode 100644 index 00000000..a2c15b63 --- /dev/null +++ b/lualib/utils/string.lua @@ -0,0 +1,215 @@ +local type = type +local select = select +local assert = assert +local tonumber = tonumber + +local string = string +local ssub = string.sub +local srep = string.rep +local sfind = string.find +local sbyte = string.byte +local sgsub = string.gsub +local sgmatch = string.gmatch + +local tconcat = table.concat + +---comment 计算`pattern`在`text`中pos位置开始出现的总次数. +---@param text string @实际内容 +---@param pattern string @匹配内容 +---@param pos integer @字符串 +---@return string @返回拼接好的字符串 +function string.count (text, pattern, pos) + local count = 0 + if not pos then + pos = 1 + end + while true do + local s, e = sfind(text, pattern, pos) + if not s or not e then + break + end + count = count + 1 + pos = e + 1 + end + return count +end + +---comment 以`text`为中心拼接`count`个`fill`在两侧. +---@param text string @实际内容 +---@param fill string @填充字符 +---@param count integer @填充数量 +---@return string @返回拼接好的字符串 +function string.center (text, fill, count) + assert(type(text) == 'string' and type(fill) == 'string' , "Invalid string `text` or `fill`.") + assert(type(count) == 'number' and tonumber(count) or count > 0, "Invalid fill `count`") + local fill_text = srep(fill, count) + return tconcat{ fill_text, text, fill_text } +end + +---comment 判断指定字符串内容是否全部空格 +---@param text string @判断内容 +---@return boolean @如果`text`全是空格返回`true`, 否则返回`false`. +function string.allspace (text) + return sfind(text or '', "^[ ]+$") and true or false +end + +---comment 连接`1`个或`N`个字符串, 非可转换字符串的对象会抛出异常 +---@return string +function string.join (...) + if select("#", ...) <= 1 then + return (...) or '' + end + return tconcat{...} +end + +---comment 根据`sep`分割`text`字符串. +---@param text string @待分割的字符串 +---@param sep string @分割用的分隔符 +---@return table @分割后的数组 +function string.split(text, sep) + assert(type(text) == 'string', "Invalid string `text`.") + if not sep or type(sep) ~= 'string' or sep == '' then + sep = '%,' + end + local index = 1 + local list = {} + for sub in sgmatch(text, "([^" .. sep .. "]*)") do + list[index] = sub + index = index + 1 + end + if index == 1 then + list[index] = text + end + return list +end + +local function strip(text, sep, left, right) + if left then + text = sgsub(text, "^[" .. sep .."]+", "", 1) + end + if right then + text = sgsub(text, "[" .. sep .."]+$", "", 1) + end + return text +end + +---comment 移除字符串头、尾指定的字符 +---@param text string @待分割的字符串 +---@param sep string @移除用的分隔符 +function string.strip (text, sep) + assert(type(text) == 'string', "Invalid string `text`.") + if not sep or type(sep) ~= 'string' or sep == '' then + sep = ' ' + end + return strip(text, sep, true, true) +end + +---comment 移除字符串头部指定的字符 +---@param text string @待分割的字符串 +---@param sep string @移除用的分隔符 +function string.lstrip (text, sep) + assert(type(text) == 'string', "Invalid string `text`.") + if not sep or type(sep) ~= 'string' or sep == '' then + sep = ' ' + end + return strip(text, sep, true, false) +end + +---comment 移除字符串尾部指定的字符 +---@param text string @待分割的字符串 +---@param sep string @移除用的分隔符 +function string.rstrip (text, sep) + assert(type(text) == 'string', "Invalid string `text`.") + if not sep or type(sep) ~= 'string' or sep == '' then + sep = ' ' + end + return strip(text, sep, false, true) +end + +---comment 判断起始位置是否指定内容 +---@param text string @待匹配内容 +---@param sstring string @其实内容 +---@param start integer @起始位置 +function string.startswith(text, sstring, start) + assert(type(text) == 'string', "Invalid string `text`.") + assert(type(sstring) == 'string', "Invalid start string.") + return sfind(text, '^' .. sstring, start) and true or false +end + +---comment 判断结束位置是否指定内容 +---@param text string @待匹配内容 +---@param estring string @结束内容 +---@param over integer @结束位置 +function string.endswith(text, estring, over) + assert(type(text) == 'string', "Invalid string `text`.") + assert(type(estring) == 'string', "Invalid end string.") + return sfind(text, estring .. '$', over) and true or false +end + +---comment 将`text`里的`s1`替换为`s2`(最多`count`此) +---@param text string @原始内容 +---@param s1 string @匹配字符串 +---@param s2 string @替换字符串 +---@param count number @替换次数 +---@return string @替换后内容 +function string.replace (text, s1, s2, count) + assert(type(text) == 'string' and text ~= '', "Invalid text string.") + assert(type(s1) == 'string' and s1 ~= '', "Invalid s1 string.") + assert(type(s2) == 'string' and s2 ~= '', "Invalid s2 string.") + local s = sgsub(text, s1, s2, count) + return s +end + +---comment 字符串转换为字节数组 +---@param text string @待转换的字符串 +---@return table @转换后的字节数组 +function string.tobytes(text) + assert(type(text) == 'string' and text ~= '', "Invalid text string.") + local list = {} + for idx = 1, #text do + list[#list+1] = sbyte(text, idx) + end + return list +end + +---comment 向指定位置的字符串后插入字符串. +---@param text string @原始字符串 +---@param pos integer @待插入的位置 +---@param str string @待插入的字符串 +---@return string @返回插入后的字符串内容 +function string.insert(text, pos, str) + assert(type(text) == 'string' and text ~= '', "Invalid text string.") + assert(type(pos) == 'number', "Invalid pos integer.") + assert(type(str) == 'string' and str ~= '', "Invalid text string.") + return tconcat{ssub(text, 1, pos), str, ssub(text, pos + 1, -1)} +end + +local _, liconv = pcall(require, "liconv") + +---comment 替换iconv函数, 错误的实现会出现运行时错误. +---@param module table +---@param encode string @需保证`module[encode]`行为与`liconv.to`行为一致 +---@param decode string @需保证`module[decode]`行为与`liconv.from`行为一致 +function string.iconv(module, encode, decode) + liconv = { to = module[encode], from = module[decode] } +end + +---comment 使用iconv进行编码转换 +---@param text string @文本内容 +---@param encoding string @目标编码 +---@return string @转换后的文本 +function string.encode (text, encoding) + assert(type(text) == 'string', "Invalid string `text`.") + assert(type(encoding) == 'string', "Invalid string encoding.") + return assert(type(liconv) == 'table' and liconv, "Lua iconv is not supported.").to(encoding, text) +end + +---comment 使用iconv进行编码转换 +---@param text string @文本内容 +---@param decoding string @原始编码 +---@return string @转换后的文本 +function string.decode (text, decoding) + assert(type(text) == 'string', "Invalid string `text`.") + assert(type(decoding) == 'string', "Invalid string encoding.") + return assert(type(liconv) == 'table' and liconv, "Lua iconv is not supported.").from(decoding, text) +end \ No newline at end of file diff --git a/lualib/utils/table.lua b/lualib/utils/table.lua new file mode 100644 index 00000000..12be4074 --- /dev/null +++ b/lualib/utils/table.lua @@ -0,0 +1,345 @@ +local type = type +local pairs = pairs +local ipairs = ipairs +local rawlen = rawlen +local assert = assert +local select = select + +local ceil = math.ceil +local floor = math.floor +local sformat = string.format + +local tsort = table.sort +local tconcat = table.concat +local tunpack = table.unpack + +---comment 获取`table`长度 +---@param tab table +---@return integer +function table.len (tab) + assert(type(tab) == 'table', "Invalid table.") + return #tab +end + +---comment 获取`table`长度(跳过元方法) +---@param tab table +---@return integer +function table.rawlen (tab) + assert(type(tab) == 'table', "Invalid table.") + if rawlen then + return rawlen(tab) + end + return #tab +end + +---comment 返回数组内最大`value` +---@param tab table +---@return number +function table.max(tab) + assert(type(tab) == 'table', "Invalid table.") + local s, e = 2, #tab + local max_value = nil + if e < 1 then + return max_value + end + max_value = tab[1] + while s < e do + if max_value < tab[s] then + max_value = tab[s] + end + s = s + 1 + if max_value < tab[e] then + max_value = tab[e] + end + e = e - 1 + end + return max_value +end + +---comment 返回数组内最小`value` +---@param tab table +---@return number +function table.min(tab) + assert(type(tab) == 'table', "Invalid table.") + local s, e = 2, #tab + local min_value = nil + if e < 1 then + return min_value + end + min_value = tab[1] + while s < e do + if min_value > tab[s] then + min_value = tab[s] + end + s = s + 1 + if min_value > tab[e] then + min_value = tab[e] + end + e = e - 1 + end + return min_value +end + +---comment 获取`table`所有`key` +---@param tab table +---@return table @返回`keys`数组 +function table.keys (tab) + assert(type(tab) == 'table', "Invalid table.") + local list = {} + for k, _ in pairs(tab) do + list[#list+1] = k + end + return list +end + +---comment 获取`table`所有`value` +---@param tab table +---@return table @返回`value`数组 +function table.values (tab) + assert(type(tab) == 'table', "Invalid table.") + local list = {} + for _, v in pairs(tab) do + list[#list+1] = v + end + return list +end + +---comment 向左旋转数组 +---@param tab table +---@return table @返回旋转完成后的数组 +function table.lrotate(tab) + assert(type(tab) == 'table', "Invalid table.") + local last = tab[1] + for i = 2, #tab do + tab[i-1] = tab[i] + end + tab[#tab] = last + return tab +end + +---comment 向右旋转数组 +---@param tab table +---@return table @返回旋转完成后的数组 +function table.rrotate(tab) + assert(type(tab) == 'table', "Invalid table.") + local last = tab[#tab] + for i = #tab - 1, 1, -1 do + tab[i+1] = tab[i] + end + tab[1] = last + return tab +end + +---comment 反转数组 +---@param tab table @待反转的数组 +---@return table @返回`tab` +function table.reverse (tab) + assert(type(tab) == 'table', "Invalid table.") + local s, e = 1, #tab + while s < e do + tab[s], tab[e] = tab[e], tab[s] + s = s + 1 + e = e - 1 + end + return tab +end + +local sorts = { + [1] = function (list1, list2) + return list1[1] < list2[1] + end, + [2] = function (list1, list2) + return list2[1] < list1[1] + end, + [3] = function (list1, list2) + return list1[2] < list2[2] + end, + [4] = function (list1, list2) + return list2[2] < list1[2] + end, +} + +---comment 格式化输出表内容 +---@param tab table @原始表 +---@param fmt string | nil @可自定义`key`、`value`格式内容 +---@param sep string | nil @如果表内字段多, 多个`format`字符串连接时候可能会需要用到. +---@param sort integer | nil @默认为(1.key升序),可选:(2.key降序)、(3.value升序)、(4.value降序) +---@return string @最终的输出内容 +function table.format (tab, fmt, sep, sort) + assert(type(tab) == 'table', "Invalid table.") + local list = {} + for k, v in pairs(tab) do + list[#list+1] = {k, v} + end + -- 根据key进行升序排列 + tsort(list, sorts[tonumber(sort) or 1] or sort[1]) + -- 开始合并数据 + for idx, item in ipairs(list) do + list[idx] = sformat(fmt or "%s=%s", item[1], item[2]) + end + return tconcat(list, sep) +end + +---comment 合并2个表 +---@param table1 table | nil @`table1`和`table2`只能有一个为空 +---@param table2 table | nil @`table1`和`table2`只能有一个为空 +---@param new_tab table | nil @可以外部传入或者内部创建 +---@return table +local function table_merge(table1, table2, new_tab) + local tab = new_tab or {} + if type(table1) == 'table' then + for k, v in pairs(table1) do + tab[k] = type(v) ~= 'table' and v or table_merge(v, {}) + end + end + if type(table2) == 'table' then + for k, v in pairs(table2) do + tab[k] = type(v) ~= 'table' and v or table_merge(v, {}) + end + end + return tab +end + +---comment 创建新表来合并2个字典表(不存在引用问题) +---@param table1 table +---@param table2 table +---@return table @始终返回新表 +function table.new (table1, table2) + assert(table1 ~= table2, "You cannot merge two tables of the same type.") + return table_merge(table1, table2, {}) +end + +---comment 合并表`table2`内容到表`table1`内(不存在引用问题) +---@param table1 table +---@param table2 table +---@return table @返回`table1` +function table.lmerge (table1, table2) + assert(table1 ~= table2, "You cannot merge two tables of the same type.") + return table_merge(nil, table2, table1) +end + +---comment 合并表`table1`内容到表`table2`内(不存在引用问题) +---@param table1 table +---@param table2 table +---@return table @返回`table2` +function table.rmerge (table1, table2) + assert(table1 ~= table2, "You cannot merge two tables of the same type.") + return table_merge(nil, table1, table2) +end + +---comment 数组转哈希表 +---@param tab table @待转换的数组 +---@return table @返回转换后的哈希表 +function table.tohash(tab) + assert(type(tab) == 'table', "Invalid table.") + local len = #tab + assert(ceil(len / 2) == floor(len / 2), "Invalid key value amount.") + local hashtab = {} + for idx = 1, len, 2 do + hashtab[tab[idx]] = tab[idx+1] + end + return hashtab +end + +---comment 哈希表转数组(列表) +---@param tab table @待转换的哈希表 +---@return table @返回转换后的数组(列表) +function table.tolist(tab) + assert(type(tab) == 'table', "Invalid table.") + local idx = 1 + local list = {} + for k, v in pairs(tab) do + list[idx] = k + idx = idx + 1 + list[idx] = v + idx = idx + 1 + end + return list +end + +---comment 将一个或者多可`key`->`value`构建为哈希表或者数组 +---@return table @返回构建好的table +function table.wrap(...) + local len = select("#", ...) + assert(ceil(len / 2) == floor(len / 2), "Invalid key value amount.") + local list = {...} + local tab = {} + for idx = 1, len, 2 do + tab[list[idx]] = list[idx+1] + end + return tab +end + +---comment 求一个序列或者多个序列进行函数映射之后的值 +---@param func function @回调函数, 返回值不能为`nil` +---@param list1 table @多个数组(至少一个) +---@return table @返回新数组 +function table.map(func, list1, ...) + assert(type(func) == 'function', "Invalid `function`.") + assert(type(list1) == 'table', "Invalid `table`.") + local newlist = {} + local lists = {list1, ...} + local count = #lists + local len + for i = 1, count do + local l = #(lists[i]) + if not len or len <= l then + len = l + end + end + local args = {} + for index = 1, len do + for idx, list in ipairs(lists) do + args[idx] = list[index] + end + local o = func(tunpack(args)) + if o ~= nil then + newlist[#newlist+1] = o + end + end + return newlist +end + +---comment 过滤不符合函数条件的元素并返回新的结果数组 +---@param func function @回调函数, 返回值必须是`boolean` +---@param list table @原始数组 +---@return table @返回新数组 +function table.filter(func, list) + assert(type(func) == 'function', "Invalid `function`.") + assert(type(list) == 'table', "Invalid `table`.") + local newlist = {} + for i = 1, #list do + local ok = func(list[i]) + assert(type(ok) == 'boolean', "callback must return `true` or `false`.") + if ok then + newlist[#newlist+1] = list[i] + end + end + return newlist +end + +---comment 过滤不符合函数条件的元素并返回新的结果数组 +---@param func function @回调函数, 返回值必须是`boolean` +---@param list table @原始数组 +---@return number @返回计算结果 +function table.reduce(func, list) + assert(type(func) == 'function', "Invalid `function`.") + assert(type(list) == 'table', "Invalid `table`.") + local len = #list + if len <= 1 then + if len == 1 then + return list[1] + end + return error("can't passed empty array.") + end + local args = {list[1], nil} + for i = 2, len do + args[2] = list[i] + local result = func(args[1], args[2]) + if type(result) ~= 'number' then + return + end + args[1] = result + end + return args[1] +end \ No newline at end of file From 291f9acfb9c0e32a70fd775fe9867058ba8d4be9 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 10 Oct 2021 17:35:29 +0800 Subject: [PATCH 844/956] =?UTF-8?q?=E7=A7=BB=E9=99=A4ffi=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/Makefile | 2 - luaclib/src/lffi/ctype.c | 261 ---- luaclib/src/lffi/ffi.c | 2933 ------------------------------------- luaclib/src/lffi/ffi.h | 403 ----- luaclib/src/lffi/makefile | 20 - luaclib/src/lffi/parser.c | 2614 --------------------------------- script/test_ffi.lua | 37 - 7 files changed, 6270 deletions(-) delete mode 100644 luaclib/src/lffi/ctype.c delete mode 100644 luaclib/src/lffi/ffi.c delete mode 100644 luaclib/src/lffi/ffi.h delete mode 100644 luaclib/src/lffi/makefile delete mode 100644 luaclib/src/lffi/parser.c delete mode 100644 script/test_ffi.lua diff --git a/luaclib/Makefile b/luaclib/Makefile index 3e5c51fb..3a7888f6 100644 --- a/luaclib/Makefile +++ b/luaclib/Makefile @@ -32,8 +32,6 @@ internal : @$(MAKE) -C src/lz build @echo "CC - lfs" @$(MAKE) -C src/lfs build - @echo "CC - lffi" - @$(MAKE) -C src/lffi build @echo "CC - lpbc" @$(MAKE) -C src/lpbc build @echo "CC - lpeg" diff --git a/luaclib/src/lffi/ctype.c b/luaclib/src/lffi/ctype.c deleted file mode 100644 index 27186f57..00000000 --- a/luaclib/src/lffi/ctype.c +++ /dev/null @@ -1,261 +0,0 @@ -/* vim: ts=4 sw=4 sts=4 et tw=78 - * Portions copyright (c) 2015-present, Facebook, Inc. All rights reserved. - * Portions copyright (c) 2011 James R. McKaskill. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ -#include "ffi.h" - -static int to_define_key; - -static void update_on_definition(lua_State* L, int ct_usr, int ct_idx) -{ - ct_usr = lua_absindex(L, ct_usr); - ct_idx = lua_absindex(L, ct_idx); - - lua_pushlightuserdata(L, &to_define_key); - lua_rawget(L, ct_usr); - - if (lua_isnil(L, -1)) { - lua_pop(L, 1); /* pop the nil */ - - /* {} */ - lua_newtable(L); - - /* {__mode='k'} */ - lua_newtable(L); - lua_pushliteral(L, "k"); - lua_setfield(L, -2, "__mode"); - - /* setmetatable({}, {__mode='k'}) */ - lua_setmetatable(L, -2); - - /* usr[TO_UPDATE_KEY] = setmetatable({}, {__mode='k'}) */ - lua_pushlightuserdata(L, &to_define_key); - lua_pushvalue(L, -2); - lua_rawset(L, ct_usr); - - /* leave the table on the stack */ - } - - /* to_update[ctype or cdata] = true */ - lua_pushvalue(L, ct_idx); - lua_pushboolean(L, 1); - lua_rawset(L, -3); - - /* pop the to_update table */ - lua_pop(L, 1); -} - -void set_defined(lua_State* L, int ct_usr, struct ctype* ct) -{ - ct_usr = lua_absindex(L, ct_usr); - - ct->is_defined = 1; - - /* update ctypes and cdatas that were created before the definition came in */ - lua_pushlightuserdata(L, &to_define_key); - lua_rawget(L, ct_usr); - - if (!lua_isnil(L, -1)) { - lua_pushnil(L); - - while (lua_next(L, -2)) { - struct ctype* upd = (struct ctype*) lua_touserdata(L, -2); - upd->base_size = ct->base_size; - upd->align_mask = ct->align_mask; - upd->is_defined = 1; - upd->is_variable_struct = ct->is_variable_struct; - upd->variable_increment = ct->variable_increment; - assert(!upd->variable_size_known); - lua_pop(L, 1); - } - - lua_pop(L, 1); - /* usr[TO_UPDATE_KEY] = nil */ - lua_pushlightuserdata(L, &to_define_key); - lua_pushnil(L); - lua_rawset(L, ct_usr); - } else { - lua_pop(L, 1); - } -} - -struct ctype* push_ctype(lua_State* L, int ct_usr, const struct ctype* ct) -{ - struct ctype* ret; - ct_usr = lua_absindex(L, ct_usr); - - ret = (struct ctype*) lua_newuserdata(L, sizeof(struct ctype)); - *ret = *ct; - - push_upval(L, &ctype_mt_key); - lua_setmetatable(L, -2); - -#if LUA_VERSION_NUM == 501 - if (!ct_usr || lua_isnil(L, ct_usr)) { - push_upval(L, &niluv_key); - lua_setfenv(L, -2); - } -#endif - - if (ct_usr && !lua_isnil(L, ct_usr)) { - lua_pushvalue(L, ct_usr); - lua_setuservalue(L, -2); - } - - if (!ct->is_defined && ct_usr && !lua_isnil(L, ct_usr)) { - update_on_definition(L, ct_usr, -1); - } - - return ret; -} - -size_t ctype_size(lua_State* L, const struct ctype* ct) -{ - if (ct->pointers - ct->is_array) { - return sizeof(void*) * (ct->is_array ? ct->array_size : 1); - - } else if (!ct->is_defined || ct->type == VOID_TYPE) { - return luaL_error(L, "can't calculate size of an undefined type"); - - } else if (ct->variable_size_known) { - assert(ct->is_variable_struct && !ct->is_array); - return ct->base_size + ct->variable_increment; - - } else if (ct->is_variable_array || ct->is_variable_struct) { - return luaL_error(L, "internal error: calc size of variable type with unknown size"); - - } else { - return ct->base_size * (ct->is_array ? ct->array_size : 1); - } -} - -void* push_cdata(lua_State* L, int ct_usr, const struct ctype* ct) -{ - struct cdata* cd; - size_t sz = ct->is_reference ? sizeof(void*) : ctype_size(L, ct); - ct_usr = lua_absindex(L, ct_usr); - - /* This is to stop valgrind from complaining. Bitfields are accessed in 8 - * byte chunks so that the code doesn't have to deal with different access - * patterns, but this means that occasionally it will read past the end of - * the struct. As its not setting the bits past the end (only reading and - * then writing the bits back) and the read is aligned its a non-issue, - * but valgrind complains nonetheless. - */ - if (ct->has_bitfield) { - sz = ALIGN_UP(sz, 7); - } - - cd = (struct cdata*) lua_newuserdata(L, sizeof(struct cdata) + sz); - *(struct ctype*) &cd->type = *ct; - memset(cd+1, 0, sz); - - /* TODO: handle cases where lua_newuserdata returns a pointer that is not - * aligned */ -#if 0 - assert((uintptr_t) (cd + 1) % 8 == 0); -#endif - -#if LUA_VERSION_NUM == 501 - if (!ct_usr || lua_isnil(L, ct_usr)) { - push_upval(L, &niluv_key); - lua_setfenv(L, -2); - } -#endif - - if (ct_usr && !lua_isnil(L, ct_usr)) { - lua_pushvalue(L, ct_usr); - lua_setuservalue(L, -2); - } - - push_upval(L, &cdata_mt_key); - lua_setmetatable(L, -2); - - if (!ct->is_defined && ct_usr && !lua_isnil(L, ct_usr)) { - update_on_definition(L, ct_usr, -1); - } - - return cd+1; -} - -/* returns the value as a ctype, pushes the user value onto the stack */ -void check_ctype(lua_State* L, int idx, struct ctype* ct) -{ - if (lua_isstring(L, idx)) { - struct parser P; - P.line = 1; - P.prev = P.next = lua_tostring(L, idx); - P.align_mask = DEFAULT_ALIGN_MASK; - parse_type(L, &P, ct); - parse_argument(L, &P, -1, ct, NULL, NULL); - lua_remove(L, -2); /* remove the user value from parse_type */ - - } else if (lua_getmetatable(L, idx)) { - if (!equals_upval(L, -1, &ctype_mt_key) - && !equals_upval(L, -1, &cdata_mt_key)) { - goto err; - } - - lua_pop(L, 1); /* pop the metatable */ - *ct = *(struct ctype*) lua_touserdata(L, idx); - lua_getuservalue(L, idx); - - } else { - goto err; - } - - return; - -err: - luaL_error(L, "expected cdata, ctype or string for arg #%d", idx); -} - -/* to_cdata returns the struct cdata* and pushes the user value onto the - * stack. If the index is not a ctype then ct is set to the zero value such - * that ct->type is INVALID_TYPE, a nil is pushed, and NULL is returned. */ -void* to_cdata(lua_State* L, int idx, struct ctype* ct) -{ - struct cdata* cd; - - memset(ct, 0, sizeof(struct ctype)); - if (!lua_isuserdata(L, idx) || !lua_getmetatable(L, idx)) { - lua_pushnil(L); - return NULL; - } - - if (!equals_upval(L, -1, &cdata_mt_key)) { - lua_pop(L, 1); /* mt */ - lua_pushnil(L); - return NULL; - } - - lua_pop(L, 1); /* mt */ - cd = (struct cdata*) lua_touserdata(L, idx); - *ct = cd->type; - lua_getuservalue(L, idx); - - if (ct->is_reference) { - return *(void**) (cd+1); - - } else if (ct->pointers && !ct->is_array) { - return *(void**) (cd+1); - - } else { - return cd + 1; - } -} - -/* check_cdata returns the struct cdata* and pushes the user value onto the - * stack. Also dereferences references. */ -void* check_cdata(lua_State* L, int idx, struct ctype* ct) -{ - void* p = to_cdata(L, idx, ct); - if (ct->type == INVALID_TYPE) { - luaL_error(L, "expected cdata for arg #%d", idx); - } - return p; -} diff --git a/luaclib/src/lffi/ffi.c b/luaclib/src/lffi/ffi.c deleted file mode 100644 index 4400d3bf..00000000 --- a/luaclib/src/lffi/ffi.c +++ /dev/null @@ -1,2933 +0,0 @@ -/* vim: ts=4 sw=4 sts=4 et tw=78 - * Portions copyright (c) 2015-present, Facebook, Inc. All rights reserved. - * Portions copyright (c) 2011 James R. McKaskill. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ -#include "ffi.h" -#include -#include - -/* Set to 1 to get extra debugging on print */ -#define DEBUG_TOSTRING 0 - -int ctype_mt_key; -int cdata_mt_key; -int constants_key; -int types_key; -int gc_key; -int callbacks_key; -int functions_key; -int abi_key; -int next_unnamed_key; -int niluv_key; -int asmname_key; - -void push_upval(lua_State* L, int* key) -{ - lua_pushlightuserdata(L, key); - lua_rawget(L, LUA_REGISTRYINDEX); -} - -void set_upval(lua_State* L, int* key) -{ - lua_pushlightuserdata(L, key); - lua_insert(L, -2); - lua_rawset(L, LUA_REGISTRYINDEX); -} - -int equals_upval(lua_State* L, int idx, int* key) -{ - int ret; - lua_pushvalue(L, idx); - push_upval(L, key); - ret = lua_rawequal(L, -2, -1); - lua_pop(L, 2); - return ret; -} - -static int type_error(lua_State* L, int idx, const char* to_type, int to_usr, const struct ctype* to_ct) -{ - luaL_Buffer B; - struct ctype ft; - - assert(to_type || (to_usr && to_ct)); - if (to_usr) { - to_usr = lua_absindex(L, to_usr); - } - - idx = lua_absindex(L, idx); - - luaL_buffinit(L, &B); - to_cdata(L, idx, &ft); - - if (ft.type != INVALID_TYPE) { - push_type_name(L, -1, &ft); - lua_pushfstring(L, "unable to convert argument %d from cdata<%s> to cdata<", idx, lua_tostring(L, -1)); - lua_remove(L, -2); - luaL_addvalue(&B); - } else { - lua_pushfstring(L, "unable to convert argument %d from lua<%s> to cdata<", idx, luaL_typename(L, idx)); - luaL_addvalue(&B); - } - - if (to_ct) { - push_type_name(L, to_usr, to_ct); - luaL_addvalue(&B); - } else { - luaL_addstring(&B, to_type); - } - - luaL_addchar(&B, '>'); - - luaL_pushresult(&B); - return lua_error(L); -} - -static void* userdata_toptr(lua_State* L, int idx) -{ - void* ptr = lua_touserdata(L, idx); - - // check for FILE* - lua_getmetatable(L, idx); - luaL_getmetatable(L, LUA_FILEHANDLE); - int isfile = lua_rawequal(L, -1, -2); - lua_pop(L, 2); - - if (isfile) { -#if LUA_VERSION_NUM == 501 - FILE** stream = (FILE**) ptr; - return *stream; -#else - luaL_Stream* stream = (luaL_Stream*) ptr; - return stream->f; -#endif - } - - return ptr; -} - -static int cdata_tointeger(lua_State* L, int idx, ptrdiff_t* val) -{ - struct ctype ct; - void* addr = to_cdata(L, idx, &ct); - lua_pop(L, 1); - - if (ct.pointers) { - return 0; - } - - switch (ct.type) { - case INT8_TYPE: - *val = *(int8_t*)addr; - return 1; - case INT16_TYPE: - *val = *(int16_t*)addr; - return 1; - case INT32_TYPE: - *val = *(int32_t*)addr; - return 1; - case INT64_TYPE: - *val = *(int64_t*)addr; - return 1; - default: - return 0; - } -} - -static int64_t check_intptr(lua_State* L, int idx, void* p, struct ctype* ct) -{ - if (ct->type == INVALID_TYPE) { - int64_t ret; - memset(ct, 0, sizeof(*ct)); - ct->base_size = 8; - ct->type = INT64_TYPE; - ct->is_defined = 1; - ret = luaL_checknumber(L, idx); - return ret; - - } else if (ct->pointers) { - return (intptr_t) p; - } - - switch (ct->type) { - case INTPTR_TYPE: - case FUNCTION_PTR_TYPE: - return *(intptr_t*) p; - - case INT64_TYPE: - return *(int64_t*) p; - - case INT32_TYPE: - return ct->is_unsigned ? (int64_t) *(uint32_t*) p : (int64_t) *(int32_t*) p; - - case INT16_TYPE: - return ct->is_unsigned ? (int64_t) *(uint16_t*) p : (int64_t) *(int16_t*) p; - - case INT8_TYPE: - return ct->is_unsigned ? (int64_t) *(uint8_t*) p : (int64_t) *(int8_t*) p; - - default: - type_error(L, idx, "intptr_t", 0, NULL); - return 0; - } -} - -#define TO_NUMBER(TYPE, ALLOW_POINTERS, LUA_TONUMBER) \ - TYPE ret = 0; \ - void* p; \ - struct ctype ct; \ - \ - switch (lua_type(L, idx)) { \ - case LUA_TBOOLEAN: \ - ret = (TYPE) lua_toboolean(L, idx); \ - break; \ - \ - case LUA_TNUMBER: \ - ret = (TYPE) LUA_TONUMBER(L, idx); \ - break; \ - \ - case LUA_TSTRING: \ - if (!ALLOW_POINTERS) { \ - type_error(L, idx, #TYPE, 0, NULL); \ - } \ - ret = (TYPE) (intptr_t) lua_tostring(L, idx); \ - break; \ - \ - case LUA_TLIGHTUSERDATA: \ - if (!ALLOW_POINTERS) { \ - type_error(L, idx, #TYPE, 0, NULL); \ - } \ - ret = (TYPE) (intptr_t) lua_topointer(L, idx); \ - break; \ - \ - case LUA_TUSERDATA: \ - p = to_cdata(L, idx, &ct); \ - \ - if (ct.type == INVALID_TYPE) { \ - if (!ALLOW_POINTERS) { \ - type_error(L, idx, #TYPE, 0, NULL); \ - } \ - ret = (TYPE) (intptr_t) userdata_toptr(L, idx); \ - } else if (ct.pointers || ct.type == STRUCT_TYPE || ct.type == UNION_TYPE) {\ - if (!ALLOW_POINTERS) { \ - type_error(L, idx, #TYPE, 0, NULL); \ - } \ - ret = (TYPE) (intptr_t) p; \ - } else if (ct.type == COMPLEX_DOUBLE_TYPE) { \ - ret = (TYPE) creal(*(complex_double*) p); \ - } else if (ct.type == COMPLEX_FLOAT_TYPE) { \ - ret = (TYPE) crealf(*(complex_float*) p); \ - } else if (ct.type == DOUBLE_TYPE) { \ - ret = (TYPE) *(double*) p; \ - } else if (ct.type == FLOAT_TYPE) { \ - ret = (TYPE) *(float*) p; \ - } else { \ - ret = check_intptr(L, idx, p, &ct); \ - } \ - lua_pop(L, 1); \ - break; \ - \ - case LUA_TNIL: \ - ret = (TYPE) 0; \ - break; \ - \ - default: \ - type_error(L, idx, #TYPE, 0, NULL); \ - } \ - -static int64_t cast_int64(lua_State* L, int idx, int is_cast) -{ TO_NUMBER(int64_t, is_cast, lua_tointeger); return ret; } - -static uint64_t cast_uint64(lua_State* L, int idx, int is_cast) -{ TO_NUMBER(uint64_t, is_cast, lua_tointeger); return ret; } - -int32_t check_int32(lua_State* L, int idx) -{ return (int32_t) cast_int64(L, idx, 0); } - -uint32_t check_uint32(lua_State* L, int idx) -{ return (uint32_t) cast_uint64(L, idx, 0); } - -int64_t check_int64(lua_State* L, int idx) -{ return cast_int64(L, idx, 0); } - -uint64_t check_uint64(lua_State* L, int idx) -{ return cast_uint64(L, idx, 0); } - -double check_double(lua_State* L, int idx) -{ TO_NUMBER(double, 0, lua_tonumber); return ret; } - -float check_float(lua_State* L, int idx) -{ TO_NUMBER(double, 0, lua_tonumber); return ret; } - -uintptr_t check_uintptr(lua_State* L, int idx) -{ TO_NUMBER(uintptr_t, 1, lua_tointeger); return ret; } - -complex_double check_complex_double(lua_State* L, int idx) -{ - double real = 0, imag = 0; - void* p; - struct ctype ct; - - switch (lua_type(L, idx)) { - case LUA_TNUMBER: - real = (double) lua_tonumber(L, idx); - break; - case LUA_TTABLE: - lua_rawgeti(L, idx, 1); - real = check_double(L, -1); - lua_pop(L, 1); - - lua_rawgeti(L, idx, 2); - if (lua_isnil(L, -1)) { - imag = real; - } else { - imag = check_double(L, -1); - } - lua_pop(L, 1); - break; - case LUA_TUSERDATA: - p = to_cdata(L, idx, &ct); - if (ct.type == COMPLEX_DOUBLE_TYPE) { - real = creal(*(complex_double*) p); - imag = cimag(*(complex_double*) p); - } else if (ct.type == COMPLEX_FLOAT_TYPE) { - real = crealf(*(complex_float*) p); - imag = cimagf(*(complex_float*) p); - } else if (ct.type == DOUBLE_TYPE) { - real = *(double*) p; - } else if (ct.type == FLOAT_TYPE) { - real = *(float*) p; - } else { - real = check_intptr(L, idx, p, &ct); - } - lua_pop(L, 1); - break; - - default: - type_error(L, idx, "complex", 0, NULL); - } - - return mk_complex_double(real, imag); -} - -complex_float check_complex_float(lua_State* L, int idx) -{ - complex_double d = check_complex_double(L, idx); - return mk_complex_float(creal(d), cimag(d)); -} - -static size_t unpack_vararg(lua_State* L, int i, char* to) -{ - void* p; - struct ctype ct; - - switch (lua_type(L, i)) { - case LUA_TBOOLEAN: - *(int*) to = lua_toboolean(L, i); - return sizeof(int); - - case LUA_TNUMBER: - *(double*) to = lua_tonumber(L, i); // TODO in Lua 5.3: lua_tointeger sometimes should be here - return sizeof(double); - - case LUA_TSTRING: - *(const char**) to = lua_tostring(L, i); - return sizeof(const char*); - - case LUA_TLIGHTUSERDATA: - *(void**) to = lua_touserdata(L, i); - return sizeof(void*); - - case LUA_TUSERDATA: - p = to_cdata(L, i, &ct); - lua_pop(L, 1); - - if (ct.type == INVALID_TYPE) { - *(void**) to = userdata_toptr(L, i); - return sizeof(void*); - - } else if (ct.pointers || ct.type == INTPTR_TYPE) { - *(void**) to = p; - return sizeof(void*); - - } else if (ct.type == INT32_TYPE) { - *(int32_t*) to = *(int32_t*) p; - return sizeof(int32_t); - - } else if (ct.type == INT64_TYPE) { - *(int64_t*) to = *(int64_t*) p; - return sizeof(int64_t); - } - goto err; - - case LUA_TNIL: - *(void**) to = NULL; - return sizeof(void*); - - default: - goto err; - } - -err: - return type_error(L, i, "vararg", 0, NULL); -} - -void unpack_varargs_stack(lua_State* L, int first, int last, char* to) -{ - int i; - - for (i = first; i <= last; i++) { - to += unpack_vararg(L, i, to); - } -} - -void unpack_varargs_stack_skip(lua_State* L, int first, int last, int ints_to_skip, int floats_to_skip, char* to) -{ - int i; - - for (i = first; i <= last; i++) { - int type = lua_type(L, i); - - if (type == LUA_TNUMBER && --floats_to_skip >= 0) { - continue; - } else if (type != LUA_TNUMBER && --ints_to_skip >= 0) { - continue; - } - - to += unpack_vararg(L, i, to); - } -} - -void unpack_varargs_float(lua_State* L, int first, int last, int max, char* to) -{ - int i; - - for (i = first; i <= last && max > 0; i++) { - if (lua_type(L, i) == LUA_TNUMBER) { - unpack_vararg(L, i, to); - to += sizeof(double); - max--; - } - } -} - -void unpack_varargs_int(lua_State* L, int first, int last, int max, char* to) -{ - int i; - - for (i = first; i <= last && max > 0; i++) { - if (lua_type(L, i) != LUA_TNUMBER) { - unpack_vararg(L, i, to); - to += sizeof(void*); - max--; - } - } -} - -void unpack_varargs_reg(lua_State* L, int first, int last, char* to) -{ - int i; - - for (i = first; i <= last; i++) { - unpack_vararg(L, i, to); - to += sizeof(double); - } -} - -/* to_enum tries to convert a value at idx to the enum type indicated by to_ct - * and uv to_usr. For strings this means it will do a string lookup for the - * enum type. It leaves the stack unchanged. Will throw an error if the type - * at idx can't be conerted. - */ -int32_t check_enum(lua_State* L, int idx, int to_usr, const struct ctype* to_ct) -{ - int32_t ret; - - switch (lua_type(L, idx)) { - case LUA_TSTRING: - /* lookup string in to_usr to find value */ - to_usr = lua_absindex(L, to_usr); - lua_pushvalue(L, idx); - lua_rawget(L, to_usr); - - if (lua_isnil(L, -1)) { - goto err; - } - - ret = (int32_t) lua_tointeger(L, -1); - lua_pop(L, 1); - return ret; - - case LUA_TUSERDATA: - return check_int32(L, idx); - - case LUA_TNIL: - return (int32_t) 0; - - case LUA_TNUMBER: - return (int32_t) lua_tointeger(L, idx); - - default: - goto err; - } - -err: - return type_error(L, idx, NULL, to_usr, to_ct); -} - -/* to_pointer tries converts a value at idx to a pointer. It fills out ct and - * pushes the uv of the found type. It will throw a lua error if it can not - * convert the value to a pointer. */ -static void* check_pointer(lua_State* L, int idx, struct ctype* ct) -{ - void* p; - memset(ct, 0, sizeof(*ct)); - idx = lua_absindex(L, idx); - - switch (lua_type(L, idx)) { - case LUA_TNIL: - ct->type = VOID_TYPE; - ct->pointers = 1; - ct->is_null = 1; - lua_pushnil(L); - return NULL; - - case LUA_TNUMBER: - ct->type = INTPTR_TYPE; - ct->is_unsigned = 1; - ct->pointers = 0; - lua_pushnil(L); - return (void*) (uintptr_t) lua_tonumber(L, idx); // TODO in Lua 5.3: maybe change to lua_tointeger - - case LUA_TLIGHTUSERDATA: - ct->type = VOID_TYPE; - ct->pointers = 1; - lua_pushnil(L); - return lua_touserdata(L, idx); - - case LUA_TSTRING: - ct->type = INT8_TYPE; - ct->pointers = 1; - ct->is_unsigned = IS_CHAR_UNSIGNED; - ct->is_array = 1; - ct->base_size = 1; - ct->const_mask = 2; - lua_pushnil(L); - return (void*) lua_tolstring(L, idx, &ct->array_size); - - case LUA_TUSERDATA: - p = to_cdata(L, idx, ct); - - if (ct->type == INVALID_TYPE) { - /* some other type of user data */ - ct->type = VOID_TYPE; - ct->pointers = 1; - return userdata_toptr(L, idx); - } else if (ct->type == STRUCT_TYPE || ct->type == UNION_TYPE) { - return p; - } else { - return (void*) (intptr_t) check_intptr(L, idx, p, ct); - } - break; - } - - type_error(L, idx, "pointer", 0, NULL); - return NULL; -} - -static int is_void_ptr(const struct ctype* ct) -{ - return ct->type == VOID_TYPE - && ct->pointers == 1; -} - -static int is_same_type(lua_State* L, int usr1, int usr2, const struct ctype* t1, const struct ctype* t2) -{ - if (t1->type != t2->type) { - return 0; - } - -#if LUA_VERSION_NUM == 501 - if (lua_isnil(L, usr1) != lua_isnil(L, usr2)) { - int ret; - usr1 = lua_absindex(L, usr1); - usr2 = lua_absindex(L, usr2); - push_upval(L, &niluv_key); - - ret = lua_rawequal(L, usr1, -1) - || lua_rawequal(L, usr2, -1); - - lua_pop(L, 1); - - if (ret) { - return 1; - } - } -#endif - - return lua_rawequal(L, usr1, usr2); -} - -static void set_struct(lua_State* L, int idx, void* to, int to_usr, const struct ctype* tt, int check_pointers); - -/* to_typed_pointer converts a value at idx to a type tt with target uv to_usr - * checking all types. May push a temporary value so that it can create - * structs on the fly. */ -void* check_typed_pointer(lua_State* L, int idx, int to_usr, const struct ctype* tt) -{ - struct ctype ft; - void* p; - - to_usr = lua_absindex(L, to_usr); - idx = lua_absindex(L, idx); - - if (tt->pointers == 1 && (tt->type == STRUCT_TYPE || tt->type == UNION_TYPE) && lua_type(L, idx) == LUA_TTABLE) { - /* need to construct a struct of the target type */ - struct ctype ct = *tt; - ct.pointers = ct.is_array = 0; - p = push_cdata(L, to_usr, &ct); - set_struct(L, idx, p, to_usr, &ct, 1); - return p; - } - - p = check_pointer(L, idx, &ft); - - if (tt->pointers == 1 && ft.pointers == 0 && (ft.type == STRUCT_TYPE || ft.type == UNION_TYPE)) { - /* auto dereference structs */ - ft.pointers = 1; - ft.const_mask <<= 1; - } - - if (is_void_ptr(tt)) { - /* any pointer can convert to void* */ - goto suc; - - } else if (is_void_ptr(&ft) && (ft.pointers || ft.is_reference)) { - /* void* can convert to any pointer */ - goto suc; - - } else if (ft.is_null) { - /* NULL can convert to any pointer */ - goto suc; - - } else if (!is_same_type(L, to_usr, -1, tt, &ft)) { - /* the base type is different */ - goto err; - - } else if (tt->pointers != ft.pointers) { - goto err; - - } else if (ft.const_mask & ~tt->const_mask) { - /* for every const in from it must be in to, there are further rules - * for const casting (see the c++ spec), but they are hard to test - * quickly */ - goto err; - } - -suc: - return p; - -err: - type_error(L, idx, NULL, to_usr, tt); - return NULL; -} - -static void set_value(lua_State* L, int idx, void* to, int to_usr, const struct ctype* tt, int check_pointers); - -static void set_array(lua_State* L, int idx, void* to, int to_usr, const struct ctype* tt, int check_pointers) -{ - size_t i, sz, esz; - struct ctype et; - - idx = lua_absindex(L, idx); - to_usr = lua_absindex(L, to_usr); - - switch (lua_type(L, idx)) { - case LUA_TSTRING: - if (tt->pointers == 1 && tt->type == INT8_TYPE) { - const char* str = lua_tolstring(L, idx, &sz); - - if (!tt->is_variable_array && sz >= tt->array_size) { - memcpy(to, str, tt->array_size); - } else { - /* include nul terminator */ - memcpy(to, str, sz+1); - } - } else { - goto err; - } - break; - - case LUA_TTABLE: - et = *tt; - et.pointers--; - et.const_mask >>= 1; - et.is_array = 0; - esz = et.pointers ? sizeof(void*) : et.base_size; - - lua_rawgeti(L, idx, 2); - - if (tt->is_variable_array) { - /* we have no idea how big the array is, so set values based off - * how many items were given to us */ - lua_pop(L, 1); - for (i = 0; i < lua_rawlen(L, idx); i++) { - lua_rawgeti(L, idx, (int) i + 1); - set_value(L, -1, (char*) to + esz * i, to_usr, &et, check_pointers); - lua_pop(L, 1); - } - - } else if (lua_isnil(L, -1)) { - /* there is no second element, so we set the whole array to the - * first element (or nil - ie 0) if there is no first element) */ - lua_pop(L, 1); - lua_rawgeti(L, idx, 1); - - if (lua_isnil(L, -1)) { - memset(to, 0, ctype_size(L, tt)); - } else { - /* if its still variable we have no idea how many values to set */ - for (i = 0; i < tt->array_size; i++) { - set_value(L, -1, (char*) to + esz * i, to_usr, &et, check_pointers); - } - } - - lua_pop(L, 1); - - } else { - /* there is a second element, so we set each element using the - * equiv index in the table initializer */ - lua_pop(L, 1); - for (i = 0; i < tt->array_size; i++) { - lua_rawgeti(L, idx, (int) (i+1)); - - if (lua_isnil(L, -1)) { - /* we've hit the end of the values provided in the - * initializer, so memset the rest to zero */ - lua_pop(L, 1); - memset((char*) to + esz * i, 0, (tt->array_size - i) * esz); - break; - - } else { - set_value(L, -1, (char*) to + esz * i, to_usr, &et, check_pointers); - lua_pop(L, 1); - } - } - } - break; - - default: - goto err; - } - - return; - -err: - type_error(L, idx, NULL, to_usr, tt); -} - -/* pops the member key from the stack, leaves the member user value on the - * stack. Returns the member offset. Returns -ve if the member can not be - * found. */ -static ptrdiff_t get_member(lua_State* L, int usr, const struct ctype* ct, struct ctype* mt) -{ - ptrdiff_t off; - lua_rawget(L, usr); - - if (lua_isnil(L, -1)) { - lua_pop(L, 1); - return -1; - } - - *mt = *(const struct ctype*) lua_touserdata(L, -1); - lua_getuservalue(L, -1); - lua_replace(L, -2); - - if (mt->is_variable_array && ct->variable_size_known) { - /* eg char mbr[?] */ - size_t sz = (mt->pointers > 1) ? sizeof(void*) : mt->base_size; - assert(ct->is_variable_struct && mt->is_array); - mt->array_size = ct->variable_increment / sz; - mt->is_variable_array = 0; - - } else if (mt->is_variable_struct && ct->variable_size_known) { - /* eg struct {char a; char b[?]} mbr; */ - assert(ct->is_variable_struct); - mt->variable_size_known = 1; - mt->variable_increment = ct->variable_increment; - } - - off = mt->offset; - mt->offset = 0; - return off; -} - -static void set_struct(lua_State* L, int idx, void* to, int to_usr, const struct ctype* tt, int check_pointers) -{ - int have_first = 0; - int have_other = 0; - struct ctype mt; - void* p; - - to_usr = lua_absindex(L, to_usr); - idx = lua_absindex(L, idx); - - switch (lua_type(L, idx)) { - case LUA_TTABLE: - /* match up to the members based off the table initializers key - this - * will match both numbered and named members in the user table - * we need a special case for when no entries in the initializer - - * zero initialize the c struct, and only one entry in the initializer - * - set all members to this value */ - memset(to, 0, ctype_size(L, tt)); - lua_pushnil(L); - while (lua_next(L, idx)) { - ptrdiff_t off; - - if (!have_first && lua_tonumber(L, -2) == 1 && lua_tonumber(L, -1) != 0) { - have_first = 1; - } else if (!have_other && (lua_type(L, -2) != LUA_TNUMBER || lua_tonumber(L, -2) != 1)) { - have_other = 1; - } - - lua_pushvalue(L, -2); - off = get_member(L, to_usr, tt, &mt); - assert(off >= 0); - set_value(L, -2, (char*) to + off, -1, &mt, check_pointers); - - /* initializer value, mt usr */ - lua_pop(L, 2); - } - - /* if we only had a single non zero value then initialize all members to that value */ - if (!have_other && have_first && tt->type != UNION_TYPE) { - size_t i, sz; - ptrdiff_t off; - lua_rawgeti(L, idx, 1); - sz = lua_rawlen(L, to_usr); - - for (i = 2; i < sz; i++) { - lua_pushinteger(L, i); - off = get_member(L, to_usr, tt, &mt); - assert(off >= 0); - set_value(L, -2, (char*) to + off, -1, &mt, check_pointers); - lua_pop(L, 1); /* mt usr */ - } - - lua_pop(L, 1); /* initializer table */ - } - break; - - case LUA_TUSERDATA: - if (check_pointers) { - p = check_typed_pointer(L, idx, to_usr, tt); - } else { - struct ctype ct; - p = check_pointer(L, idx, &ct); - } - memcpy(to, p, tt->base_size); - lua_pop(L, 1); - break; - - default: - goto err; - } - - return; - -err: - type_error(L, idx, NULL, to_usr, tt); -} - -static void set_value(lua_State* L, int idx, void* to, int to_usr, const struct ctype* tt, int check_pointers) -{ - int top = lua_gettop(L); - - if (tt->is_array) { - set_array(L, idx, to, to_usr, tt, check_pointers); - - } else if (tt->pointers || tt->is_reference) { - union { - uint8_t c[sizeof(void*)]; - void* p; - } u; - - if (lua_istable(L, idx)) { - luaL_error(L, "Can't set a pointer member to a struct that's about to be freed"); - } - - if (check_pointers) { - u.p = check_typed_pointer(L, idx, to_usr, tt); - } else { - struct ctype ct; - u.p = check_pointer(L, idx, &ct); - } - -#ifndef ALLOW_MISALIGNED_ACCESS - if ((uintptr_t) to & PTR_ALIGN_MASK) { - memcpy(to, u.c, sizeof(void*)); - } else -#endif - { - *(void**) to = u.p; - } - - lua_pop(L, 1); - - } else if (tt->is_bitfield) { - - uint64_t hi_mask = UINT64_C(0) - (UINT64_C(1) << (tt->bit_offset + tt->bit_size)); - uint64_t low_mask = (UINT64_C(1) << tt->bit_offset) - UINT64_C(1); - uint64_t val = check_uint64(L, idx); - val &= (UINT64_C(1) << tt->bit_size) - 1; - val <<= tt->bit_offset; - *(uint64_t*) to = val | (*(uint64_t*) to & (hi_mask | low_mask)); - - } else if (tt->type == STRUCT_TYPE || tt->type == UNION_TYPE) { - set_struct(L, idx, to, to_usr, tt, check_pointers); - - } else { - -#ifndef ALLOW_MISALIGNED_ACCESS - union { - uint8_t c[8]; - _Bool b; - uint64_t u64; - float f; - double d; - } misalign; - - void* origto = to; - - if ((uintptr_t) origto & (tt->base_size - 1)) { - to = misalign.c; - } -#endif - - switch (tt->type) { - case BOOL_TYPE: - *(_Bool*) to = (cast_int64(L, idx, !check_pointers) != 0); - break; - case INT8_TYPE: - if (tt->is_unsigned) { - *(uint8_t*) to = (uint8_t) cast_uint64(L, idx, !check_pointers); - } else { - *(int8_t*) to = (int8_t) cast_int64(L, idx, !check_pointers); - } - break; - case INT16_TYPE: - if (tt->is_unsigned) { - *(uint16_t*) to = (uint16_t) cast_uint64(L, idx, !check_pointers); - } else { - *(int16_t*) to = (int16_t) cast_int64(L, idx, !check_pointers); - } - break; - case INT32_TYPE: - if (tt->is_unsigned) { - *(uint32_t*) to = (uint32_t) cast_uint64(L, idx, !check_pointers); - } else { - *(int32_t*) to = (int32_t) cast_int64(L, idx, !check_pointers); - } - break; - case INT64_TYPE: - if (tt->is_unsigned) { - *(uint64_t*) to = cast_uint64(L, idx, !check_pointers); - } else { - *(int64_t*) to = cast_int64(L, idx, !check_pointers); - } - break; - case FLOAT_TYPE: - *(float*) to = (float) check_double(L, idx); - break; - case DOUBLE_TYPE: - *(double*) to = check_double(L, idx); - break; - case COMPLEX_FLOAT_TYPE: - *(complex_float*) to = check_complex_float(L, idx); - break; - case COMPLEX_DOUBLE_TYPE: - *(complex_double*) to = check_complex_double(L, idx); - break; - case INTPTR_TYPE: - *(uintptr_t*) to = check_uintptr(L, idx); - break; - case ENUM_TYPE: - *(int32_t*) to = check_enum(L, idx, to_usr, tt); - break; - default: - goto err; - } - -#ifndef ALLOW_MISALIGNED_ACCESS - if ((uintptr_t) origto & (tt->base_size - 1)) { - memcpy(origto, misalign.c, tt->base_size); - } -#endif - } - - assert(lua_gettop(L) == top); - return; -err: - type_error(L, idx, NULL, to_usr, tt); -} - -static int ffi_typeof(lua_State* L) -{ - struct ctype ct; - check_ctype(L, 1, &ct); - push_ctype(L, -1, &ct); - return 1; -} - -static void setmintop(lua_State* L, int idx) -{ - if (lua_gettop(L) < idx) { - lua_settop(L, idx); - } -} - -/* warning: in the case that it finds an array size, it removes that index */ -static void get_variable_array_size(lua_State* L, int idx, struct ctype* ct) -{ - /* we only care about the variable buisness for the variable array - * directly ie ffi.new('char[?]') or the struct that contains the variable - * array ffi.new('struct {char v[?]}'). A pointer to the struct doesn't - * care about the variable size (it treats it as a zero sized array). */ - - if (ct->is_variable_array) { - assert(ct->is_array); - ct->array_size = (size_t) luaL_checknumber(L, idx); - ct->is_variable_array = 0; - lua_remove(L, idx); - - } else if (ct->is_variable_struct && !ct->variable_size_known) { - assert(ct->type == STRUCT_TYPE && !ct->is_array); - ct->variable_increment *= (size_t) luaL_checknumber(L, idx); - ct->variable_size_known = 1; - lua_remove(L, idx); - } -} - -static int is_scalar(struct ctype* ct) -{ - int type = ct->type; - if (ct->pointers || ct->is_reference) { - return !ct->is_array; - } - return type != STRUCT_TYPE && type != UNION_TYPE && !IS_COMPLEX(type); -} - -static int should_pack(lua_State *L, int ct_usr, struct ctype* ct, int idx) -{ - struct ctype argt; - ct_usr = lua_absindex(L, ct_usr); - - if (IS_COMPLEX(ct->type)) { - return 0; - } - - switch (lua_type(L, idx)) { - case LUA_TTABLE: - return 0; - case LUA_TSTRING: - return ct->type == STRUCT_TYPE; - case LUA_TUSERDATA: - // don't pack if the argument is a cdata with the same type - to_cdata(L, idx, &argt); - int same = is_same_type(L, ct_usr, -1, ct, &argt); - lua_pop(L, 1); - return !same; - default: - return 1; - } -} - -static int do_new(lua_State* L, int is_cast) -{ - int cargs, i; - void* p; - struct ctype ct; - int check_ptrs = !is_cast; - - check_ctype(L, 1, &ct); - - /* this removes the vararg argument if its needed, and errors if its invalid */ - if (!is_cast) { - get_variable_array_size(L, 2, &ct); - } - - p = push_cdata(L, -1, &ct); - - /* if the user mt has a __gc function then call ffi.gc on this value */ - if (push_user_mt(L, -2, &ct)) { - push_upval(L, &gc_key); - lua_pushvalue(L, -3); - - /* user_mt.__gc */ - lua_pushliteral(L, "__gc"); - lua_rawget(L, -4); - - lua_rawset(L, -3); /* gc_upval[cdata] = user_mt.__gc */ - lua_pop(L, 2); /* user_mt and gc_upval */ - } - - /* stack is: - * ctype arg - * ctor args ... 0+ - * ctype usr - * cdata - */ - - cargs = lua_gettop(L) - 3; - - if (cargs == 0) { - return 1; - } - - int scalar = is_scalar(&ct); - if (scalar && cargs > 1) { - return luaL_error(L, "too many initializers"); - } - - if (cargs > 1 || (!scalar && should_pack(L, -2, &ct, 2))) { - lua_createtable(L, cargs, 0); - lua_replace(L, 1); - for (i = 1; i <= cargs; i++) { - lua_pushvalue(L, i + 1); - lua_rawseti(L, 1, i); - } - assert(lua_gettop(L) == cargs + 3); - set_value(L, 1, p, -2, &ct, check_ptrs); - return 1; - } - - set_value(L, 2, p, -2, &ct, check_ptrs); - return 1; -} - -static int ffi_new(lua_State* L) -{ return do_new(L, 0); } - -static int ffi_cast(lua_State* L) -{ return do_new(L, 1); } - -static int ctype_new(lua_State* L) -{ return do_new(L, 0); } - -static int ctype_call(lua_State* L) -{ - struct ctype ct; - int top = lua_gettop(L); - - check_ctype(L, 1, &ct); - - if (push_user_mt(L, -1, &ct)) { - lua_pushstring(L, "__new"); - lua_rawget(L, -2); - if (!lua_isnil(L, -1)) { - lua_insert(L, 1); // function at bottom of stack under args - lua_pop(L, 2); - lua_call(L, top, 1); - return 1; - } - lua_pop(L, 2); - } - lua_pop(L, 1); - - assert(lua_gettop(L) == top); - return do_new(L, 0); -} - -static int ffi_sizeof(lua_State* L) -{ - struct ctype ct; - check_ctype(L, 1, &ct); - get_variable_array_size(L, 2, &ct); - lua_pushinteger(L, ctype_size(L, &ct)); - return 1; -} - -static int ffi_alignof(lua_State* L) -{ - struct ctype ct, mt; - lua_settop(L, 2); - check_ctype(L, 1, &ct); - - /* if no member is specified then we return the alignment of the type */ - if (lua_isnil(L, 2)) { - lua_pushinteger(L, ct.align_mask + 1); - return 1; - } - - /* get the alignment of the member */ - lua_pushvalue(L, 2); - if (get_member(L, -2, &ct, &mt) < 0) { - push_type_name(L, 3, &ct); - return luaL_error(L, "type %s has no member %s", lua_tostring(L, -1), lua_tostring(L, 2)); - } - - lua_pushinteger(L, mt.align_mask + 1); - return 1; -} - -static int ffi_offsetof(lua_State* L) -{ - ptrdiff_t off; - struct ctype ct, mt; - lua_settop(L, 2); - check_ctype(L, 1, &ct); - - lua_pushvalue(L, 2); - off = get_member(L, -2, &ct, &mt); /* this replaces the member key at -1 with the mbr usr value */ - if (off < 0) { - push_type_name(L, 3, &ct); - return luaL_error(L, "type %s has no member %s", lua_tostring(L, -1), lua_tostring(L, 2)); - } - - lua_pushinteger(L, off); - - if (!mt.is_bitfield) { - return 1; - } - - lua_pushinteger(L, mt.bit_offset); - lua_pushinteger(L, mt.bit_size); - return 3; -} - -static int ffi_istype(lua_State* L) -{ - struct ctype tt, ft; - check_ctype(L, 1, &tt); - to_cdata(L, 2, &ft); - - if (ft.type == INVALID_TYPE) { - goto fail; - } - - if (!is_same_type(L, 3, 4, &tt, &ft)) { - goto fail; - } - - if (tt.pointers != ft.pointers) { - goto fail; - } - - if (tt.is_array != ft.is_array) { - goto fail; - } - - if (tt.is_array && tt.array_size != ft.array_size) { - goto fail; - } - - if (tt.calling_convention != ft.calling_convention) { - goto fail; - } - - lua_pushboolean(L, 1); - return 1; - -fail: - lua_pushboolean(L, 0); - return 1; -} - -static int cdata_gc(lua_State* L) -{ - struct ctype ct; - check_cdata(L, 1, &ct); - lua_settop(L, 1); - - /* call the gc func if there is any registered */ - lua_pushvalue(L, 1); - lua_rawget(L, lua_upvalueindex(2)); - if (!lua_isnil(L, -1)) { - lua_pushvalue(L, 1); - lua_pcall(L, 1, 0, 0); - } - - /* unset the closure */ - lua_pushvalue(L, 1); - lua_pushnil(L); - lua_rawset(L, lua_upvalueindex(1)); - - return 0; -} - -static int user_mt_key; - -static int ffi_metatype(lua_State* L) -{ - struct ctype ct; - lua_settop(L, 2); - - check_ctype(L, 1, &ct); - if (lua_type(L, 2) != LUA_TTABLE && lua_type(L, 2) != LUA_TNIL) { - return luaL_argerror(L, 2, "metatable must be a table or nil"); - } - - lua_pushlightuserdata(L, &user_mt_key); - lua_pushvalue(L, 2); - lua_rawset(L, 3); /* user[user_mt_key] = mt */ - - /* return the passed in ctype */ - push_ctype(L, 3, &ct); - return 1; -} - -/* push_user_mt returns 1 if the type has a user metatable and pushes it onto - * the stack, otherwise it returns 0 and pushes nothing */ -int push_user_mt(lua_State* L, int ct_usr, const struct ctype* ct) -{ - if (ct->type != STRUCT_TYPE && ct->type != UNION_TYPE && !IS_COMPLEX(ct->type)) { - return 0; - } - if (!lua_istable(L, ct_usr)) { - return 0; - } - - ct_usr = lua_absindex(L, ct_usr); - lua_pushlightuserdata(L, &user_mt_key); - lua_rawget(L, ct_usr); - - if (lua_isnil(L, -1)) { - lua_pop(L, 1); - return 0; - } - return 1; -} - -static int ffi_gc(lua_State* L) -{ - struct ctype ct; - lua_settop(L, 2); - check_cdata(L, 1, &ct); - - push_upval(L, &gc_key); - lua_pushvalue(L, 1); - lua_pushvalue(L, 2); - lua_rawset(L, -3); - - /* return the cdata back */ - lua_settop(L, 1); - return 1; -} - -/* lookup_cdata_index returns the offset of the found type and user value on - * the stack if valid. Otherwise returns -ve and doesn't touch the stack. - */ -static ptrdiff_t lookup_cdata_index(lua_State* L, int idx, int ct_usr, struct ctype* ct) -{ - struct ctype mt; - ptrdiff_t off; - - ct_usr = lua_absindex(L, ct_usr); - int type = lua_type(L, idx); - - switch (type) { - case LUA_TNUMBER: - case LUA_TUSERDATA: - /* possibilities are array, pointer */ - - if (!ct->pointers || is_void_ptr(ct)) { - return -1; - } - - // unbox cdata - if (type == LUA_TUSERDATA) { - if (!cdata_tointeger(L, idx, &off)) { - return -1; - } - } else { - off = lua_tointeger(L, idx); - } - - ct->is_array = 0; - ct->pointers--; - ct->const_mask >>= 1; - ct->is_reference = 0; - - lua_pushvalue(L, ct_usr); - - return (ct->pointers ? sizeof(void*) : ct->base_size) * off; - - case LUA_TSTRING: - /* possibilities are struct/union, pointer to struct/union */ - - if ((ct->type != STRUCT_TYPE && ct->type != UNION_TYPE) || ct->is_array || ct->pointers > 1) { - return -1; - } - - lua_pushvalue(L, idx); - off = get_member(L, ct_usr, ct, &mt); - if (off < 0) { - return -1; - } - - *ct = mt; - return off; - - default: - return -1; - } -} - -static int cdata_newindex(lua_State* L) -{ - struct ctype tt; - char* to; - ptrdiff_t off; - - lua_settop(L, 3); - - to = (char*) check_cdata(L, 1, &tt); - off = lookup_cdata_index(L, 2, -1, &tt); - - if (off < 0) { - if (!push_user_mt(L, -1, &tt)) { - goto err; - } - - lua_pushliteral(L, "__newindex"); - lua_rawget(L, -2); - - if (lua_isnil(L, -1)) { - goto err; - } - - lua_insert(L, 1); - lua_settop(L, 4); - lua_call(L, 3, LUA_MULTRET); - return lua_gettop(L); - } - - if (tt.const_mask & 1) { - return luaL_error(L, "can't set const data"); - } - - set_value(L, 3, to + off, -1, &tt, 1); - return 0; - -err: - push_type_name(L, 4, &tt); - return luaL_error(L, "type %s has no member %s", lua_tostring(L, -1), lua_tostring(L, 2)); -} - -static int cdata_index(lua_State* L) -{ - void* to; - struct ctype ct; - char* data; - ptrdiff_t off; - - lua_settop(L, 2); - data = (char*) check_cdata(L, 1, &ct); - assert(lua_gettop(L) == 3); - - if (!ct.pointers) { - switch (ct.type) { - case FUNCTION_PTR_TYPE: - /* Callbacks use the same metatable as standard cdata values, but have set - * and free members. So instead of mt.__index = mt, we do the equiv here. */ - lua_getmetatable(L, 1); - lua_pushvalue(L, 2); - lua_rawget(L, -2); - return 1; - - /* This provides the .re and .im virtual members */ - case COMPLEX_DOUBLE_TYPE: - case COMPLEX_FLOAT_TYPE: - if (!lua_isstring(L, 2)) { - luaL_error(L, "invalid member for complex number"); - - } else if (strcmp(lua_tostring(L, 2), "re") == 0) { - lua_pushnumber(L, ct.type == COMPLEX_DOUBLE_TYPE ? creal(*(complex_double*) data) : crealf(*(complex_float*) data)); - - } else if (strcmp(lua_tostring(L, 2), "im") == 0) { - lua_pushnumber(L, ct.type == COMPLEX_DOUBLE_TYPE ? cimag(*(complex_double*) data) : cimagf(*(complex_float*) data)); - - } else { - luaL_error(L, "invalid member for complex number"); - } - return 1; - } - } - - off = lookup_cdata_index(L, 2, -1, &ct); - - if (off < 0) { - assert(lua_gettop(L) == 3); - if (!push_user_mt(L, -1, &ct)) { - goto err; - } - - lua_pushliteral(L, "__index"); - lua_rawget(L, -2); - - if (lua_isnil(L, -1)) { - goto err; - } - - if (lua_istable(L, -1)) { - lua_pushvalue(L, 2); - lua_gettable(L, -2); - return 1; - } - - lua_insert(L, 1); - lua_settop(L, 3); - lua_call(L, 2, LUA_MULTRET); - return lua_gettop(L); - -err: - push_type_name(L, 3, &ct); - return luaL_error(L, "type %s has no member %s", lua_tostring(L, -1), lua_tostring(L, 2)); - } - - assert(lua_gettop(L) == 4); /* ct, key, ct_usr, mbr_usr */ - data += off; - - if (ct.is_array) { - /* push a reference to the array */ - ct.is_reference = 1; - to = push_cdata(L, -1, &ct); - *(void**) to = data; - return 1; - - } else if (ct.is_bitfield) { - - if (ct.type == INT64_TYPE) { - struct ctype rt; - uint64_t val = *(uint64_t*) data; - val >>= ct.bit_offset; - val &= (UINT64_C(1) << ct.bit_size) - 1; - - memset(&rt, 0, sizeof(rt)); - rt.base_size = 8; - rt.type = INT64_TYPE; - rt.is_unsigned = 1; - rt.is_defined = 1; - - to = push_cdata(L, 0, &rt); - *(uint64_t*) to = val; - - return 1; - - } else if (ct.type == BOOL_TYPE) { - uint64_t val = *(uint64_t*) data; - lua_pushboolean(L, (int) (val & (UINT64_C(1) << ct.bit_offset))); - return 1; - - } else { - uint64_t val = *(uint64_t*) data; - val >>= ct.bit_offset; - val &= (UINT64_C(1) << ct.bit_size) - 1; - lua_pushinteger(L, val); - return 1; - } - - } else if (ct.pointers) { -#ifndef ALLOW_MISALIGNED_ACCESS - union { - uint8_t c[8]; - void* p; - } misalignbuf; - - if ((uintptr_t) data & PTR_ALIGN_MASK) { - memcpy(misalignbuf.c, data, sizeof(void*)); - data = misalignbuf.c; - } -#endif - to = push_cdata(L, -1, &ct); - *(void**) to = *(void**) data; - return 1; - - } else if (ct.type == STRUCT_TYPE || ct.type == UNION_TYPE) { - /* push a reference to the member */ - ct.is_reference = 1; - to = push_cdata(L, -1, &ct); - *(void**) to = data; - return 1; - - } else { -#ifndef ALLOW_MISALIGNED_ACCESS - union { - uint8_t c[8]; - double d; - float f; - uint64_t u64; - } misalignbuf; - - assert(ct.base_size <= 8); - - if ((uintptr_t) data & (ct.base_size - 1)) { - memcpy(misalignbuf.c, data, ct.base_size); - data = misalignbuf.c; - } -#endif - - switch (ct.type) { - case BOOL_TYPE: - lua_pushboolean(L, *(_Bool*) data); - break; - case INT8_TYPE: - lua_pushinteger(L, ct.is_unsigned ? (lua_Integer) *(uint8_t*) data : (lua_Integer) *(int8_t*) data); - break; - case INT16_TYPE: - lua_pushinteger(L, ct.is_unsigned ? (lua_Integer) *(uint16_t*) data : (lua_Integer) *(int16_t*) data); - break; - case ENUM_TYPE: - case INT32_TYPE: - lua_pushinteger(L, ct.is_unsigned ? (lua_Integer) *(uint32_t*) data : (lua_Integer) *(int32_t*) data); - break; - case INT64_TYPE: - to = push_cdata(L, -1, &ct); - *(int64_t*) to = *(int64_t*) data; - break; - case INTPTR_TYPE: - to = push_cdata(L, -1, &ct); - *(intptr_t*) to = *(intptr_t*) data; - break; - case FLOAT_TYPE: - lua_pushnumber(L, *(float*) data); - break; - case DOUBLE_TYPE: - lua_pushnumber(L, *(double*) data); - break; - case COMPLEX_DOUBLE_TYPE: - to = push_cdata(L, -1, &ct); - *(complex_double*) to = *(complex_double*) data; - break; - case COMPLEX_FLOAT_TYPE: - to = push_cdata(L, -1, &ct); - *(complex_float*) to = *(complex_float*) data; - break; - default: - luaL_error(L, "internal error: invalid member type"); - } - - return 1; - } -} - -static complex_double check_complex(lua_State* L, int idx, void* p, struct ctype* ct) -{ - if (ct->type == INVALID_TYPE) { - double d = luaL_checknumber(L, idx); -#ifdef HAVE_COMPLEX - return d; -#else - complex_double c; - c.real = d; - c.imag = 0; - return c; -#endif - } else if (ct->type == COMPLEX_DOUBLE_TYPE) { - return *(complex_double*) p; - } else if (ct->type == COMPLEX_FLOAT_TYPE) { - complex_float* f = (complex_float*) p; -#ifdef HAVE_COMPLEX - return *f; -#else - complex_double d; - d.real = f->real; - d.imag = f->imag; - return d; -#endif - } else { - complex_double dummy; - type_error(L, idx, "complex", 0, NULL); - memset(&dummy, 0, sizeof(dummy)); - return dummy; - } -} - -static int rank(const struct ctype* ct) -{ - if (ct->pointers) { - return 5; - } - - switch (ct->type) { - case COMPLEX_DOUBLE_TYPE: - return 7; - case COMPLEX_FLOAT_TYPE: - return 6; - case INTPTR_TYPE: - return sizeof(intptr_t) >= sizeof(int64_t) ? 4 : 1; - case INT64_TYPE: - return ct->is_unsigned ? 3 : 2; - case INT32_TYPE: - case INT16_TYPE: - case INT8_TYPE: - return 2; - default: - return 0; - } -} - -static void push_complex(lua_State* L, complex_double res, int ct_usr, const struct ctype* ct) -{ - if (ct->type == COMPLEX_DOUBLE_TYPE) { - complex_double* p = (complex_double*) push_cdata(L, ct_usr, ct); - *p = res; - } else { - complex_float* p = (complex_float*) push_cdata(L, ct_usr, ct); -#ifdef HAVE_COMPLEX - *p = (complex float) res; -#else - p->real = (float) res.real; - p->imag = (float) res.imag; -#endif - } -} - -static void push_number(lua_State* L, int64_t val, int ct_usr, const struct ctype* ct) -{ - if ((ct->pointers || ct->type == INTPTR_TYPE) && sizeof(intptr_t) != sizeof(int64_t)) { - intptr_t* p = (intptr_t*) push_cdata(L, ct_usr, ct); - *p = val; - } else { - int64_t* p = (int64_t*) push_cdata(L, ct_usr, ct); - *p = val; - } -} - -static int call_user_op(lua_State* L, const char* opfield, int idx, int ct_usr, const struct ctype* ct) -{ - idx = lua_absindex(L, idx); - - if (push_user_mt(L, ct_usr, ct)) { - lua_pushstring(L, opfield); - lua_rawget(L, -2); - if (!lua_isnil(L, -1)) { - int top = lua_gettop(L); - lua_pushvalue(L, idx); - lua_call(L, 1, LUA_MULTRET); - return lua_gettop(L) - top + 1; - } - lua_pop(L, 2); - } - return -1; -} - -static int cdata_unm(lua_State* L) -{ - struct ctype ct; - void* p; - int64_t val; - int ret; - - lua_settop(L, 1); - p = to_cdata(L, 1, &ct); - - ret = call_user_op(L, "__unm", 1, 2, &ct); - if (ret >= 0) { - return ret; - } - - val = check_intptr(L, 1, p, &ct); - - if (ct.pointers) { - luaL_error(L, "can't negate a pointer value"); - } else { - memset(&ct, 0, sizeof(ct)); - ct.type = INT64_TYPE; - ct.base_size = 8; - ct.is_defined = 1; - push_number(L, -val, 0, &ct); - } - - return 1; -} - -/* returns -ve if no binop was called otherwise returns the number of return - * arguments */ -static int call_user_binop(lua_State* L, const char* opfield, int lidx, int lusr, const struct ctype* lt, int ridx, int rusr, const struct ctype* rt) -{ - lidx = lua_absindex(L, lidx); - ridx = lua_absindex(L, ridx); - - if (push_user_mt(L, lusr, lt)) { - lua_pushstring(L, opfield); - lua_rawget(L, -2); - - if (!lua_isnil(L, -1)) { - int top = lua_gettop(L); - lua_pushvalue(L, lidx); - lua_pushvalue(L, ridx); - lua_call(L, 2, LUA_MULTRET); - return lua_gettop(L) - top + 1; - } - - lua_pop(L, 2); /* user_mt and user_mt.op */ - } - - if (push_user_mt(L, rusr, rt)) { - lua_pushstring(L, opfield); - lua_rawget(L, -2); - - if (!lua_isnil(L, -1)) { - int top = lua_gettop(L); - lua_pushvalue(L, lidx); - lua_pushvalue(L, ridx); - lua_call(L, 2, LUA_MULTRET); - return lua_gettop(L) - top + 1; - } - - lua_pop(L, 2); /* user_mt and user_mt.op */ - } - - return -1; -} - -static int cdata_concat(lua_State* L) -{ - struct ctype lt, rt; - int ret; - - lua_settop(L, 2); - to_cdata(L, 1, <); - to_cdata(L, 2, &rt); - - ret = call_user_binop(L, "__concat", 1, 3, <, 2, 4, &rt); - if (ret >= 0) { - return ret; - } - - return luaL_error(L, "NYI"); -} - -static int cdata_len(lua_State* L) -{ - struct ctype ct; - int ret; - - lua_settop(L, 1); - to_cdata(L, 1, &ct); - - ret = call_user_op(L, "__len", 1, 2, &ct); - if (ret >= 0) { - return ret; - } - - push_type_name(L, 2, &ct); - return luaL_error(L, "type %s does not implement the __len metamethod", lua_tostring(L, -1)); -} - -static int cdata_pairs(lua_State* L) -{ - struct ctype ct; - int ret; - - lua_settop(L, 1); - to_cdata(L, 1, &ct); - - ret = call_user_op(L, "__pairs", 1, 2, &ct); - if (ret >= 0) { - return ret; - } - - push_type_name(L, 2, &ct); - return luaL_error(L, "type %s does not implement the __pairs metamethod", lua_tostring(L, -1)); -} - -static int cdata_ipairs(lua_State* L) -{ - struct ctype ct; - int ret; - - lua_settop(L, 1); - to_cdata(L, 1, &ct); - - ret = call_user_op(L, "__ipairs", 1, 2, &ct); - if (ret >= 0) { - return ret; - } - - push_type_name(L, 2, &ct); - return luaL_error(L, "type %s does not implement the __ipairs metamethod", lua_tostring(L, -1)); -} - -static int cdata_add(lua_State* L) -{ - struct ctype lt, rt, ct; - void *lp, *rp; - int ct_usr; - int ret; - - lua_settop(L, 2); - - lp = to_cdata(L, 1, <); - rp = to_cdata(L, 2, &rt); - assert(lua_gettop(L) == 4); - - ret = call_user_binop(L, "__add", 1, 3, <, 2, 4, &rt); - if (ret >= 0) { - return ret; - } - assert(lua_gettop(L) == 4); - - ct_usr = rank(<) > rank(&rt) ? 3 : 4; - ct = rank(<) > rank(&rt) ? lt : rt; - - if (IS_COMPLEX(ct.type)) { - complex_double left, right, res; - - left = check_complex(L, 1, lp, <); - right = check_complex(L, 2, rp, &rt); - assert(lua_gettop(L) == 4); - -#ifdef HAVE_COMPLEX - res = left + right; -#else - res.real = left.real + right.real; - res.imag = left.imag + right.imag; -#endif - - push_complex(L, res, ct_usr, &ct); - return 1; - - } else { - int64_t left = check_intptr(L, 1, lp, <); - int64_t right = check_intptr(L, 2, rp, &rt); - assert(lua_gettop(L) == 4); - - /* note due to 2s complement it doesn't matter if we do the addition as int or uint, - * but the result needs to be uint64_t if either of the sources are */ - - if (lt.pointers && rt.pointers) { - luaL_error(L, "can't add two pointers"); - - } else if (lt.pointers) { - int64_t res = left + (lt.pointers > 1 ? sizeof(void*) : lt.base_size) * right; - lt.is_array = 0; - push_number(L, res, 3, <); - - } else if (rt.pointers) { - int64_t res = right + (rt.pointers > 1 ? sizeof(void*) : rt.base_size) * left; - rt.is_array = 0; - push_number(L, res, 4, &rt); - - } else { - push_number(L, left + right, ct_usr, &ct); - } - - return 1; - } -} - -static int cdata_sub(lua_State* L) -{ - struct ctype lt, rt, ct; - void *lp, *rp; - int ct_usr; - int ret; - - lua_settop(L, 2); - - lp = to_cdata(L, 1, <); - rp = to_cdata(L, 2, &rt); - - ret = call_user_binop(L, "__sub", 1, 3, <, 2, 4, &rt); - if (ret >= 0) { - return ret; - } - - ct_usr = rank(<) > rank(&rt) ? 3 : 4; - ct = rank(<) > rank(&rt) ? lt : rt; - - if (IS_COMPLEX(ct.type)) { - complex_double left, right, res; - - left = check_complex(L, 1, lp, <); - right = check_complex(L, 2, rp, &rt); - -#ifdef HAVE_COMPLEX - res = left - right; -#else - res.real = left.real - right.real; - res.imag = left.imag - right.imag; -#endif - - push_complex(L, res, ct_usr, &ct); - return 1; - - } else { - int64_t left = check_intptr(L, 1, lp, <); - int64_t right = check_intptr(L, 2, rp, &rt); - - if (rt.pointers) { - luaL_error(L, "NYI: can't subtract a pointer value"); - - } else if (lt.pointers) { - int64_t res = left - (lt.pointers > 1 ? sizeof(void*) : lt.base_size) * right; - lt.is_array = 0; - push_number(L, res, 3, <); - - } else { - int64_t res = left - right; - push_number(L, res, ct_usr, &ct); - } - - return 1; - } -} - -/* TODO fix for unsigned */ -#define NUMBER_ONLY_BINOP(OPSTR, DO_NORMAL, DO_COMPLEX) \ - struct ctype lt, rt, ct; \ - void *lp, *rp; \ - int ct_usr; \ - int ret; \ - \ - lua_settop(L, 2); \ - \ - lp = to_cdata(L, 1, <); \ - rp = to_cdata(L, 2, &rt); \ - \ - ret = call_user_binop(L, OPSTR, 1, 3, <, 2, 4, &rt); \ - if (ret >= 0) { \ - return ret; \ - } \ - \ - ct_usr = rank(<) > rank(&rt) ? 3 : 4; \ - ct = rank(<) > rank(&rt) ? lt : rt; \ - \ - if (IS_COMPLEX(ct.type)) { \ - complex_double res; \ - complex_double left = check_complex(L, 1, lp, <); \ - complex_double right = check_complex(L, 2, rp, &rt); \ - \ - DO_COMPLEX(left, right, res); \ - push_complex(L, res, ct_usr, &ct); \ - \ - } else if (lt.pointers || rt.pointers) { \ - luaL_error(L, "can't operate on a pointer value"); \ - \ - } else { \ - int64_t res; \ - int64_t left = check_intptr(L, 1, lp, <); \ - int64_t right = check_intptr(L, 2, rp, &rt); \ - \ - DO_NORMAL(left, right, res); \ - push_number(L, res, ct_usr, &ct); \ - } \ - \ - return 1 - -#define MUL(l,r,s) s = l * r -#define DIV(l,r,s) s = l / r -#define MOD(l,r,s) s = l % r -#define POW(l,r,s) s = pow(l, r) - -#ifdef HAVE_COMPLEX -#define MULC(l,r,s) s = l * r -#define DIVC(l,r,s) s = l / r -#define MODC(l,r,s) (void) l, (void) r, memset(&s, 0, sizeof(s)), luaL_error(L, "NYI: complex mod") -#define POWC(l,r,s) s = cpow(l, r) -#else -#define MULC(l,r,s) s.real = l.real * r.real - l.imag * r.imag, s.imag = l.real * r.imag + l.imag * r.real -#define DIVC(l,r,s) s.real = (l.real * r.real + l.imag * r.imag) / (r.real * r.real + r.imag * r.imag), \ - s.imag = (l.imag * r.real - l.real * r.imag) / (r.real * r.real + r.imag * r.imag) -#define MODC(l,r,s) (void) l, (void) r, memset(&s, 0, sizeof(s)), luaL_error(L, "NYI: complex mod") -#define POWC(l,r,s) (void) l, (void) r, memset(&s, 0, sizeof(s)), luaL_error(L, "NYI: complex pow") -#endif - -static int cdata_mul(lua_State* L) -{ NUMBER_ONLY_BINOP("__mul", MUL, MULC); } - -static int cdata_div(lua_State* L) -{ NUMBER_ONLY_BINOP("__div", DIV, DIVC); } - -static int cdata_mod(lua_State* L) -{ NUMBER_ONLY_BINOP("__mod", MOD, MODC); } - -static int cdata_pow(lua_State* L) -{ NUMBER_ONLY_BINOP("__pow", POW, POWC); } - -#define COMPARE_BINOP(OPSTR, OP, OPC) \ - struct ctype lt, rt; \ - void *lp, *rp; \ - int ret, res; \ - \ - lua_settop(L, 2); \ - \ - lp = to_cdata(L, 1, <); \ - rp = to_cdata(L, 2, &rt); \ - \ - ret = call_user_binop(L, OPSTR, 1, 3, <, 2, 4, &rt); \ - if (ret >= 0) { \ - return ret; \ - } \ - \ - if (IS_COMPLEX(lt.type) || IS_COMPLEX(rt.type)) { \ - complex_double left = check_complex(L, 1, lp, <); \ - complex_double right = check_complex(L, 2, rp, &rt); \ - \ - res = OPC(left, right); \ - \ - lua_pushboolean(L, res); \ - \ - } else { \ - int64_t left = check_intptr(L, 1, lp, <); \ - int64_t right = check_intptr(L, 2, rp, &rt); \ - \ - if (lt.pointers && rt.pointers) { \ - if (is_void_ptr(<) || is_void_ptr(&rt) || is_same_type(L, 3, 4, <, &rt)) { \ - res = OP((uint64_t) left, (uint64_t) right); \ - } else { \ - goto err; \ - } \ - \ - } else if (lt.is_null && rt.type == FUNCTION_PTR_TYPE) { \ - res = OP((uint64_t) left, (uint64_t) right); \ - \ - } else if (rt.is_null && lt.type == FUNCTION_PTR_TYPE) { \ - res = OP((uint64_t) left, (uint64_t) right); \ - \ - } else if (lt.pointers && rt.type == INTPTR_TYPE && rt.is_unsigned) {\ - res = OP((uint64_t) left, (uint64_t) right); \ - \ - } else if (rt.pointers && lt.type == INTPTR_TYPE && lt.is_unsigned) {\ - res = OP((uint64_t) left, (uint64_t) right); \ - \ - } else if (rt.pointers || lt.pointers) { \ - goto err; \ - \ - } else if (lt.is_unsigned && rt.is_unsigned) { \ - res = OP((uint64_t) left, (uint64_t) right); \ - \ - } else if (lt.is_unsigned) { \ - res = OP((int64_t) (uint64_t) left, right); \ - \ - } else if (rt.is_unsigned) { \ - res = OP(left, (int64_t) (uint64_t) right); \ - \ - } else { \ - res = OP(left, right); \ - } \ - \ - lua_pushboolean(L, res); \ - } \ - return 1 - -#define EQ(l, r) (l) == (r) -#define LT(l, r) (l) < (r) -#define LE(l, r) (l) <= (r) - -#ifdef HAVE_COMPLEX -#define EQC(l, r) (l) == (r) -#else -#define EQC(l, r) (l).real == (r).real && (l).imag == (r).imag -#endif - -#define LEC(l, r) EQC(l, r), luaL_error(L, "complex numbers are non-orderable") -#define LTC(l, r) EQC(l, r), luaL_error(L, "complex numbers are non-orderable") - -static int cdata_eq(lua_State* L) -{ - COMPARE_BINOP("__eq", EQ, EQC); -err: - lua_pushboolean(L, 0); - return 1; -} - -static int cdata_lt(lua_State* L) -{ - COMPARE_BINOP("__lt", LT, LTC); -err: - lua_getuservalue(L, 1); - lua_getuservalue(L, 2); - push_type_name(L, -2, <); - push_type_name(L, -2, <); - return luaL_error(L, "trying to compare incompatible types %s and %s", lua_tostring(L, -2), lua_tostring(L, -1)); -} - -static int cdata_le(lua_State* L) -{ - COMPARE_BINOP("__le", LE, LEC); -err: - lua_getuservalue(L, 1); - lua_getuservalue(L, 2); - push_type_name(L, -2, <); - push_type_name(L, -2, <); - return luaL_error(L, "trying to compare incompatible types %s and %s", lua_tostring(L, -2), lua_tostring(L, -1)); -} - -static const char* etype_tostring(int type) -{ - switch (type) { - case VOID_TYPE: return "void"; - case DOUBLE_TYPE: return "double"; - case FLOAT_TYPE: return "float"; - case COMPLEX_DOUBLE_TYPE: return "complex double"; - case COMPLEX_FLOAT_TYPE: return "complex float"; - case BOOL_TYPE: return "bool"; - case INT8_TYPE: return "int8"; - case INT16_TYPE: return "int16"; - case INT32_TYPE: return "int32"; - case INT64_TYPE: return "int64"; - case INTPTR_TYPE: return "intptr"; - case ENUM_TYPE: return "enum"; - case UNION_TYPE: return "union"; - case STRUCT_TYPE: return "struct"; - case FUNCTION_PTR_TYPE: return "function ptr"; - case FUNCTION_TYPE: return "function"; - default: return "invalid"; - } -} - -static void print_type(lua_State* L, const struct ctype* ct) -{ - lua_pushfstring(L, " sz %d %d %d align %d ptr %d %d %d type %s%s %d %d %d name %d call %d %d var %d %d %d bit %d %d %d %d jit %d", - /* sz */ - ct->base_size, - ct->array_size, - ct->offset, - /* align */ - ct->align_mask, - /* ptr */ - ct->is_array, - ct->pointers, - ct->const_mask, - /* type */ - ct->is_unsigned ? "u" : "", - etype_tostring(ct->type), - ct->is_reference, - ct->is_defined, - ct->is_null, - /* name */ - ct->has_member_name, - /* call */ - ct->calling_convention, - ct->has_var_arg, - /* var */ - ct->is_variable_array, - ct->is_variable_struct, - ct->variable_size_known, - /* bit */ - ct->is_bitfield, - ct->has_bitfield, - ct->bit_offset, - ct->bit_size, - /* jit */ - ct->is_jitted); -} - -static int ctype_tostring(lua_State* L) -{ - struct ctype ct; - assert(lua_type(L, 1) == LUA_TUSERDATA); - lua_settop(L, 1); - check_ctype(L, 1, &ct); - assert(lua_gettop(L) == 2); - push_type_name(L, -1, &ct); - lua_pushfstring(L, "ctype<%s>", lua_tostring(L, -1)); - - if (DEBUG_TOSTRING) { - print_type(L, &ct); - lua_concat(L, 2); - } - - return 1; -} - -static int cdata_tostring(lua_State* L) -{ - struct ctype ct; - char buf[64]; - void* p; - int ret; - - lua_settop(L, 1); - p = to_cdata(L, 1, &ct); - - ret = call_user_op(L, "__tostring", 1, 2, &ct); - if (ret >= 0) { - return ret; - } - - if (ct.pointers > 0 || ct.is_reference || ct.type == STRUCT_TYPE || ct.type == UNION_TYPE) { - push_type_name(L, -1, &ct); - lua_pushfstring(L, "cdata<%s>: %p", lua_tostring(L, -1), p); - - if (DEBUG_TOSTRING) { - print_type(L, &ct); - lua_concat(L, 2); - } - - return 1; - } - - switch (ct.type) { - case COMPLEX_DOUBLE_TYPE: - { - complex_double c = *(complex_double*) p; - lua_pushfstring(L, "%f+%fi", creal(c), cimag(c)); - } - return 1; - - case COMPLEX_FLOAT_TYPE: - { - complex_float c = *(complex_float*) p; - lua_pushfstring(L, "%f+%fi", crealf(c), cimagf(c)); - } - return 1; - - case FUNCTION_PTR_TYPE: - p = *(void**) p; - push_type_name(L, -1, &ct); - lua_pushfstring(L, "cdata<%s>: %p", lua_tostring(L, -1), *(void**) p); - return 1; - - case INTPTR_TYPE: - lua_pushfstring(L, "%p", *(uintptr_t*) p); - return 1; - - case INT64_TYPE: - sprintf(buf, ct.is_unsigned ? "%"PRIu64 : "%"PRId64, *(uint64_t*) p); - lua_pushstring(L, buf); - return 1; - - default: - sprintf(buf, ct.is_unsigned ? "%"PRId64 : "%"PRId64, (int64_t) check_intptr(L, 1, p, &ct)); - lua_pushstring(L, buf); - return 1; - } -} - -static int ffi_type(lua_State* L) -{ - if (lua_isuserdata(L, 1) && lua_getmetatable(L, 1)) { - if (equals_upval(L, -1, &cdata_mt_key) || equals_upval(L, -1, &ctype_mt_key)) { - lua_pushstring(L, "cdata"); - return 1; - } - lua_pop(L, 1); /* mt */ - } - - /* call the old _G.type, we use an upvalue as _G.type is set - * to this function */ - lua_pushvalue(L, lua_upvalueindex(1)); - lua_insert(L, 1); - lua_call(L, lua_gettop(L)-1, LUA_MULTRET); - return lua_gettop(L); -} - -static int ffi_number(lua_State* L) -{ - struct ctype ct; - void* data = to_cdata(L, 1, &ct); - - if (ct.type != INVALID_TYPE) { - lua_pushinteger(L, check_intptr(L, 1, data, &ct)); - return 1; - } else { - /* call the old _G.tonumber, we use an upvalue as _G.tonumber is set - * to this function */ - lua_pushvalue(L, lua_upvalueindex(1)); - lua_insert(L, 1); - lua_call(L, lua_gettop(L)-1, LUA_MULTRET); - return lua_gettop(L); - } -} - -static int ffi_string(lua_State* L) -{ - struct ctype ct; - char* data; - lua_settop(L, 2); - - data = (char*) check_cdata(L, 1, &ct); - - if (is_void_ptr(&ct)) { - lua_pushlstring(L, data, (size_t) luaL_checknumber(L, 2)); - return 1; - - } else if (ct.type == INT8_TYPE && ct.pointers == 1) { - size_t sz; - - if (lua_isuserdata(L, 2)) { - ptrdiff_t val; - if (!cdata_tointeger(L, 2, &val)) { - type_error(L, 2, "int", 0, NULL); - } - sz = (size_t) val; - } else if (!lua_isnil(L, 2)) { - sz = (size_t) luaL_checknumber(L, 2); - - } else if (ct.is_array && !ct.is_variable_array) { - char* nul = memchr(data, '\0', ct.array_size); - sz = nul ? nul - data : ct.array_size; - - } else { - sz = strlen(data); - } - - lua_pushlstring(L, data, sz); - return 1; - } - - return luaL_error(L, "unable to convert cdata to string"); -} - -static int ffi_copy(lua_State* L) -{ - struct ctype ft, tt; - char *to, *from; - - setmintop(L, 3); - to = (char*) check_pointer(L, 1, &tt); - from = (char*) check_pointer(L, 2, &ft); - - if (!lua_isnoneornil(L, 3)) { - memcpy(to, from, (size_t) luaL_checknumber(L, 3)); - - } else if (ft.type == INT8_TYPE && ft.pointers == 1) { - size_t sz = ft.is_array ? ft.array_size : strlen(from); - memcpy(to, from, sz); - to[sz] = '\0'; - } - - return 0; -} - -static int ffi_fill(lua_State* L) -{ - struct ctype ct; - void* to; - size_t sz; - int val = 0; - - setmintop(L, 3); - to = check_pointer(L, 1, &ct); - sz = (size_t) luaL_checknumber(L, 2); - - if (!lua_isnoneornil(L, 3)) { - val = (int) luaL_checkinteger(L, 3); - } - - memset(to, val, sz); - return 0; -} - -static int ffi_abi(lua_State* L) -{ - luaL_checkstring(L, 1); - push_upval(L, &abi_key); - lua_pushvalue(L, 1); - lua_rawget(L, -2); - lua_pushboolean(L, lua_toboolean(L, -1)); - return 1; -} - -static void* find_symbol(lua_State* L, int modidx, const char* asmname) -{ - size_t i; - void** libs; - size_t num; - void* sym = NULL; - - libs = (void**) lua_touserdata(L, modidx); - num = lua_rawlen(L, modidx) / sizeof(void*); - - for (i = 0; i < num && sym == NULL; i++) { - if (libs[i]) { - sym = GetProcAddressA(libs[i], asmname); - } - } - - return sym; -} - -/* pushes the user table */ -static void* lookup_global(lua_State* L, int modidx, int nameidx, const char** pname, struct ctype* ct) -{ - int top = lua_gettop(L); - void* sym; - - modidx = lua_absindex(L, modidx); - nameidx = lua_absindex(L, nameidx); - - *pname = luaL_checkstring(L, nameidx); - - /* get the ctype */ - push_upval(L, &functions_key); - lua_pushvalue(L, nameidx); - lua_rawget(L, -2); - if (lua_isnil(L, -1)) { - luaL_error(L, "missing declaration for function/global %s", *pname); - return NULL; - } - - /* leave just the ct_usr on the stack */ - *ct = *(const struct ctype*) lua_touserdata(L, -1); - lua_getuservalue(L, -1); - lua_replace(L, top + 1); - lua_pop(L, 1); - - assert(lua_gettop(L) == top + 1); - - /* get the assembly name */ - push_upval(L, &asmname_key); - lua_pushvalue(L, nameidx); - lua_rawget(L, -2); - if (lua_isstring(L, -1)) { - *pname = lua_tostring(L, -1); - } - lua_pop(L, 2); - - sym = find_symbol(L, modidx, *pname); - - assert(lua_gettop(L) == top + 1); - return sym; -} - -static int ffi_debug(lua_State* L) -{ - lua_newtable(L); - push_upval(L, &ctype_mt_key); - lua_setfield(L, -2, "ctype_mt"); - push_upval(L, &cdata_mt_key); - lua_setfield(L, -2, "cdata_mt"); - push_upval(L, &constants_key); - lua_setfield(L, -2, "constants"); - push_upval(L, &types_key); - lua_setfield(L, -2, "types"); - push_upval(L, &gc_key); - lua_setfield(L, -2, "gc"); - push_upval(L, &callbacks_key); - lua_setfield(L, -2, "callbacks"); - push_upval(L, &functions_key); - lua_setfield(L, -2, "functions"); - push_upval(L, &abi_key); - lua_setfield(L, -2, "abi"); - push_upval(L, &next_unnamed_key); - lua_setfield(L, -2, "next_unnamed"); - return 1; -} - -static int do64(lua_State* L, int is_unsigned) -{ - lua_Number low, high; - struct ctype ct; - int64_t val; - - lua_settop(L, 2); - - if (!lua_isnil(L, 2)) { - high = luaL_checknumber(L, 1); - low = luaL_checknumber(L, 2); - } else { - high = 0; - low = luaL_checknumber(L, 1); - } - - val = ((int64_t) (uint32_t) high << 32) | (int64_t) (uint32_t) low; - - if (!is_unsigned && (high < 0 || low < 0)) { - val = -val; - } - - memset(&ct, 0, sizeof(ct)); - ct.type = INT64_TYPE; - ct.is_unsigned = is_unsigned; - ct.is_defined = 1; - ct.base_size = sizeof(int64_t); - push_number(L, (int64_t) val, 0, &ct); - - return 1; -} - -static int ffi_i64(lua_State* L) -{ return do64(L, 0); } - -static int ffi_u64(lua_State* L) -{ return do64(L, 1); } - -static const luaL_Reg cdata_mt[] = { - {"__gc", &cdata_gc}, - {"__index", &cdata_index}, - {"__newindex", &cdata_newindex}, - {"__add", &cdata_add}, - {"__sub", &cdata_sub}, - {"__mul", &cdata_mul}, - {"__div", &cdata_div}, - {"__mod", &cdata_mod}, - {"__pow", &cdata_pow}, - {"__unm", &cdata_unm}, - {"__eq", &cdata_eq}, - {"__lt", &cdata_lt}, - {"__le", &cdata_le}, - {"__tostring", &cdata_tostring}, - {"__concat", &cdata_concat}, - {"__len", &cdata_len}, - {"__pairs", &cdata_pairs}, - {"__ipairs", &cdata_ipairs}, - {NULL, NULL} -}; - -static const luaL_Reg ctype_mt[] = { - {"__call", &ctype_call}, - {"__new", &ctype_new}, - {"__tostring", &ctype_tostring}, - {NULL, NULL} -}; - -static const luaL_Reg ffi_reg[] = { - {"cdef", &ffi_cdef}, - {"new", &ffi_new}, - {"typeof", &ffi_typeof}, - {"cast", &ffi_cast}, - {"metatype", &ffi_metatype}, - {"gc", &ffi_gc}, - {"sizeof", &ffi_sizeof}, - {"alignof", &ffi_alignof}, - {"offsetof", &ffi_offsetof}, - {"istype", &ffi_istype}, - {"string", &ffi_string}, - {"copy", &ffi_copy}, - {"fill", &ffi_fill}, - {"abi", &ffi_abi}, - {"debug", &ffi_debug}, - {"i64", &ffi_i64}, - {"u64", &ffi_u64}, - {NULL, NULL} -}; - -/* leaves the usr table on the stack */ -static void push_builtin(lua_State* L, struct ctype* ct, const char* name, int type, int size, int align, int is_unsigned) -{ - memset(ct, 0, sizeof(*ct)); - ct->type = type; - ct->base_size = size; - ct->align_mask = align; - ct->is_defined = 1; - ct->is_unsigned = is_unsigned; - - if (IS_COMPLEX(type)) { - lua_newtable(L); - } else { - lua_pushnil(L); - } - - push_upval(L, &types_key); - push_ctype(L, -2, ct); - lua_setfield(L, -2, name); - lua_pop(L, 2); /* types, usr table */ -} - -static void push_builtin_undef(lua_State* L, struct ctype* ct, const char* name, int type) -{ - memset(ct, 0, sizeof(*ct)); - ct->type = type; - - push_upval(L, &types_key); - push_ctype(L, 0, ct); - lua_setfield(L, -2, name); - lua_pop(L, 1); /* types */ -} - -static void add_typedef(lua_State* L, const char* from, const char* to) -{ - struct ctype ct; - struct parser P; - P.line = 1; - P.align_mask = DEFAULT_ALIGN_MASK; - P.next = P.prev = from; - - push_upval(L, &types_key); - parse_type(L, &P, &ct); - parse_argument(L, &P, -1, &ct, NULL, NULL); - push_ctype(L, -1, &ct); - - /* stack is at +4: types, type usr, arg usr, ctype */ - - lua_setfield(L, -4, to); - lua_pop(L, 3); /* types, type usr, arg usr */ -} - -static int setup_upvals(lua_State* L) -{ - /* setup builtin types */ - { - complex_double* pc; - struct {char ch; uint16_t v;} a16; - struct {char ch; uint32_t v;} a32; - struct {char ch; uint64_t v;} a64; - struct {char ch; float v;} af; - struct {char ch; double v;} ad; -#ifdef HAVE_LONG_DOUBLE - struct {char ch; long double v;} ald; -#endif - struct {char ch; uintptr_t v;} aptr; - struct ctype ct; - struct {char ch; complex_float v;} cf; - struct {char ch; complex_double v;} cd; -#if defined HAVE_LONG_DOUBLE && defined HAVE_COMPLEX - struct {char ch; complex long double v;} cld; -#endif - - push_builtin(L, &ct, "void", VOID_TYPE, 0, 0, 0); - push_builtin(L, &ct, "bool", BOOL_TYPE, sizeof(_Bool), sizeof(_Bool) -1, 1); - push_builtin(L, &ct, "uint8_t", INT8_TYPE, sizeof(uint8_t), 0, 1); - push_builtin(L, &ct, "int8_t", INT8_TYPE, sizeof(int8_t), 0, 0); - push_builtin(L, &ct, "uint16_t", INT16_TYPE, sizeof(uint16_t), ALIGNOF(a16), 1); - push_builtin(L, &ct, "int16_t", INT16_TYPE, sizeof(int16_t), ALIGNOF(a16), 0); - push_builtin(L, &ct, "uint32_t", INT32_TYPE, sizeof(uint32_t), ALIGNOF(a32), 1); - push_builtin(L, &ct, "int32_t", INT32_TYPE, sizeof(int32_t), ALIGNOF(a32), 0); - push_builtin(L, &ct, "uint64_t", INT64_TYPE, sizeof(uint64_t), ALIGNOF(a64), 1); - push_builtin(L, &ct, "int64_t", INT64_TYPE, sizeof(int64_t), ALIGNOF(a64), 0); - push_builtin(L, &ct, "float", FLOAT_TYPE, sizeof(float), ALIGNOF(af), 0); - push_builtin(L, &ct, "double", DOUBLE_TYPE, sizeof(double), ALIGNOF(ad), 0); -#ifdef HAVE_LONG_DOUBLE - push_builtin(L, &ct, "long double", LONG_DOUBLE_TYPE, sizeof(long double), ALIGNOF(ald), 0); -#else - push_builtin_undef(L, &ct, "long double", LONG_DOUBLE_TYPE); -#endif - push_builtin(L, &ct, "uintptr_t", INTPTR_TYPE, sizeof(uintptr_t), ALIGNOF(aptr), 1); - push_builtin(L, &ct, "intptr_t", INTPTR_TYPE, sizeof(uintptr_t), ALIGNOF(aptr), 0); - push_builtin(L, &ct, "complex float", COMPLEX_FLOAT_TYPE, sizeof(complex_float), ALIGNOF(cf), 0); - push_builtin(L, &ct, "complex double", COMPLEX_DOUBLE_TYPE, sizeof(complex_double), ALIGNOF(cd), 0); -#if defined HAVE_LONG_DOUBLE && defined HAVE_COMPLEX - push_builtin(L, &ct, "complex long double", COMPLEX_LONG_DOUBLE_TYPE, sizeof(complex long double), ALIGNOF(cld), 0); -#else - push_builtin_undef(L, &ct, "complex long double", COMPLEX_LONG_DOUBLE_TYPE); -#endif - - /* add NULL and i constants */ - push_upval(L, &constants_key); - - memset(&ct, 0, sizeof(ct)); - ct.type = VOID_TYPE; - ct.is_defined = 1; - ct.pointers = 1; - ct.is_null = 1; - - /* add ffi.C.NULL */ - push_cdata(L, 0, &ct); - lua_setfield(L, -2, "NULL"); - - /* add ffi.NULL */ - push_cdata(L, 0, &ct); - lua_setfield(L, 1, "NULL"); - - memset(&ct, 0, sizeof(ct)); - ct.type = COMPLEX_DOUBLE_TYPE; - ct.is_defined = 1; - ct.base_size = sizeof(complex_double); - pc = (complex_double*) push_cdata(L, 0, &ct); -#ifdef HAVE_COMPLEX - *pc = 1i; -#else - pc->real = 0; - pc->imag = 1; -#endif - lua_setfield(L, -2, "i"); - - lua_pop(L, 1); /* constants */ - } - - assert(lua_gettop(L) == 1); - - /* setup builtin typedefs */ - { - add_typedef(L, "bool", "_Bool"); - - if (sizeof(uint32_t) == sizeof(size_t)) { - add_typedef(L, "uint32_t", "size_t"); - add_typedef(L, "int32_t", "ssize_t"); - } else if (sizeof(uint64_t) == sizeof(size_t)) { - add_typedef(L, "uint64_t", "size_t"); - add_typedef(L, "int64_t", "ssize_t"); - } - - if (sizeof(int32_t) == sizeof(intptr_t)) { - add_typedef(L, "int32_t", "intptr_t"); - add_typedef(L, "int32_t", "ptrdiff_t"); - } else if (sizeof(int64_t) == sizeof(intptr_t)) { - add_typedef(L, "int64_t", "intptr_t"); - add_typedef(L, "int64_t", "ptrdiff_t"); - } - - if (sizeof(uint8_t) == sizeof(wchar_t)) { - add_typedef(L, "uint8_t", "wchar_t"); - } else if (sizeof(uint16_t) == sizeof(wchar_t)) { - add_typedef(L, "uint16_t", "wchar_t"); - } else if (sizeof(uint32_t) == sizeof(wchar_t)) { - add_typedef(L, "uint32_t", "wchar_t"); - } - - if (sizeof(va_list) == sizeof(char*)) { - add_typedef(L, "char*", "va_list"); - } else { - struct {char ch; va_list v;} av; - lua_pushfstring(L, "struct {char data[%d] __attribute__((align(%d)));}", (int) sizeof(va_list), (int) ALIGNOF(av) + 1); - add_typedef(L, lua_tostring(L, -1), "va_list"); - lua_pop(L, 1); - } - - add_typedef(L, "va_list", "__builtin_va_list"); - add_typedef(L, "va_list", "__gnuc_va_list"); - } - - assert(lua_gettop(L) == 1); - - /* setup ABI params table */ - push_upval(L, &abi_key); - -#if defined ARCH_X86 || defined ARCH_ARM - lua_pushboolean(L, 1); - lua_setfield(L, -2, "32bit"); -#elif defined ARCH_X64 || defined ARCH_PPC64 - lua_pushboolean(L, 1); - lua_setfield(L, -2, "64bit"); -#else -#error -#endif - -#if defined ARCH_X86 || defined ARCH_X64 || defined ARCH_ARM || defined ARCH_PPC64 - lua_pushboolean(L, 1); - lua_setfield(L, -2, "le"); -#else -#error -#endif - -#if defined ARCH_X86 || defined ARCH_X64 || defined ARCH_PPC64 - lua_pushboolean(L, 1); - lua_setfield(L, -2, "fpu"); -#elif defined ARCH_ARM - lua_pushboolean(L, 1); - lua_setfield(L, -2, "softfp"); -#else -#error -#endif - lua_pop(L, 1); /* abi tbl */ - - - /* GC table - shouldn't pin cdata values */ - push_upval(L, &gc_key); - lua_newtable(L); - lua_pushliteral(L, "k"); - lua_setfield(L, -2, "__mode"); - lua_setmetatable(L, -2); - lua_pop(L, 1); /* gc table */ - - - /* ffi.os */ -#if defined OS_CE - lua_pushliteral(L, "WindowsCE"); -#elif defined OS_WIN - lua_pushliteral(L, "Windows"); -#elif defined OS_OSX - lua_pushliteral(L, "OSX"); -#elif defined OS_LINUX - lua_pushliteral(L, "Linux"); -#elif defined OS_BSD - lua_pushliteral(L, "BSD"); -#elif defined OS_POSIX - lua_pushliteral(L, "POSIX"); -#else - lua_pushliteral(L, "Other"); -#endif - lua_setfield(L, 1, "os"); - - - /* ffi.arch */ -#if defined ARCH_X86 - lua_pushliteral(L, "x86"); -#elif defined ARCH_X64 - lua_pushliteral(L, "x64"); -#elif defined ARCH_ARM - lua_pushliteral(L, "arm"); -#elif defined ARCH_PPC64 - lua_pushliteral(L, "ppc64"); -#else -# error -#endif - lua_setfield(L, 1, "arch"); - - assert(lua_gettop(L) == 1); - - return 0; -} - -static void setup_mt(lua_State* L, const luaL_Reg* mt, int upvals) -{ - lua_pushboolean(L, 1); - lua_setfield(L, -upvals-2, "__metatable"); - luaL_setfuncs(L, mt, upvals); -} - -LUA_API int luaopen_lffi(lua_State* L) -{ - lua_settop(L, 0); - - lua_newtable(L); - set_upval(L, &niluv_key); - - lua_newtable(L); - setup_mt(L, ctype_mt, 0); - set_upval(L, &ctype_mt_key); - - lua_newtable(L); - set_upval(L, &callbacks_key); - - lua_newtable(L); - set_upval(L, &gc_key); - - lua_newtable(L); - push_upval(L, &callbacks_key); - push_upval(L, &gc_key); - setup_mt(L, cdata_mt, 2); - set_upval(L, &cdata_mt_key); - - lua_newtable(L); - set_upval(L, &constants_key); - - lua_newtable(L); - set_upval(L, &types_key); - - lua_newtable(L); - set_upval(L, &functions_key); - - lua_newtable(L); - set_upval(L, &asmname_key); - - lua_newtable(L); - set_upval(L, &abi_key); - - lua_pushinteger(L, 1); - set_upval(L, &next_unnamed_key); - - assert(lua_gettop(L) == 0); - - /* ffi table */ - lua_newtable(L); - luaL_setfuncs(L, ffi_reg, 0); - - /* setup_upvals(ffi tbl) */ - lua_pushcfunction(L, &setup_upvals); - lua_pushvalue(L, 1); - lua_call(L, 1, 0); - - assert(lua_gettop(L) == 1); - - lua_getglobal(L, "tonumber"); - lua_pushcclosure(L, &ffi_number, 1); - lua_pushvalue(L, -1); - lua_setglobal(L, "tonumber"); - lua_setfield(L, -2, "number"); /* ffi.number */ - - lua_getglobal(L, "type"); - lua_pushcclosure(L, &ffi_type, 1); - lua_pushvalue(L, -1); - lua_setglobal(L, "type"); - lua_setfield(L, -2, "type"); /* ffi.type */ - return 1; -} diff --git a/luaclib/src/lffi/ffi.h b/luaclib/src/lffi/ffi.h deleted file mode 100644 index f0770769..00000000 --- a/luaclib/src/lffi/ffi.h +++ /dev/null @@ -1,403 +0,0 @@ -/* vim: ts=4 sw=4 sts=4 et tw=78 - * Portions copyright (c) 2015-present, Facebook, Inc. All rights reserved. - * Portions copyright (c) 2011 James R. McKaskill. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#pragma once - -#include "../../../src/core.h" -#include -#include -#include -#ifndef HAVE_COMPLEX - #define HAVE_COMPLEX - #include -#endif - -struct jit; -#define Dst_DECL struct jit* Dst -#define Dst_REF (Dst->ctx) -#define DASM_EXTERN(a,b,c,d) get_extern(a,b,c,d) - -#if defined LUA_FFI_BUILD_AS_DLL -# define EXPORT __declspec(dllexport) -#elif defined __GNUC__ -# define EXPORT __attribute__((visibility("default"))) -#else -# define EXPORT -#endif - -static int lua_absindex2(lua_State* L, int idx) { - return (LUA_REGISTRYINDEX <= idx && idx < 0) - ? lua_gettop(L) + idx + 1 - : idx; -} -/* use our own version of lua_absindex such that lua_absindex(L, 0) == 0 */ -#define lua_absindex(L, idx) lua_absindex2(L, idx) - -#if LUA_VERSION_NUM == 501 -static void lua_callk(lua_State *L, int nargs, int nresults, int ctx, lua_CFunction k) -{ - lua_call(L, nargs, nresults); -} -/* -** set functions from list 'l' into table at top - 'nup'; each -** function gets the 'nup' elements at the top as upvalues. -** Returns with only the table at the stack. -*/ -static void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { - luaL_checkstack(L, nup, "too many upvalues"); - for (; l && l->name; l++) { /* fill the table with given functions */ - int i; - for (i = 0; i < nup; i++) /* copy upvalues to the top */ - lua_pushvalue(L, -nup); - lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ - lua_setfield(L, -(nup + 2), l->name); - } - lua_pop(L, nup); /* remove upvalues */ -} -#define lua_setuservalue lua_setfenv -#define lua_getuservalue lua_getfenv -#define lua_rawlen lua_objlen -static char* luaL_prepbuffsize(luaL_Buffer* B, size_t sz) { - if (sz > LUAL_BUFFERSIZE) { - luaL_error(B->L, "string too long"); - } - return luaL_prepbuffer(B); -} -#elif LUA_VERSION_NUM >= 503 -static void (lua_remove)(lua_State *L, int idx) { - lua_remove(L, idx); -} -#endif - -/* architectures */ -#if defined _WIN32 && defined UNDER_CE -# define OS_CE -#elif defined _WIN32 -# define OS_WIN -#elif defined __APPLE__ && defined __MACH__ -# define OS_OSX -#elif defined __linux__ -# define OS_LINUX -#elif defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__ -# define OS_BSD -#elif defined unix || defined __unix__ || defined __unix || defined _POSIX_VERSION || defined _XOPEN_VERSION -# define OS_POSIX -#endif - -/* architecture */ -#if defined __i386__ || defined _M_IX86 -# define ARCH_X86 -#elif defined __amd64__ || defined _M_X64 -# define ARCH_X64 -#elif defined __arm__ || defined __ARM__ || defined ARM || defined __ARM || defined __arm || defined __aarch64__ || defined _M_ARM -# define ARCH_ARM -#elif defined __powerpc64__ -# define ARCH_PPC64 -#else -# error -#endif - - -#ifdef _WIN32 - -# ifdef UNDER_CE - static void* DoLoadLibraryA(const char* name) { - wchar_t buf[MAX_PATH]; - int sz = MultiByteToWideChar(CP_UTF8, 0, name, -1, buf, 512); - if (sz > 0) { - buf[sz] = 0; - return LoadLibraryW(buf); - } else { - return NULL; - } - } -# define LoadLibraryA DoLoadLibraryA -# else -# define GetProcAddressA GetProcAddress -# endif - -# define LIB_FORMAT_1 "%s.dll" -# define AllocPage(size) VirtualAlloc(NULL, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE) -# define FreePage(data, size) VirtualFree(data, 0, MEM_RELEASE) -# define EnableExecute(data, size) do {DWORD old; VirtualProtect(data, size, PAGE_EXECUTE, &old); FlushInstructionCache(GetCurrentProcess(), data, size);} while (0) -# define EnableWrite(data, size) do {DWORD old; VirtualProtect(data, size, PAGE_READWRITE, &old);} while (0) - -#else -#ifdef OS_OSX -# define LIB_FORMAT_1 "%s.dylib" -# define LIB_FORMAT_2 "lib%s.dylib" -#else -# define LIB_FORMAT_1 "%s.so" -# define LIB_FORMAT_2 "lib%s.so" -#endif -# define LoadLibraryA(name) dlopen(name, RTLD_LAZY | RTLD_GLOBAL) -# define GetProcAddressA(lib, name) dlsym(lib, name) -# define AllocPage(size) mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0) -# define FreePage(data, size) munmap(data, size) -# define EnableExecute(data, size) mprotect(data, size, PROT_READ|PROT_EXEC) -# define EnableWrite(data, size) mprotect(data, size, PROT_READ|PROT_WRITE) -#endif - -#if defined ARCH_X86 || defined ARCH_X64 -#define ALLOW_MISALIGNED_ACCESS -#endif - -struct token; - -struct parser { - int line; - const char* next; - const char* prev; - unsigned align_mask; -}; - -struct page { - size_t size; - size_t off; - size_t freed; -}; - -struct jit { - lua_State* L; - int32_t last_errno; - size_t pagenum; - struct page** pages; - size_t align_page_size; - void** globals; - int function_extern; - void* lua_dll; - void* kernel32_dll; -}; - -#define ALIGN_DOWN(PTR, MASK) \ - (((uintptr_t) (PTR)) & (~ ((uintptr_t) (MASK)) )) -#define ALIGN_UP(PTR, MASK) \ - (( ((uintptr_t) (PTR)) + ((uintptr_t) (MASK)) ) & (~ ((uintptr_t) (MASK)) )) - -/* struct cdata/struct ctype */ - -#define PTR_ALIGN_MASK (sizeof(void*) - 1) -#define FUNCTION_ALIGN_MASK (sizeof(void (*)()) - 1) -#define DEFAULT_ALIGN_MASK 7 - -#ifdef OS_OSX -/* TODO: figure out why the alignof trick doesn't work on OS X */ -#define ALIGNED_DEFAULT 7 -#elif defined __GNUC__ -#define ALIGNED_DEFAULT (__alignof__(void* __attribute__((aligned))) - 1) -#else -#define ALIGNED_DEFAULT PTR_ALIGN_MASK -#endif - -extern int ctype_mt_key; -extern int cdata_mt_key; -extern int constants_key; -extern int types_key; -extern int gc_key; -extern int callbacks_key; -extern int functions_key; -extern int abi_key; -extern int next_unnamed_key; -extern int niluv_key; -extern int asmname_key; - -int equals_upval(lua_State* L, int idx, int* key); -void push_upval(lua_State* L, int* key); -void set_upval(lua_State* L, int* key); - -/* both ctype and cdata are stored as userdatas - * - * usr value is a table shared between the related subtypes which has: - * name -> member ctype (for structs and unions) - * +ves -> member ctype - in memory order (for structs) - * +ves -> argument ctype (for function prototypes) - * 0 -> return ctype (for function prototypes) - * light userdata -> misc - */ - -enum { - C_CALL, - STD_CALL, - FAST_CALL, -}; - -enum { - INVALID_TYPE, - VOID_TYPE, - FLOAT_TYPE, - DOUBLE_TYPE, - LONG_DOUBLE_TYPE, - COMPLEX_FLOAT_TYPE, - COMPLEX_DOUBLE_TYPE, - COMPLEX_LONG_DOUBLE_TYPE, - BOOL_TYPE, - INT8_TYPE, - INT16_TYPE, - INT32_TYPE, - INT64_TYPE, - INTPTR_TYPE, - ENUM_TYPE, - UNION_TYPE, - STRUCT_TYPE, - FUNCTION_TYPE, - FUNCTION_PTR_TYPE, -}; - -#define IS_CHAR_UNSIGNED (((char) -1) > 0) -#define IS_COMPLEX(type) ((type) == COMPLEX_FLOAT_TYPE || (type) == COMPLEX_DOUBLE_TYPE) - -#define POINTER_BITS 2 -#define POINTER_MAX ((1 << POINTER_BITS) - 1) - -#define ALIGNOF(S) ((int) ((char*) &S.v - (char*) &S - 1)) - -/* Note: if adding a new member that is associated with a struct/union - * definition then it needs to be copied over in ctype.c:set_defined for when - * we create types based off of the declaration alone. - * - * Since this is used as a header for every ctype and cdata, and we create a - * ton of them on the stack, we try and minimise its size. - */ -struct ctype { - size_t base_size; /* size of the base type in bytes */ - - union { - /* valid if is_bitfield */ - struct { - /* size of bitfield in bits */ - unsigned bit_size : 7; - /* offset within the current byte between 0-63 */ - unsigned bit_offset : 6; - }; - /* Valid if is_array */ - size_t array_size; - /* Valid for is_variable_struct or is_variable_array. If - * variable_size_known (only used for is_variable_struct) then this is - * the total increment otherwise this is the per element increment. - */ - size_t variable_increment; - }; - size_t offset; - unsigned align_mask : 4; /* as (align bytes - 1) eg 7 gives 8 byte alignment */ - unsigned pointers : POINTER_BITS; /* number of dereferences to get to the base type including +1 for arrays */ - unsigned const_mask : POINTER_MAX + 1; /* const pointer mask, LSB is current pointer, +1 for the whether the base type is const */ - unsigned type : 5; /* value given by type enum above */ - unsigned is_reference : 1; - unsigned is_array : 1; - unsigned is_defined : 1; - unsigned is_null : 1; - unsigned has_member_name : 1; - unsigned calling_convention : 2; - unsigned has_var_arg : 1; - unsigned is_variable_array : 1; /* set for variable array types where we don't know the variable size yet */ - unsigned is_variable_struct : 1; - unsigned variable_size_known : 1; /* used for variable structs after we know the variable size */ - unsigned is_bitfield : 1; - unsigned has_bitfield : 1; - unsigned is_jitted : 1; - unsigned is_packed : 1; - unsigned is_unsigned : 1; -}; - -#ifdef _MSC_VER -__declspec(align(16)) -#endif -struct cdata { - const struct ctype type -#ifdef __GNUC__ - __attribute__ ((aligned(16))) -#endif - ; -}; - -#ifdef HAVE_COMPLEX -typedef double complex complex_double; -typedef float complex complex_float; -static complex_double mk_complex_double(double real, double imag) { - return real + imag * 1i; -} -static complex_double mk_complex_float(double real, double imag) { - return real + imag * 1i; -} -#else -typedef struct { - double real, imag; -} complex_double; - -typedef struct { - float real, imag; -} complex_float; - -static complex_double mk_complex_double(double real, double imag) { - complex_double ret = { real, imag }; - return ret; -} -static complex_float mk_complex_float(double real, double imag) { - complex_float ret = { real, imag }; - return ret; -} -static double creal(complex_double c) { - return c.real; -} -static float crealf(complex_float c) { - return c.real; -} - -static double cimag(complex_double c) { - return c.imag; -} -static float cimagf(complex_float c) { - return c.imag; -} -#endif - -#define CALLBACK_FUNC_USR_IDX 1 - -void set_defined(lua_State* L, int ct_usr, struct ctype* ct); -struct ctype* push_ctype(lua_State* L, int ct_usr, const struct ctype* ct); -void* push_cdata(lua_State* L, int ct_usr, const struct ctype* ct); /* called from asm */ -void check_ctype(lua_State* L, int idx, struct ctype* ct); -void* to_cdata(lua_State* L, int idx, struct ctype* ct); -void* check_cdata(lua_State* L, int idx, struct ctype* ct); -size_t ctype_size(lua_State* L, const struct ctype* ct); - -int parse_type(lua_State* L, struct parser* P, struct ctype* type); -void parse_argument(lua_State* L, struct parser* P, int ct_usr, struct ctype* type, struct token* name, struct parser* asmname); -void push_type_name(lua_State* L, int usr, const struct ctype* ct); - -int push_user_mt(lua_State* L, int ct_usr, const struct ctype* ct); - -int ffi_cdef(lua_State* L); - -int x86_return_size(lua_State* L, int usr, const struct ctype* ct); -void compile_globals(struct jit* jit, lua_State* L); -int get_extern(struct jit* jit, uint8_t* addr, int idx, int type); - -/* WARNING: assembly needs to be updated for prototype changes of these functions */ -int check_bool(lua_State* L, int idx); -double check_double(lua_State* L, int idx); -double check_complex_imag(lua_State* L, int idx); -float check_float(lua_State* L, int idx); -uint64_t check_uint64(lua_State* L, int idx); -int64_t check_int64(lua_State* L, int idx); -int32_t check_int32(lua_State* L, int idx); -uint32_t check_uint32(lua_State* L, int idx); -uintptr_t check_uintptr(lua_State* L, int idx); -int32_t check_enum(lua_State* L, int idx, int to_usr, const struct ctype* tt); -/* these two will always push a value so that we can create structs/functions on the fly */ -void* check_typed_pointer(lua_State* L, int idx, int to_usr, const struct ctype* tt); -complex_double check_complex_double(lua_State* L, int idx); -complex_float check_complex_float(lua_State* L, int idx); - -void unpack_varargs_stack(lua_State* L, int first, int last, char* to); -void unpack_varargs_reg(lua_State* L, int first, int last, char* to); - -void unpack_varargs_stack_skip(lua_State* L, int first, int last, int ints_to_skip, int floats_to_skip, char* to); -void unpack_varargs_float(lua_State* L, int first, int last, int max, char* to); -void unpack_varargs_int(lua_State* L, int first, int last, int max, char* to); diff --git a/luaclib/src/lffi/makefile b/luaclib/src/lffi/makefile deleted file mode 100644 index e49bc12a..00000000 --- a/luaclib/src/lffi/makefile +++ /dev/null @@ -1,20 +0,0 @@ -.PHONY : build rebuild clean - -default : - @echo "=======================================" - @echo "Please use 'make build' command to build it.." - @echo "Please use 'make rebuild' command to build it.." - @echo "Please use 'make clean' command to clean all." - @echo "=======================================" - -CC = cc - -INCLUDE = -I/usr/local/include -I../../../src -LIB = -L/usr/local/lib -L../ -L../../../ - -CFLAGS = -O3 -Wall -shared -fPIC -Wno-unused-function -fno-strict-aliasing -Wno-uninitialized -Wno-ignored-attributes -DLL = -lcore - -build: - @$(CC) -o lffi.so ffi.c parser.c ctype.c $(CFLAGS) $(INCLUDE) $(LIB) $(DLL) - @mv *.so ../../ diff --git a/luaclib/src/lffi/parser.c b/luaclib/src/lffi/parser.c deleted file mode 100644 index b170b058..00000000 --- a/luaclib/src/lffi/parser.c +++ /dev/null @@ -1,2614 +0,0 @@ -/* vim: ts=4 sw=4 sts=4 et tw=78 - * Portions copyright (c) 2015-present, Facebook, Inc. All rights reserved. - * Portions copyright (c) 2011 James R. McKaskill. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ -#include "ffi.h" - -#define IS_CONST(tok) (IS_LITERAL(tok, "const") || IS_LITERAL(tok, "__const") || IS_LITERAL(tok, "__const__")) -#define IS_VOLATILE(tok) (IS_LITERAL(tok, "volatile") || IS_LITERAL(tok, "__volatile") || IS_LITERAL(tok, "__volatile__")) -#define IS_RESTRICT(tok) (IS_LITERAL(tok, "restrict") || IS_LITERAL(tok, "__restrict") || IS_LITERAL(tok, "__restrict__")) -#define IS_INLINE(tok) (IS_LITERAL(tok, "inline") || IS_LITERAL(tok, "__inline") || IS_LITERAL(tok, "__inline__")) - -enum etoken { - TOK_NIL, - TOK_NUMBER, - TOK_STRING, - TOK_TOKEN, - - /* the order of these values must match the token strings in lex.c */ - - TOK_3_BEGIN, - TOK_VA_ARG, - - TOK_2_BEGIN, - TOK_LEFT_SHIFT, TOK_RIGHT_SHIFT, TOK_LOGICAL_AND, TOK_LOGICAL_OR, TOK_LESS_EQUAL, - TOK_GREATER_EQUAL, TOK_EQUAL, TOK_NOT_EQUAL, - - TOK_1_BEGIN, - TOK_OPEN_CURLY, TOK_CLOSE_CURLY, TOK_SEMICOLON, TOK_COMMA, TOK_COLON, - TOK_ASSIGN, TOK_OPEN_PAREN, TOK_CLOSE_PAREN, TOK_OPEN_SQUARE, TOK_CLOSE_SQUARE, - TOK_DOT, TOK_AMPERSAND, TOK_LOGICAL_NOT, TOK_BITWISE_NOT, TOK_MINUS, - TOK_PLUS, TOK_STAR, TOK_DIVIDE, TOK_MODULUS, TOK_LESS, - TOK_GREATER, TOK_BITWISE_XOR, TOK_BITWISE_OR, TOK_QUESTION, TOK_POUND, - - TOK_REFERENCE = TOK_AMPERSAND, - TOK_MULTIPLY = TOK_STAR, - TOK_BITWISE_AND = TOK_AMPERSAND, -}; - -struct token { - enum etoken type; - int64_t integer; - const char* str; - size_t size; -}; - -#define IS_LITERAL(TOK, STR) \ - (((TOK).size == sizeof(STR) - 1) && 0 == memcmp((TOK).str, STR, sizeof(STR) - 1)) - -/* the order of tokens _must_ match the order of the enum etoken enum */ - -static char tok3[][4] = { - "...", /* unused ">>=", "<<=", */ -}; - -static char tok2[][3] = { - "<<", ">>", "&&", "||", "<=", - ">=", "==", "!=", - /* unused "+=", "-=", "*=", "/=", "%=", "&=", "^=", "|=", "++", "--", "->", "::", */ -}; - -static char tok1[] = { - '{', '}', ';', ',', ':', - '=', '(', ')', '[', ']', - '.', '&', '!', '~', '-', - '+', '*', '/', '%', '<', - '>', '^', '|', '?', '#' -}; - -static int next_token(lua_State* L, struct parser* P, struct token* tok) -{ - size_t i; - const char* s = P->next; - - /* UTF8 BOM */ - if (s[0] == '\xEF' && s[1] == '\xBB' && s[2] == '\xBF') { - s += 3; - } - - /* consume whitespace and comments */ - for (;;) { - /* consume whitespace */ - while(*s == '\t' || *s == '\n' || *s == ' ' || *s == '\v' || *s == '\r') { - if (*s == '\n') { - P->line++; - } - s++; - } - - /* consume comments */ - if (*s == '/' && *(s+1) == '/') { - - s = strchr(s, '\n'); - if (!s) { - luaL_error(L, "non-terminated comment"); - } - - } else if (*s == '/' && *(s+1) == '*') { - s += 2; - - for (;;) { - if (s[0] == '\0') { - luaL_error(L, "non-terminated comment"); - } else if (s[0] == '*' && s[1] == '/') { - s += 2; - break; - } else if (s[0] == '\n') { - P->line++; - } - s++; - } - - } else if (*s == '\0') { - tok->type = TOK_NIL; - return 0; - - } else { - break; - } - } - - P->prev = s; - - for (i = 0; i < sizeof(tok3) / sizeof(tok3[0]); i++) { - if (s[0] == tok3[i][0] && s[1] == tok3[i][1] && s[2] == tok3[i][2]) { - tok->type = (enum etoken) (TOK_3_BEGIN + 1 + i); - P->next = s + 3; - goto end; - } - } - - for (i = 0; i < sizeof(tok2) / sizeof(tok2[0]); i++) { - if (s[0] == tok2[i][0] && s[1] == tok2[i][1]) { - tok->type = (enum etoken) (TOK_2_BEGIN + 1 + i); - P->next = s + 2; - goto end; - } - } - - for (i = 0; i < sizeof(tok1) / sizeof(tok1[0]); i++) { - if (s[0] == tok1[i]) { - tok->type = (enum etoken) (TOK_1_BEGIN + 1 + i); - P->next = s + 1; - goto end; - } - } - - if (*s == '.' || *s == '-' || ('0' <= *s && *s <= '9')) { - /* number */ - tok->type = TOK_NUMBER; - - /* split out the negative case so we get the full range of bits for - * unsigned (eg to support 0xFFFFFFFF where sizeof(long) == 4) - */ - if (*s == '-') { - tok->integer = strtol(s, (char**) &s, 0); - } else { - tok->integer = strtoul(s, (char**) &s, 0); - } - - while (*s == 'u' || *s == 'U' || *s == 'l' || *s == 'L') { - s++; - } - - P->next = s; - goto end; - - } else if (*s == '\'' || *s == '\"') { - /* "..." or '...' */ - char quote = *s; - s++; /* jump over " */ - - tok->type = TOK_STRING; - tok->str = s; - - while (*s != quote) { - - if (*s == '\0' || (*s == '\\' && *(s+1) == '\0')) { - return luaL_error(L, "string not finished"); - } - - if (*s == '\\') { - s++; - } - - s++; - } - - tok->size = s - tok->str; - s++; /* jump over " */ - P->next = s; - goto end; - - } else if (('a' <= *s && *s <= 'z') || ('A' <= *s && *s <= 'Z') || *s == '_') { - /* tokens */ - tok->type = TOK_TOKEN; - tok->str = s; - - while (('a' <= *s && *s <= 'z') || ('A' <= *s && *s <= 'Z') || *s == '_' || ('0' <= *s && *s <= '9')) { - s++; - } - - tok->size = s - tok->str; - P->next = s; - goto end; - - } else { - return luaL_error(L, "invalid character %d", P->line); - } - -end: - /*fprintf(stderr, "token %d %d %.*s %.10s\n", tok->type, (int) tok->size, (tok->type == TOK_TOKEN || tok->type == TOK_STRING) ? (int) tok->size : 0, tok->str, P->next);*/ - return 1; -} - -#define require_token(L, P, tok) require_token_line(L, P, tok, __FILE__, __LINE__) - -static void require_token_line(lua_State* L, struct parser* P, struct token* tok, const char* file, int line) -{ - if (!next_token(L, P, tok)) { - luaL_error(L, "unexpected end on line %s:%d", file, line); - } -} - -static void check_token(lua_State* L, struct parser* P, int type, const char* str, const char* err, ...) -{ - struct token tok; - if (!next_token(L, P, &tok) || tok.type != type || (tok.type == TOK_TOKEN && (tok.size != strlen(str) || memcmp(tok.str, str, tok.size) != 0))) { - va_list ap; - va_start(ap, err); - lua_pushvfstring(L, err, ap); - lua_error(L); - } -} - -static void put_back(struct parser* P) -{ P->next = P->prev; } - - -int64_t calculate_constant(lua_State* L, struct parser* P); - -static int g_name_key; -static int g_front_name_key; -static int g_back_name_key; - -#ifndef max -#define max(a,b) ((a) < (b) ? (b) : (a)) -#endif - -#ifndef min -#define min(a,b) ((a) < (b) ? (a) : (b)) -#endif - -enum test {TEST}; - -/* Parses an enum definition from after the open curly through to the close - * curly. Expects the user table to be on the top of the stack - */ -static int parse_enum(lua_State* L, struct parser* P, struct ctype* type) -{ - struct token tok; - int value = -1; - int ct_usr = lua_gettop(L); - - for (;;) { - require_token(L, P, &tok); - - assert(lua_gettop(L) == ct_usr); - - if (tok.type == TOK_CLOSE_CURLY) { - break; - } else if (tok.type != TOK_TOKEN) { - return luaL_error(L, "unexpected token in enum at line %d", P->line); - } - - lua_pushlstring(L, tok.str, tok.size); - - require_token(L, P, &tok); - - if (tok.type == TOK_COMMA || tok.type == TOK_CLOSE_CURLY) { - /* we have an auto calculated enum value */ - value++; - } else if (tok.type == TOK_ASSIGN) { - /* we have an explicit enum value */ - value = (int) calculate_constant(L, P); - require_token(L, P, &tok); - } else { - return luaL_error(L, "unexpected token in enum at line %d", P->line); - } - - assert(lua_gettop(L) == ct_usr + 1); - - /* add the enum value to the constants table */ - push_upval(L, &constants_key); - lua_pushvalue(L, -2); - lua_pushinteger(L, value); - lua_rawset(L, -3); - lua_pop(L, 1); - - assert(lua_gettop(L) == ct_usr + 1); - - /* add the enum value to the enum usr value table */ - lua_pushinteger(L, value); - lua_rawset(L, ct_usr); - - if (tok.type == TOK_CLOSE_CURLY) { - break; - } else if (tok.type != TOK_COMMA) { - return luaL_error(L, "unexpected token in enum at line %d", P->line); - } - } - - type->base_size = sizeof(enum test); - type->align_mask = sizeof(enum test) - 1; - - assert(lua_gettop(L) == ct_usr); - return 0; -} - -static void calculate_member_position(lua_State* L, struct parser* P, struct ctype* ct, struct ctype* mt, int* pbit_offset, int* pbitfield_type) -{ - int bit_offset = *pbit_offset; - - if (ct->type == UNION_TYPE) { - size_t msize; - - if (mt->is_variable_struct || mt->is_variable_array) { - luaL_error(L, "NYI: variable sized members in unions"); - return; - - } else if (mt->is_bitfield) { - msize = (mt->align_mask + 1); -#ifdef _WIN32 - /* MSVC has a bug where it doesn't update the alignment of - * a union for bitfield members. */ - mt->align_mask = 0; -#endif - - } else if (mt->is_array) { - msize = mt->array_size * (mt->pointers > 1 ? sizeof(void*) : mt->base_size); - - } else { - msize = mt->pointers ? sizeof(void*) : mt->base_size; - } - - ct->base_size = max(ct->base_size, msize); - - } else if (mt->is_bitfield) { - if (mt->has_member_name && mt->bit_size == 0) { - luaL_error(L, "zero length bitfields must be unnamed on line %d", P->line); - } - -#if defined _WIN32 - /* MSVC uses a seperate storage unit for each size. This is aligned - * before the first bitfield. :0 finishes up the storage unit using - * the greater alignment of the storage unit or the type used with the - * :0. This is equivalent to the :0 always creating a new storage - * unit, but not necesserily using it yet. - */ - - if (*pbitfield_type == -1 && mt->bit_size == 0) { - /* :0 not after a bitfield are ignored */ - return; - } - - { - int different_storage = mt->align_mask != *pbitfield_type; - int no_room_left = bit_offset + mt->bit_size > (mt->align_mask + 1) * CHAR_BIT; - - if (different_storage || no_room_left || !mt->bit_size) { - ct->base_size += (bit_offset + CHAR_BIT - 1) / CHAR_BIT; - bit_offset = 0; - if (*pbitfield_type >= 0) { - ct->base_size = ALIGN_UP(ct->base_size, *pbitfield_type); - } - ct->base_size = ALIGN_UP(ct->base_size, mt->align_mask); - } - } - - mt->bit_offset = bit_offset; - mt->offset = ct->base_size; - - *pbitfield_type = mt->align_mask; - bit_offset += mt->bit_size; - -// #elif defined OS_OSX -// /* OSX doesn't use containers and bitfields are not aligned. So -// * bitfields never add any padding, except for :0 which still forces -// * an alignment based off the type used with the :0 */ -// if (mt->bit_size) { -// mt->offset = ct->base_size; -// mt->bit_offset = bit_offset; -// bit_offset += mt->bit_size; -// ct->base_size += bit_offset / CHAR_BIT; -// bit_offset = bit_offset % CHAR_BIT; -// } else { -// ct->base_size += (bit_offset + CHAR_BIT - 1) / CHAR_BIT; -// ct->base_size = ALIGN_UP(ct->base_size, mt->align_mask); -// bit_offset = 0; -// } - -// if (!mt->has_member_name) { -// /* unnamed bitfields don't update the struct alignment */ -// mt->align_mask = 0; -// } - -#elif defined __GNUC__ - /* GCC tries to pack bitfields in as close as much as possible, but - * still making sure that they don't cross alignment boundaries. - * :0 forces an alignment based off the type used with the :0 - */ - - int bits_used = (ct->base_size - ALIGN_DOWN(ct->base_size, mt->align_mask)) * CHAR_BIT + bit_offset; - int need_to_realign = bits_used + mt->bit_size > mt->base_size * CHAR_BIT; - - if (!mt->is_packed && (!mt->bit_size || need_to_realign)) { - ct->base_size += (bit_offset + CHAR_BIT - 1) / CHAR_BIT; - ct->base_size = ALIGN_UP(ct->base_size, mt->align_mask); - bit_offset = 0; - } - - mt->bit_offset = bit_offset; - mt->offset = ct->base_size; - - bit_offset += mt->bit_size; - ct->base_size += bit_offset / CHAR_BIT; - bit_offset = bit_offset % CHAR_BIT; - - /* unnamed bitfields don't update the struct alignment */ - if (!mt->has_member_name) { - mt->align_mask = 0; - } -#else -#error -#endif - - } else { - /* finish up the current bitfield storage unit */ - ct->base_size += (bit_offset + CHAR_BIT - 1) / CHAR_BIT; - bit_offset = 0; - - if (*pbitfield_type >= 0) { - ct->base_size = ALIGN_UP(ct->base_size, *pbitfield_type); - } - - *pbitfield_type = -1; - - ct->base_size = ALIGN_UP(ct->base_size, mt->align_mask); - mt->offset = ct->base_size; - - if (mt->is_variable_array) { - ct->is_variable_struct = 1; - ct->variable_increment = mt->pointers > 1 ? sizeof(void*) : mt->base_size; - - } else if (mt->is_variable_struct) { - assert(!mt->variable_size_known && !mt->is_array && !mt->pointers); - ct->base_size += mt->base_size; - ct->is_variable_struct = 1; - ct->variable_increment = mt->variable_increment; - - } else if (mt->is_array) { - ct->base_size += mt->array_size * (mt->pointers > 1 ? sizeof(void*) : mt->base_size); - - } else { - ct->base_size += mt->pointers ? sizeof(void*) : mt->base_size; - } - } - - /* increase the outer struct/union alignment if needed */ - if (mt->align_mask > (int) ct->align_mask) { - ct->align_mask = mt->align_mask; - } - - if (mt->has_bitfield || mt->is_bitfield) { - ct->has_bitfield = 1; - } - - *pbit_offset = bit_offset; -} - -static int copy_submembers(lua_State* L, int to_usr, int from_usr, const struct ctype* ft, int* midx) -{ - struct ctype ct; - int i, sublen; - - from_usr = lua_absindex(L, from_usr); - to_usr = lua_absindex(L, to_usr); - - /* integer keys */ - sublen = (int) lua_rawlen(L, from_usr); - for (i = 1; i <= sublen; i++) { - lua_rawgeti(L, from_usr, i); - - ct = *(const struct ctype*) lua_touserdata(L, -1); - ct.offset += ft->offset; - lua_getuservalue(L, -1); - - push_ctype(L, -1, &ct); - lua_rawseti(L, to_usr, (*midx)++); - - lua_pop(L, 2); /* ctype, user value */ - } - - /* string keys */ - lua_pushnil(L); - while (lua_next(L, from_usr)) { - if (lua_type(L, -2) == LUA_TSTRING) { - struct ctype ct = *(const struct ctype*) lua_touserdata(L, -1); - ct.offset += ft->offset; - lua_getuservalue(L, -1); - - /* uservalue[sub_mname] = new_sub_mtype */ - lua_pushvalue(L, -3); - push_ctype(L, -2, &ct); - lua_rawset(L, to_usr); - - lua_pop(L, 1); /* remove submember user value */ - } - lua_pop(L, 1); - } - - return 0; -} - -static int add_member(lua_State* L, int ct_usr, int mname, int mbr_usr, const struct ctype* mt, int* midx) -{ - ct_usr = lua_absindex(L, ct_usr); - mname = lua_absindex(L, mname); - - push_ctype(L, mbr_usr, mt); - - /* usrvalue[mbr index] = pushed mtype */ - lua_pushvalue(L, -1); - lua_rawseti(L, ct_usr, (*midx)++); - - /* set usrvalue[mname] = pushed mtype */ - lua_pushvalue(L, mname); - lua_pushvalue(L, -2); - lua_rawset(L, ct_usr); - - /* set usrvalue[mtype] = mname */ - lua_pushvalue(L, -1); - lua_pushvalue(L, mname); - lua_rawset(L, ct_usr); - - lua_pop(L, 1); - - return 0; -} - -/* Parses a struct from after the open curly through to the close curly. - */ -static int parse_struct(lua_State* L, struct parser* P, int tmp_usr, const struct ctype* ct) -{ - struct token tok; - int midx = 1; - int top = lua_gettop(L); - - tmp_usr = lua_absindex(L, tmp_usr); - - /* parse members */ - for (;;) { - struct ctype mbase; - - assert(lua_gettop(L) == top); - - /* see if we're at the end of the struct */ - require_token(L, P, &tok); - if (tok.type == TOK_CLOSE_CURLY) { - break; - } else if (ct->is_variable_struct) { - return luaL_error(L, "can't have members after a variable sized member on line %d", P->line); - } else { - put_back(P); - } - - /* members are of the form - * , , ; - * eg struct foo bar, *bar2[2]; - * mbase is 'struct foo' - * mtype is '' then '*[2]' - * mname is 'bar' then 'bar2' - */ - - parse_type(L, P, &mbase); - - for (;;) { - struct token mname; - struct ctype mt = mbase; - - memset(&mname, 0, sizeof(mname)); - - if (ct->is_variable_struct) { - return luaL_error(L, "can't have members after a variable sized member on line %d", P->line); - } - - assert(lua_gettop(L) == top + 1); - parse_argument(L, P, -1, &mt, &mname, NULL); - assert(lua_gettop(L) == top + 2); - - if (!mt.is_defined && (mt.pointers - mt.is_array) == 0) { - return luaL_error(L, "member type is undefined on line %d", P->line); - } - - if (mt.type == VOID_TYPE && (mt.pointers - mt.is_array) == 0) { - return luaL_error(L, "member type can not be void on line %d", P->line); - } - - mt.has_member_name = (mname.size > 0); - lua_pushlstring(L, mname.str, mname.size); - - add_member(L, tmp_usr, -1, -2, &mt, &midx); - - /* pop the usr value from push_argument and the member name */ - lua_pop(L, 2); - assert(lua_gettop(L) == top + 1); - - require_token(L, P, &tok); - if (tok.type == TOK_SEMICOLON) { - break; - } else if (tok.type != TOK_COMMA) { - luaL_error(L, "unexpected token in struct definition on line %d", P->line); - } - } - - /* pop the usr value from push_type */ - lua_pop(L, 1); - } - - assert(lua_gettop(L) == top); - return 0; -} - -static int calculate_struct_offsets(lua_State* L, struct parser* P, int ct_usr, struct ctype* ct, int tmp_usr) -{ - int i; - int midx = 1; - int sz = (int) lua_rawlen(L, tmp_usr); - int bit_offset = 0; - int bitfield_type = -1; - - ct_usr = lua_absindex(L, ct_usr); - tmp_usr = lua_absindex(L, tmp_usr); - - for (i = 1; i <= sz; i++) { - struct ctype mt; - - /* get the member type */ - lua_rawgeti(L, tmp_usr, i); - mt = *(const struct ctype*) lua_touserdata(L, -1); - - /* get the member user table */ - lua_getuservalue(L, -1); - - /* get the member name */ - lua_pushvalue(L, -2); - lua_rawget(L, tmp_usr); - - calculate_member_position(L, P, ct, &mt, &bit_offset, &bitfield_type); - - if (mt.has_member_name) { - assert(!lua_isnil(L, -1)); - add_member(L, ct_usr, -1, -2, &mt, &midx); - - } else if (mt.type == STRUCT_TYPE || mt.type == UNION_TYPE) { - /* With an unnamed member we copy all of the submembers into our - * usr value adjusting the offset as necessary. Note ctypes are - * immutable so need to push a new ctype to update the offset. - */ - copy_submembers(L, ct_usr, -2, &mt, &midx); - - } else { - /* We ignore unnamed members that aren't structs or unions. These - * are there just to change the padding */ - } - - lua_pop(L, 3); - } - - /* finish up the current bitfield storage unit */ - ct->base_size += (bit_offset + CHAR_BIT - 1) / CHAR_BIT; - - /* only void is allowed 0 size */ - if (ct->base_size == 0) { - ct->base_size = 1; - } - - ct->base_size = ALIGN_UP(ct->base_size, ct->align_mask); - return 0; -} - -/* copy over attributes that could be specified before the typedef eg - * __attribute__(packed) const type_t */ -static void instantiate_typedef(struct parser* P, struct ctype* tt, const struct ctype* ft) -{ - struct ctype pt = *tt; - *tt = *ft; - - tt->const_mask |= pt.const_mask; - tt->is_packed = pt.is_packed; - - if (tt->is_packed) { - tt->align_mask = 0; - } else { - /* Instantiate the typedef in the current packing. This may be - * further updated if a pointer is added or another alignment - * attribute is applied. If pt.align_mask is already non-zero than an - * increased alignment via __declspec(aligned(#)) has been set. */ - tt->align_mask = max(min(P->align_mask, tt->align_mask), pt.align_mask); - } -} - -/* this parses a struct or union starting with the optional - * name before the opening brace - * leaves the type usr value on the stack - */ -static int parse_record(lua_State* L, struct parser* P, struct ctype* ct) -{ - struct token tok; - int top = lua_gettop(L); - - require_token(L, P, &tok); - - /* name is optional */ - if (tok.type == TOK_TOKEN) { - /* declaration */ - lua_pushlstring(L, tok.str, tok.size); - - assert(lua_gettop(L) == top+1); - - /* lookup the name to see if we've seen this type before */ - push_upval(L, &types_key); - lua_pushvalue(L, -2); - lua_rawget(L, top+2); - - assert(lua_gettop(L) == top+3); - - if (lua_isnil(L, -1)) { - lua_pop(L, 1); /* pop the nil usr value */ - lua_newtable(L); /* the new usr table */ - - /* stack layout is: - * top+1: record name - * top+2: types table - * top+3: new usr table - */ - - lua_pushlightuserdata(L, &g_name_key); - lua_pushvalue(L, top+1); - lua_rawset(L, top+3); /* usr[name_key] = name */ - - lua_pushvalue(L, top+1); - push_ctype(L, top+3, ct); - lua_rawset(L, top+2); /* types[name] = new_ctype */ - - } else { - /* get the exsting declared type */ - const struct ctype* prevt = (const struct ctype*) lua_touserdata(L, top+3); - - if (prevt->type != ct->type) { - lua_getuservalue(L, top+3); - push_type_name(L, -1, ct); - push_type_name(L, top+3, prevt); - luaL_error(L, "type '%s' previously declared as '%s'", lua_tostring(L, -2), lua_tostring(L, -1)); - } - - instantiate_typedef(P, ct, prevt); - - /* replace the ctype with its usr value */ - lua_getuservalue(L, -1); - lua_replace(L, -2); - } - - /* remove the extra name and types table */ - lua_replace(L, -3); - lua_pop(L, 1); - - assert(lua_gettop(L) == top + 1 && lua_istable(L, -1)); - - /* if a name is given then we may be at the end of the string - * eg for ffi.new('struct foo') - */ - if (!next_token(L, P, &tok)) { - return 0; - } - - } else { - /* create a new unnamed record */ - int num; - - /* get the next unnamed number */ - push_upval(L, &next_unnamed_key); - num = lua_tointeger(L, -1); - lua_pop(L, 1); - - /* increment the unnamed upval */ - lua_pushinteger(L, num + 1); - set_upval(L, &next_unnamed_key); - - lua_newtable(L); /* the new usr table - leave on stack */ - - /* usr[name_key] = num */ - lua_pushlightuserdata(L, &g_name_key); - lua_pushfstring(L, "%d", num); - lua_rawset(L, -3); - } - - if (tok.type != TOK_OPEN_CURLY) { - /* this may just be a declaration or use of the type as an argument or - * member */ - put_back(P); - return 0; - } - - if (ct->is_defined) { - return luaL_error(L, "redefinition in line %d", P->line); - } - - assert(lua_gettop(L) == top + 1 && lua_istable(L, -1)); - - if (ct->type == ENUM_TYPE) { - parse_enum(L, P, ct); - } else { - /* we do a two stage parse, where we parse the content first and build up - * the temp user table. We then iterate over that to calculate the offsets - * and fill out ct_usr. This is so we can handle out of order members - * (eg vtable) and attributes specified at the end of the struct. - */ - lua_newtable(L); - parse_struct(L, P, -1, ct); - calculate_struct_offsets(L, P, -2, ct, -1); - assert(lua_gettop(L) == top + 2 && lua_istable(L, -1)); - lua_pop(L, 1); - } - - assert(lua_gettop(L) == top + 1 && lua_istable(L, -1)); - set_defined(L, -1, ct); - assert(lua_gettop(L) == top + 1); - return 0; -} - -/* parses single or multi work built in types, and pushes it onto the stack */ -static int parse_type_name(lua_State* L, struct parser* P) -{ - struct token tok; - int flags = 0; - - enum { - UNSIGNED = 0x01, - SIGNED = 0x02, - LONG = 0x04, - SHORT = 0x08, - INT = 0x10, - CHAR = 0x20, - LONG_LONG = 0x40, - INT8 = 0x80, - INT16 = 0x100, - INT32 = 0x200, - INT64 = 0x400, - DOUBLE = 0x800, - FLOAT = 0x1000, - COMPLEX = 0x2000, - }; - - require_token(L, P, &tok); - - /* we have to manually decode the builtin types since they can take up - * more then one token - */ - for (;;) { - if (tok.type != TOK_TOKEN) { - break; - } else if (IS_LITERAL(tok, "unsigned")) { - flags |= UNSIGNED; - } else if (IS_LITERAL(tok, "signed")) { - flags |= SIGNED; - } else if (IS_LITERAL(tok, "short")) { - flags |= SHORT; - } else if (IS_LITERAL(tok, "char")) { - flags |= CHAR; - } else if (IS_LITERAL(tok, "long")) { - flags |= (flags & LONG) ? LONG_LONG : LONG; - } else if (IS_LITERAL(tok, "int")) { - flags |= INT; - } else if (IS_LITERAL(tok, "__int8")) { - flags |= INT8; - } else if (IS_LITERAL(tok, "__int16")) { - flags |= INT16; - } else if (IS_LITERAL(tok, "__int32")) { - flags |= INT32; - } else if (IS_LITERAL(tok, "__int64")) { - flags |= INT64; - } else if (IS_LITERAL(tok, "double")) { - flags |= DOUBLE; - } else if (IS_LITERAL(tok, "float")) { - flags |= FLOAT; - } else if (IS_LITERAL(tok, "complex") || IS_LITERAL(tok, "_Complex")) { - flags |= COMPLEX; - } else if (IS_LITERAL(tok, "register")) { - /* ignore */ - } else { - break; - } - - if (!next_token(L, P, &tok)) { - break; - } - } - - if (flags) { - put_back(P); - } - - if (flags & CHAR) { - if (flags & SIGNED) { - lua_pushliteral(L, "int8_t"); - } else if (flags & UNSIGNED) { - lua_pushliteral(L, "uint8_t"); - } else { - lua_pushstring(L, (((char) -1) > 0) ? "uint8_t" : "int8_t"); - } - - } else if (flags & INT8) { - lua_pushstring(L, (flags & UNSIGNED) ? "uint8_t" : "int8_t"); - } else if (flags & INT16) { - lua_pushstring(L, (flags & UNSIGNED) ? "uint16_t" : "int16_t"); - } else if (flags & INT32) { - lua_pushstring(L, (flags & UNSIGNED) ? "uint32_t" : "int32_t"); - } else if (flags & (INT64 | LONG_LONG)) { - lua_pushstring(L, (flags & UNSIGNED) ? "uint64_t" : "int64_t"); - - } else if (flags & COMPLEX) { - if (flags & LONG) { - lua_pushliteral(L, "complex long double"); - } else if (flags & FLOAT) { - lua_pushliteral(L, "complex float"); - } else { - lua_pushliteral(L, "complex double"); - } - - } else if (flags & DOUBLE) { - if (flags & LONG) { - lua_pushliteral(L, "long double"); - } else { - lua_pushliteral(L, "double"); - } - - } else if (flags & FLOAT) { - lua_pushliteral(L, "float"); - - } else if (flags & SHORT) { -#define SHORT_TYPE(u) (sizeof(short) == sizeof(int64_t) ? u "int64_t" : sizeof(short) == sizeof(int32_t) ? u "int32_t" : u "int16_t") - if (flags & UNSIGNED) { - lua_pushstring(L, SHORT_TYPE("u")); - } else { - lua_pushstring(L, SHORT_TYPE("")); - } -#undef SHORT_TYPE - - } else if (flags & LONG) { -#define LONG_TYPE(u) (sizeof(long) == sizeof(int64_t) ? u "int64_t" : u "int32_t") - if (flags & UNSIGNED) { - lua_pushstring(L, LONG_TYPE("u")); - } else { - lua_pushstring(L, LONG_TYPE("")); - } -#undef LONG_TYPE - - } else if (flags) { -#define INT_TYPE(u) (sizeof(int) == sizeof(int64_t) ? u "int64_t" : sizeof(int) == sizeof(int32_t) ? u "int32_t" : u "int16_t") - if (flags & UNSIGNED) { - lua_pushstring(L, INT_TYPE("u")); - } else { - lua_pushstring(L, INT_TYPE("")); - } -#undef INT_TYPE - - } else { - lua_pushlstring(L, tok.str, tok.size); - } - - return 0; -} - -/* parse_attribute parses a token to see if it is an attribute. It may then - * parse some following tokens to decode the attribute setting the appropriate - * fields in ct. It will return 1 if the token was used (and possibly some - * more following it) or 0 if not. If the token was used, the next token must - * be retrieved using next_token/require_token. - */ -static int parse_attribute(lua_State* L, struct parser* P, struct token* tok, struct ctype* ct, struct parser* asmname) -{ - if (tok->type != TOK_TOKEN) { - return 0; - - } else if (asmname && (IS_LITERAL(*tok, "__asm__") || IS_LITERAL(*tok, "__asm"))) { - check_token(L, P, TOK_OPEN_PAREN, NULL, "unexpected token after __asm__ on line %d", P->line); - *asmname = *P; - - require_token(L, P, tok); - while (tok->type == TOK_STRING) { - require_token(L, P, tok); - } - - if (tok->type != TOK_CLOSE_PAREN) { - luaL_error(L, "unexpected token after __asm__ on line %d", P->line); - } - return 1; - - } else if (IS_LITERAL(*tok, "__attribute__") || IS_LITERAL(*tok, "__declspec")) { - int parens = 1; - check_token(L, P, TOK_OPEN_PAREN, NULL, "expected parenthesis after __attribute__ or __declspec on line %d", P->line); - - for (;;) { - require_token(L, P, tok); - if (tok->type == TOK_OPEN_PAREN) { - parens++; - } else if (tok->type == TOK_CLOSE_PAREN) { - if (--parens == 0) { - break; - } - - } else if (tok->type != TOK_TOKEN) { - /* ignore unknown symbols within parentheses */ - - } else if (IS_LITERAL(*tok, "align") || IS_LITERAL(*tok, "aligned") || IS_LITERAL(*tok, "__aligned__")) { - unsigned align = 0; - require_token(L, P, tok); - - switch (tok->type) { - case TOK_CLOSE_PAREN: - align = ALIGNED_DEFAULT; - put_back(P); - break; - - case TOK_OPEN_PAREN: - require_token(L, P, tok); - - if (tok->type != TOK_NUMBER) { - luaL_error(L, "expected align(#) on line %d", P->line); - } - - switch (tok->integer) { - case 1: align = 0; break; - case 2: align = 1; break; - case 4: align = 3; break; - case 8: align = 7; break; - case 16: align = 15; break; - default: - luaL_error(L, "unsupported align size on line %d", P->line); - } - - check_token(L, P, TOK_CLOSE_PAREN, NULL, "expected align(#) on line %d", P->line); - break; - - default: - luaL_error(L, "expected align(#) on line %d", P->line); - } - - /* __attribute__(aligned(#)) is only supposed to increase alignment */ - ct->align_mask = max(align, ct->align_mask); - - } else if (IS_LITERAL(*tok, "packed") || IS_LITERAL(*tok, "__packed__")) { - ct->align_mask = 0; - ct->is_packed = 1; - - } else if (IS_LITERAL(*tok, "mode") || IS_LITERAL(*tok, "__mode__")) { - - check_token(L, P, TOK_OPEN_PAREN, NULL, "expected mode(MODE) on line %d", P->line); - - require_token(L, P, tok); - if (tok->type != TOK_TOKEN) { - luaL_error(L, "expected mode(MODE) on line %d", P->line); - } - - if (ct->type == FLOAT_TYPE || ct->type == DOUBLE_TYPE) { - struct {char ch; float v;} af; - struct {char ch; double v;} ad; - - if (IS_LITERAL(*tok, "SF") || IS_LITERAL(*tok, "__SF__")) { - ct->type = FLOAT_TYPE; - ct->base_size = sizeof(float); - ct->align_mask = ALIGNOF(af); - - } else if (IS_LITERAL(*tok, "DF") || IS_LITERAL(*tok, "__DF__")) { - ct->type = DOUBLE_TYPE; - ct->base_size = sizeof(double); - ct->align_mask = ALIGNOF(ad); - - } else { - luaL_error(L, "unexpected mode on line %d", P->line); - } - - } else { - struct {char ch; uint16_t v;} a16; - struct {char ch; uint32_t v;} a32; - struct {char ch; uint64_t v;} a64; - - if (IS_LITERAL(*tok, "QI") || IS_LITERAL(*tok, "__QI__") - || IS_LITERAL(*tok, "byte") || IS_LITERAL(*tok, "__byte__") - ) { - ct->type = INT8_TYPE; - ct->base_size = sizeof(uint8_t); - ct->align_mask = 0; - - } else if (IS_LITERAL(*tok, "HI") || IS_LITERAL(*tok, "__HI__")) { - ct->type = INT16_TYPE; - ct->base_size = sizeof(uint16_t); - ct->align_mask = ALIGNOF(a16); - - } else if (IS_LITERAL(*tok, "SI") || IS_LITERAL(*tok, "__SI__") -#if defined ARCH_X86 || defined ARCH_ARM - || IS_LITERAL(*tok, "word") || IS_LITERAL(*tok, "__word__") - || IS_LITERAL(*tok, "pointer") || IS_LITERAL(*tok, "__pointer__") -#endif - ) { - ct->type = INT32_TYPE; - ct->base_size = sizeof(uint32_t); - ct->align_mask = ALIGNOF(a32); - - } else if (IS_LITERAL(*tok, "DI") || IS_LITERAL(*tok, "__DI__") -#if defined ARCH_X64 || defined ARCH_PPC64 - || IS_LITERAL(*tok, "word") || IS_LITERAL(*tok, "__word__") - || IS_LITERAL(*tok, "pointer") || IS_LITERAL(*tok, "__pointer__") -#endif - ) { - ct->type = INT64_TYPE; - ct->base_size = sizeof(uint64_t); - ct->align_mask = ALIGNOF(a64); - - } else { - luaL_error(L, "unexpected mode on line %d", P->line); - } - } - - check_token(L, P, TOK_CLOSE_PAREN, NULL, "expected mode(MODE) on line %d", P->line); - - } else if (IS_LITERAL(*tok, "cdecl") || IS_LITERAL(*tok, "__cdecl__")) { - ct->calling_convention = C_CALL; - - } else if (IS_LITERAL(*tok, "fastcall") || IS_LITERAL(*tok, "__fastcall__")) { - ct->calling_convention = FAST_CALL; - - } else if (IS_LITERAL(*tok, "stdcall") || IS_LITERAL(*tok, "__stdcall__")) { - ct->calling_convention = STD_CALL; - } - /* ignore unknown tokens within parentheses */ - } - return 1; - - } else if (IS_LITERAL(*tok, "__cdecl")) { - ct->calling_convention = C_CALL; - return 1; - - } else if (IS_LITERAL(*tok, "__fastcall")) { - ct->calling_convention = FAST_CALL; - return 1; - - } else if (IS_LITERAL(*tok, "__stdcall")) { - ct->calling_convention = STD_CALL; - return 1; - - } else if (IS_LITERAL(*tok, "__extension__") || IS_LITERAL(*tok, "extern")) { - /* ignore */ - return 1; - - } else { - return 0; - } -} - -/* parses out the base type of a type expression in a function declaration, - * struct definition, typedef etc - * - * leaves the usr value of the type on the stack - */ -int parse_type(lua_State* L, struct parser* P, struct ctype* ct) -{ - struct token tok; - int top = lua_gettop(L); - - memset(ct, 0, sizeof(*ct)); - - require_token(L, P, &tok); - - /* get const/volatile before the base type */ - for (;;) { - if (tok.type != TOK_TOKEN) { - return luaL_error(L, "unexpected value before type name on line %d", P->line); - - } else if (IS_CONST(tok)) { - ct->const_mask = 1; - require_token(L, P, &tok); - - } else if (IS_VOLATILE(tok) || - IS_RESTRICT(tok) || - IS_LITERAL(tok, "static") || - IS_INLINE(tok)) { - /* ignored for now */ - require_token(L, P, &tok); - } else if (parse_attribute(L, P, &tok, ct, NULL)) { - /* get function attributes before the return type */ - require_token(L, P, &tok); - - } else { - break; - } - } - - /* get base type */ - if (tok.type != TOK_TOKEN) { - return luaL_error(L, "unexpected value before type name on line %d", P->line); - - } else if (IS_LITERAL(tok, "struct")) { - ct->type = STRUCT_TYPE; - parse_record(L, P, ct); - - } else if (IS_LITERAL(tok, "union")) { - ct->type = UNION_TYPE; - parse_record(L, P, ct); - - } else if (IS_LITERAL(tok, "enum")) { - ct->type = ENUM_TYPE; - parse_record(L, P, ct); - - } else { - put_back(P); - - /* lookup type */ - push_upval(L, &types_key); - parse_type_name(L, P); - lua_rawget(L, -2); - lua_remove(L, -2); - - if (lua_isnil(L, -1)) { - lua_pushlstring(L, tok.str, tok.size); - return luaL_error(L, "unknown type %s on line %d", lua_tostring(L, -1), P->line); - } - - instantiate_typedef(P, ct, (const struct ctype*) lua_touserdata(L, -1)); - - /* we only want the usr tbl from the ctype in the types tbl */ - lua_getuservalue(L, -1); - lua_replace(L, -2); - } - - while (next_token(L, P, &tok)) { - if (tok.type != TOK_TOKEN) { - put_back(P); - break; - - } else if (IS_CONST(tok) || IS_VOLATILE(tok)) { - /* ignore for now */ - - } else { - put_back(P); - break; - } - } - - assert(lua_gettop(L) == top + 1 && (lua_istable(L, -1) || lua_isnil(L, -1))); - return 0; -} - -enum name_type { - BOTH, - FRONT, - BACK, -}; - -static void append_type_name(luaL_Buffer* B, int usr, const struct ctype* ct, enum name_type type) -{ - size_t i; - lua_State* L = B->L; - - usr = lua_absindex(L, usr); - - if (type == FRONT || type == BOTH) { - if (ct->type != FUNCTION_PTR_TYPE && (ct->const_mask & (1 << ct->pointers))) { - luaL_addstring(B, "const "); - } - - if (ct->is_unsigned) { - luaL_addstring(B, "unsigned "); - } - - switch (ct->type) { - case ENUM_TYPE: - luaL_addstring(B, "enum "); - goto get_name; - - case STRUCT_TYPE: - luaL_addstring(B, "struct "); - goto get_name; - - case UNION_TYPE: - luaL_addstring(B, "union "); - goto get_name; - - get_name: - lua_pushlightuserdata(L, &g_name_key); - lua_rawget(L, usr); - luaL_addvalue(B); - break; - - case FUNCTION_TYPE: - case FUNCTION_PTR_TYPE: - lua_pushlightuserdata(L, &g_front_name_key); - lua_rawget(L, usr); - luaL_addvalue(B); - break; - - case VOID_TYPE: - luaL_addstring(B, "void"); - break; - case BOOL_TYPE: - luaL_addstring(B, "bool"); - break; - case DOUBLE_TYPE: - luaL_addstring(B, "double"); - break; - case LONG_DOUBLE_TYPE: - luaL_addstring(B, "long double"); - break; - case FLOAT_TYPE: - luaL_addstring(B, "float"); - break; - case COMPLEX_LONG_DOUBLE_TYPE: - luaL_addstring(B, "long complex double"); - break; - case COMPLEX_DOUBLE_TYPE: - luaL_addstring(B, "complex double"); - break; - case COMPLEX_FLOAT_TYPE: - luaL_addstring(B, "complex float"); - break; - case INT8_TYPE: - luaL_addstring(B, "char"); - break; - case INT16_TYPE: - luaL_addstring(B, "short"); - break; - case INT32_TYPE: - luaL_addstring(B, "int"); - break; - case INT64_TYPE: - luaL_addstring(B, "long long"); - break; - - case INTPTR_TYPE: - if (sizeof(intptr_t) == sizeof(int32_t)) { - luaL_addstring(B, "long"); - } else if (sizeof(intptr_t) == sizeof(int64_t)) { - luaL_addstring(B, "long long"); - } else { - luaL_error(L, "internal error - bad type"); - } - break; - - default: - luaL_error(L, "internal error - bad type %d", ct->type); - } - - for (i = 0; i < ct->pointers - ct->is_array; i++) { - luaL_addchar(B, '*'); - if (ct->const_mask & (1 << (ct->pointers - i - 1))) { - luaL_addstring(B, " const"); - } - } - } - - if (type == BOTH || type == BACK) { - if (ct->is_reference) { - luaL_addstring(B, " &"); - } - - if (ct->is_variable_array && !ct->variable_size_known) { - luaL_addstring(B, "[?]"); - } else if (ct->is_array) { - lua_pushfstring(L, "[%d]", (int) ct->array_size); - luaL_addvalue(B); - } - - if (ct->type == FUNCTION_PTR_TYPE || ct->type == FUNCTION_TYPE) { - lua_pushlightuserdata(L, &g_back_name_key); - lua_rawget(L, usr); - luaL_addvalue(B); - } - - if (ct->is_bitfield) { - lua_pushfstring(L, " : %d", (int) ct->bit_size); - luaL_addvalue(B); - } - } -} - -void push_type_name(lua_State* L, int usr, const struct ctype* ct) -{ - luaL_Buffer B; - usr = lua_absindex(L, usr); - luaL_buffinit(L, &B); - append_type_name(&B, usr, ct, BOTH); - luaL_pushresult(&B); -} - -static void push_function_type_strings(lua_State* L, int usr, const struct ctype* ct) -{ - size_t i, args; - luaL_Buffer B; - int top = lua_gettop(L); - const struct ctype* ret_ct; - - int arg_ct = top+3; - int arg_usr = top+4; - int ret_usr = top+6; - - usr = lua_absindex(L, usr); - - /* return type */ - lua_settop(L, top+4); /* room for two returns and two temp positions */ - lua_rawgeti(L, usr, 0); - lua_getuservalue(L, -1); - ret_ct = (const struct ctype*) lua_touserdata(L, -2); - - luaL_buffinit(L, &B); - append_type_name(&B, ret_usr, ret_ct, FRONT); - - if (ret_ct->type != FUNCTION_TYPE && ret_ct->type != FUNCTION_PTR_TYPE) { - luaL_addchar(&B, ' '); - } - - switch (ct->calling_convention) { - case STD_CALL: - luaL_addstring(&B, "(__stdcall *"); - break; - case FAST_CALL: - luaL_addstring(&B, "(__fastcall *"); - break; - case C_CALL: - luaL_addstring(&B, "(*"); - break; - default: - luaL_error(L, "internal error - unknown calling convention"); - } - - luaL_pushresult(&B); - lua_replace(L, top+1); - - luaL_buffinit(L, &B); - luaL_addstring(&B, ")("); - - /* arguments */ - args = lua_rawlen(L, usr); - for (i = 1; i <= args; i++) { - if (i > 1) { - luaL_addstring(&B, ", "); - } - - /* note push the arg and user value below the indexes used by the buffer - * and use indexes relative to top to avoid problems due to the buffer - * system pushing a variable number of arguments onto the stack */ - lua_rawgeti(L, usr, (int) i); - lua_replace(L, arg_ct); - lua_getuservalue(L, arg_ct); - lua_replace(L, arg_usr); - append_type_name(&B, arg_usr, (const struct ctype*) lua_touserdata(L, arg_ct), BOTH); - } - - luaL_addstring(&B, ")"); - append_type_name(&B, ret_usr, ret_ct, BACK); - luaL_pushresult(&B); - lua_replace(L, top+2); - - lua_settop(L, top+2); - assert(lua_isstring(L, top+1) && lua_isstring(L, top+2)); -} - -/* parses from after the opening paranthesis to after the closing parenthesis */ -static void parse_function_arguments(lua_State* L, struct parser* P, int ct_usr, struct ctype* ct) -{ - struct token tok; - int args = 0; - int top = lua_gettop(L); - - ct_usr = lua_absindex(L, ct_usr); - - for (;;) { - require_token(L, P, &tok); - - if (tok.type == TOK_CLOSE_PAREN) { - break; - } - - if (args) { - if (tok.type != TOK_COMMA) { - luaL_error(L, "unexpected token in function argument %d on line %d", args, P->line); - } - - require_token(L, P, &tok); - } - - if (tok.type == TOK_VA_ARG) { - ct->has_var_arg = true; - check_token(L, P, TOK_CLOSE_PAREN, "", "unexpected token after ... in function on line %d", P->line); - break; - - } else if (tok.type == TOK_TOKEN) { - struct ctype at; - - put_back(P); - parse_type(L, P, &at); - parse_argument(L, P, -1, &at, NULL, NULL); - - assert(lua_gettop(L) == top + 2); - - /* array arguments are just treated as their base pointer type */ - at.is_array = 0; - - /* check for the c style int func(void) and error on other uses of arguments of type void */ - if (at.type == VOID_TYPE && at.pointers == 0) { - if (args) { - luaL_error(L, "can't have argument of type void on line %d", P->line); - } - - check_token(L, P, TOK_CLOSE_PAREN, "", "unexpected void in function on line %d", P->line); - lua_pop(L, 2); - break; - } - - push_ctype(L, -1, &at); - lua_rawseti(L, ct_usr, ++args); - - lua_pop(L, 2); /* parse_type and parse_argument at_usr */ - - } else { - luaL_error(L, "unexpected token in function argument %d on line %d", args+1, P->line); - } - } - - assert(lua_gettop(L) == top); -} - -static int max_bitfield_size(int type) -{ - switch (type) { - case BOOL_TYPE: - return 1; - case INT8_TYPE: - return 8; - case INT16_TYPE: - return 16; - case INT32_TYPE: - case ENUM_TYPE: - return 32; - case INT64_TYPE: - return 64; - default: - return -1; - } -} - -static struct ctype* parse_argument2(lua_State* L, struct parser* P, int ct_usr, struct ctype* ct, struct token* name, struct parser* asmname); - -/* parses from after the first ( in a function declaration or function pointer - * can be one of: - * void foo(...) before ... - * void (foo)(...) before foo - * void (* <>)(...) before <> which is the inner type - */ -static struct ctype* parse_function(lua_State* L, struct parser* P, int ct_usr, struct ctype* ct, struct token* name, struct parser* asmname) -{ - /* We have a function pointer or a function. The usr table will - * get replaced by the canonical one (if there is one) in - * find_canonical_usr after all the arguments and returns have - * been parsed. */ - struct token tok; - int top = lua_gettop(L); - struct ctype* ret; - - lua_newtable(L); - ret = push_ctype(L, ct_usr, ct); - lua_rawseti(L, -2, 0); - ct_usr = lua_gettop(L); - - memset(ct, 0, sizeof(*ct)); - ct->base_size = sizeof(void (*)()); - ct->align_mask = min(FUNCTION_ALIGN_MASK, P->align_mask); - ct->type = FUNCTION_TYPE; - ct->is_defined = 1; - - if (name->type == TOK_NIL) { - for (;;) { - require_token(L, P, &tok); - - if (tok.type == TOK_STAR) { - - if (ct->type == FUNCTION_TYPE) { - ct->type = FUNCTION_PTR_TYPE; - } else if (ct->pointers == POINTER_MAX) { - luaL_error(L, "maximum number of pointer derefs reached - use a struct to break up the pointers on line %d", P->line); - } else { - ct->pointers++; - ct->const_mask <<= 1; - } - - } else if (parse_attribute(L, P, &tok, ct, asmname)) { - /* parse_attribute sets the appropriate fields */ - - } else { - /* call parse_argument to handle the inner contents - * e.g. the <> in "void (* <>) (...)". Note that the - * inner contents can itself be a function, a function - * ptr, array, etc (e.g. "void (*signal(int sig, void - * (*func)(int)))(int)" ). - */ - put_back(P); - ct = parse_argument2(L, P, ct_usr, ct, name, asmname); - break; - } - } - - check_token(L, P, TOK_CLOSE_PAREN, NULL, "unexpected token in function on line %d", P->line); - check_token(L, P, TOK_OPEN_PAREN, NULL, "unexpected token in function on line %d", P->line); - } - - parse_function_arguments(L, P, ct_usr, ct); - - /* if we have an inner function then set the outer function ptr as its - * return type and return the inner function - * e.g. for void (* )(int) inner is - * surrounded by <>, return type is void (*)(int) - */ - if (lua_gettop(L) == ct_usr+1) { - lua_replace(L, ct_usr); - } - - assert(lua_gettop(L) == top + 1 && lua_istable(L, -1)); - return ret; -} - -static struct ctype* parse_argument2(lua_State* L, struct parser* P, int ct_usr, struct ctype* ct, struct token* name, struct parser* asmname) -{ - struct token tok; - int top = lua_gettop(L); - int ft_usr = 0; - - luaL_checkstack(L, 10, "function too complex"); - ct_usr = lua_absindex(L, ct_usr); - - for (;;) { - if (!next_token(L, P, &tok)) { - /* we've reached the end of the string */ - break; - - } else if (tok.type == TOK_STAR) { - if (ct->pointers == POINTER_MAX) { - luaL_error(L, "maximum number of pointer derefs reached - use a struct to break up the pointers on line %d", P->line); - } - - ct->pointers++; - ct->const_mask <<= 1; - - /* __declspec(align(#)) may come before the type in a member */ - if (!ct->is_packed) { - ct->align_mask = max(min(PTR_ALIGN_MASK, P->align_mask), ct->align_mask); - } - - } else if (tok.type == TOK_REFERENCE) { - ct->is_reference = 1; - - } else if (parse_attribute(L, P, &tok, ct, asmname)) { - /* parse attribute has filled out appropriate fields in type */ - - } else if (tok.type == TOK_OPEN_PAREN) { - ct = parse_function(L, P, ct_usr, ct, name, asmname); - ft_usr = lua_gettop(L); - - } else if (tok.type == TOK_OPEN_SQUARE) { - /* array */ - if (ct->pointers == POINTER_MAX) { - luaL_error(L, "maximum number of pointer derefs reached - use a struct to break up the pointers"); - } - ct->is_array = 1; - ct->pointers++; - ct->const_mask <<= 1; - require_token(L, P, &tok); - - if (ct->pointers == 1 && !ct->is_defined) { - luaL_error(L, "array of undefined type on line %d", P->line); - } - - if (ct->is_variable_struct || ct->is_variable_array) { - luaL_error(L, "can't have an array of a variably sized type on line %d", P->line); - } - - if (tok.type == TOK_QUESTION) { - ct->is_variable_array = 1; - ct->variable_increment = (ct->pointers > 1) ? sizeof(void*) : ct->base_size; - check_token(L, P, TOK_CLOSE_SQUARE, "", "invalid character in array on line %d", P->line); - - } else if (tok.type == TOK_CLOSE_SQUARE) { - ct->array_size = 0; - - } else if (tok.type == TOK_TOKEN && IS_RESTRICT(tok)) { - /* odd gcc extension foo[__restrict] for arguments */ - ct->array_size = 0; - check_token(L, P, TOK_CLOSE_SQUARE, "", "invalid character in array on line %d", P->line); - - } else { - int64_t asize; - put_back(P); - asize = calculate_constant(L, P); - if (asize < 0) { - luaL_error(L, "array size can not be negative on line %d", P->line); - } - ct->array_size = (size_t) asize; - check_token(L, P, TOK_CLOSE_SQUARE, "", "invalid character in array on line %d", P->line); - } - - } else if (tok.type == TOK_COLON) { - int64_t bsize = calculate_constant(L, P); - - if (ct->pointers || bsize < 0 || bsize > max_bitfield_size(ct->type)) { - luaL_error(L, "invalid bitfield on line %d", P->line); - } - - ct->is_bitfield = 1; - ct->bit_size = (unsigned) bsize; - - } else if (tok.type != TOK_TOKEN) { - /* we've reached the end of the declaration */ - put_back(P); - break; - - } else if (IS_CONST(tok)) { - ct->const_mask |= 1; - - } else if (IS_VOLATILE(tok) || IS_RESTRICT(tok)) { - /* ignored for now */ - - } else { - *name = tok; - } - } - - assert((ft_usr == 0 && lua_gettop(L) == top) || (lua_gettop(L) == top + 1 && ft_usr == top + 1 && (lua_istable(L, -1) || lua_isnil(L, -1)))); - return ct; -} - -static void find_canonical_usr(lua_State* L, int ct_usr, const struct ctype *ct) -{ - struct ctype rt; - int top = lua_gettop(L); - int types; - - if (ct->type != FUNCTION_PTR_TYPE && ct->type != FUNCTION_TYPE) { - return; - } - - luaL_checkstack(L, 10, "function too complex"); - ct_usr = lua_absindex(L, ct_usr); - - /* check to see if we already have the canonical usr table */ - lua_pushlightuserdata(L, &g_name_key); - lua_rawget(L, ct_usr); - if (!lua_isnil(L, -1)) { - lua_pop(L, 1); - assert(top == lua_gettop(L)); - return; - } - lua_pop(L, 1); - - assert(top == lua_gettop(L)); - - /* first canonize the return type */ - lua_rawgeti(L, ct_usr, 0); - rt = *(struct ctype*) lua_touserdata(L, -1); - lua_getuservalue(L, -1); - find_canonical_usr(L, -1, &rt); - push_ctype(L, -1, &rt); - lua_rawseti(L, ct_usr, 0); - lua_pop(L, 2); /* return ctype and usr */ - - assert(top == lua_gettop(L)); - - /* look up the type string in the types table */ - push_upval(L, &types_key); - types = lua_gettop(L); - - push_function_type_strings(L, ct_usr, ct); - lua_pushvalue(L, -2); - lua_pushvalue(L, -2); - lua_concat(L, 2); - - lua_pushvalue(L, -1); - lua_rawget(L, types); - - assert(lua_gettop(L) == types + 4 && types == top + 1); - /* stack: types, front, back, both, looked up value */ - - if (lua_isnil(L, -1)) { - lua_pop(L, 1); - - lua_pushlightuserdata(L, &g_front_name_key); - lua_pushvalue(L, -4); - lua_rawset(L, ct_usr); - - lua_pushlightuserdata(L, &g_back_name_key); - lua_pushvalue(L, -3); - lua_rawset(L, ct_usr); - - lua_pushlightuserdata(L, &g_name_key); - lua_pushvalue(L, -2); - lua_rawset(L, ct_usr); - - lua_pushvalue(L, -1); - push_ctype(L, ct_usr, ct); - lua_rawset(L, types); - } else { - lua_getuservalue(L, -1); - lua_replace(L, ct_usr); - lua_pop(L, 1); - } - - lua_pop(L, 4); - assert(top == lua_gettop(L) && types == top + 1); -} - - -/* parses after the main base type of a typedef, function argument or - * struct/union member - * eg for const void* bar[3] the base type is void with the subtype so far of - * const, this parses the "* bar[3]" and updates the type argument - * - * ct_usr and type must be as filled out by parse_type - * - * pushes the updated user value on the top of the stack - */ -void parse_argument(lua_State* L, struct parser* P, int ct_usr, struct ctype* ct, struct token* pname, struct parser* asmname) -{ - struct token tok, name; - int top = lua_gettop(L); - - memset(&name, 0, sizeof(name)); - parse_argument2(L, P, ct_usr, ct, &name, asmname); - - for (;;) { - if (!next_token(L, P, &tok)) { - break; - } else if (parse_attribute(L, P, &tok, ct, asmname)) { - /* parse_attribute sets the appropriate fields */ - } else { - put_back(P); - break; - } - } - - if (lua_gettop(L) == top) { - lua_pushvalue(L, ct_usr); - } - - find_canonical_usr(L, -1, ct); - - if (pname) { - *pname = name; - } -} - -static void parse_typedef(lua_State* L, struct parser* P) -{ - struct token tok; - struct ctype base_type; - int top = lua_gettop(L); - - parse_type(L, P, &base_type); - - for (;;) { - struct ctype arg_type = base_type; - struct token name; - - memset(&name, 0, sizeof(name)); - - assert(lua_gettop(L) == top + 1); - parse_argument(L, P, -1, &arg_type, &name, NULL); - assert(lua_gettop(L) == top + 2); - - if (!name.size) { - luaL_error(L, "Can't have a typedef without a name on line %d", P->line); - } else if (arg_type.is_variable_array) { - luaL_error(L, "Can't typedef a variable length array on line %d", P->line); - } - - push_upval(L, &types_key); - lua_pushlstring(L, name.str, name.size); - push_ctype(L, -3, &arg_type); - lua_rawset(L, -3); - lua_pop(L, 2); /* types and parse_argument usr tbl */ - - require_token(L, P, &tok); - - if (tok.type == TOK_SEMICOLON) { - break; - } else if (tok.type != TOK_COMMA) { - luaL_error(L, "Unexpected character in typedef on line %d", P->line); - } - } - - lua_pop(L, 1); /* parse_type usr tbl */ - assert(lua_gettop(L) == top); -} - -static bool is_hex(char ch) -{ return ('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F'); } - -static bool is_digit(char ch) -{ return '0' <= ch && ch <= '9'; } - -static int from_hex(char ch) -{ - if (ch >= 'a') { - return ch - 'a' + 10; - } else if (ch >= 'A') { - return ch - 'A' + 10; - } else { - return ch - '0'; - } -} - -static void push_strings(lua_State* L, struct parser* P) -{ - luaL_Buffer B; - luaL_buffinit(L, &B); - - for (;;) { - const char *p, *e; - char *t, *s; - struct token tok; - - require_token(L, P, &tok); - if (tok.type != TOK_STRING) { - break; - } - - p = tok.str; - e = p + tok.size; - - t = luaL_prepbuffsize(&B, tok.size); - s = t; - - while (p < e) { - if (*p == '\\') { - if (++p == e) { - luaL_error(L, "parse error in string"); - } - switch (*p) { - case '\\': *(t++) = '\\'; p++; break; - case '\"': *(t++) = '\"'; p++; break; - case '\'': *(t++) = '\''; p++; break; - case 'n': *(t++) = '\n'; p++; break; - case 'r': *(t++) = '\r'; p++; break; - case 'b': *(t++) = '\b'; p++; break; - case 't': *(t++) = '\t'; p++; break; - case 'f': *(t++) = '\f'; p++; break; - case 'a': *(t++) = '\a'; p++; break; - case 'v': *(t++) = '\v'; p++; break; - case 'e': *(t++) = 0x1B; p++; break; - case 'x': - { - uint8_t u; - p++; - if (p + 2 > e || !is_hex(p[0]) || !is_hex(p[1])) { - luaL_error(L, "parse error in string"); - } - u = (from_hex(p[0]) << 4) | from_hex(p[1]); - *(t++) = *(char*) &u; - p += 2; - break; - } - default: - { - uint8_t u; - const char* e2 = min(p + 3, e); - if (!is_digit(*p)) { - luaL_error(L, "parse error in string"); - } - u = *p - '0'; - p++; - while (is_digit(*p) && p < e2) { - u = 10*u + *p-'0'; - p++; - } - *(t++) = *(char*) &u; - break; - } - } - } else { - *(t++) = *(p++); - } - } - - luaL_addsize(&B, t-s); - } - - luaL_pushresult(&B); -} - -static void parse_constant_assignemnt(lua_State* L, - struct parser* P, - const struct ctype* type, - const struct token* name) -{ - int64_t val = calculate_constant(L, P); - - check_token(L, P, TOK_SEMICOLON, "", "expected ; after constant definition on line %d", P->line); - - push_upval(L, &constants_key); - lua_pushlstring(L, name->str, name->size); - - switch (type->type) { - case INT8_TYPE: - case INT16_TYPE: - case INT32_TYPE: - if (type->is_unsigned) - lua_pushinteger(L, (unsigned int) val); - else - lua_pushinteger(L, (int) val); - break; - - default: - luaL_error(L, "expected a valid 8-, 16-, or 32-bit signed or unsigned integer type after 'static const' on line %d", P->line); - } - - lua_rawset(L, -3); - lua_pop(L, 2); /*constants and type*/ -} - -#define END 0 -#define PRAGMA_POP 1 - -static int parse_root(lua_State* L, struct parser* P) -{ - int top = lua_gettop(L); - struct token tok; - - while (next_token(L, P, &tok)) { - /* we can have: - * struct definition - * enum definition - * union definition - * struct/enum/union declaration - * typedef - * function declaration - * pragma pack - */ - - assert(lua_gettop(L) == top); - - if (tok.type == TOK_SEMICOLON) { - /* empty semicolon in root continue on */ - - } else if (tok.type == TOK_POUND) { - - check_token(L, P, TOK_TOKEN, "pragma", "unexpected pre processor directive on line %d", P->line); - check_token(L, P, TOK_TOKEN, "pack", "unexpected pre processor directive on line %d", P->line); - check_token(L, P, TOK_OPEN_PAREN, "", "invalid pack directive on line %d", P->line); - - require_token(L, P, &tok); - - if (tok.type == TOK_NUMBER) { - if (tok.integer != 1 && tok.integer != 2 && tok.integer != 4 && tok.integer != 8 && tok.integer != 16) { - luaL_error(L, "pack directive with invalid pack size on line %d", P->line); - } - - P->align_mask = (unsigned) (tok.integer - 1); - check_token(L, P, TOK_CLOSE_PAREN, "", "invalid pack directive on line %d", P->line); - - } else if (tok.type == TOK_TOKEN && IS_LITERAL(tok, "push")) { - int line = P->line; - unsigned previous_alignment = P->align_mask; - - check_token(L, P, TOK_CLOSE_PAREN, "", "invalid pack directive on line %d", P->line); - - if (parse_root(L, P) != PRAGMA_POP) { - luaL_error(L, "reached end of string without a pragma pop to match the push on line %d", line); - } - - P->align_mask = previous_alignment; - - } else if (tok.type == TOK_TOKEN && IS_LITERAL(tok, "pop")) { - check_token(L, P, TOK_CLOSE_PAREN, "", "invalid pack directive on line %d", P->line); - return PRAGMA_POP; - - } else { - luaL_error(L, "invalid pack directive on line %d", P->line); - } - - - } else if (tok.type != TOK_TOKEN) { - return luaL_error(L, "unexpected character on line %d", P->line); - - } else if (IS_LITERAL(tok, "__extension__")) { - /* ignore */ - continue; - - } else if (IS_LITERAL(tok, "extern")) { - /* ignore extern as data and functions can only be extern */ - continue; - - } else if (IS_LITERAL(tok, "typedef")) { - parse_typedef(L, P); - - } else { - /* type declaration, type definition, or function declaration */ - struct ctype type; - struct token name; - struct parser asmname; - - memset(&name, 0, sizeof(name)); - memset(&asmname, 0, sizeof(asmname)); - - put_back(P); - parse_type(L, P, &type); - - for (;;) { - parse_argument(L, P, -1, &type, &name, &asmname); - - if (name.size) { - /* global/function declaration */ - - /* set asmname_tbl[name] = asmname */ - if (asmname.next) { - push_upval(L, &asmname_key); - lua_pushlstring(L, name.str, name.size); - push_strings(L, &asmname); - lua_rawset(L, -3); - lua_pop(L, 1); /* asmname upval */ - } - - push_upval(L, &functions_key); - lua_pushlstring(L, name.str, name.size); - push_ctype(L, -3, &type); - lua_rawset(L, -3); - lua_pop(L, 1); /* functions upval */ - } else { - /* type declaration/definition - already been processed */ - } - - lua_pop(L, 1); - - if (!next_token(L, P, &tok)) { - break; - } - - if (tok.type == TOK_COMMA) { - continue; - } - - if (tok.type == TOK_OPEN_CURLY) { - int line = P->line; - int remaining = 1; - while (remaining > 0 && next_token(L, P, &tok)) { - if (tok.type == TOK_CLOSE_CURLY) { - remaining--; - } else if (tok.type == TOK_OPEN_CURLY) { - remaining++; - } - } - if (remaining > 0) { - luaL_error(L, "missing closing bracket for line %d", line); - } - } else if (tok.type == TOK_ASSIGN) { - parse_constant_assignemnt(L, P, &type, &name); - } else if (tok.type != TOK_SEMICOLON) { - luaL_error(L, "missing semicolon on line %d", P->line); - } - break; - } - - lua_pop(L, 1); - } - } - - return END; -} - -int ffi_cdef(lua_State* L) -{ - struct parser P; - - P.line = 1; - P.prev = P.next = luaL_checkstring(L, 1); - P.align_mask = DEFAULT_ALIGN_MASK; - - if (parse_root(L, &P) == PRAGMA_POP) { - luaL_error(L, "pragma pop without an associated push on line %d", P.line); - } - - return 0; -} - -/* calculate_constant handles operator precedence by having a number of - * recursive commands each of which computes the result at that level of - * precedence and above. calculate_constant1 is the highest precedence - */ - -static int64_t string_to_int(const char* str, size_t size) -{ - const char* end = str + size; - char c = *str++; - if (str < end) - { - if (c == '\\') { - c = *str++; - switch (c) { - case '\'': c = '\''; break; - case '\"': c = '\"'; break; - case '\?': c = '\?'; break; - case '\\': c = '\\'; break; - case 'a': c = '\a'; break; - case 'b': c = '\b'; break; - case 'f': c = '\f'; break; - case 'n': c = '\n'; break; - case 'r': c = '\r'; break; - case 't': c = '\t'; break; - case 'v': c = '\v'; break; - case 'e': c = 27; break; - case 'x': - c = 0; - while (str < end) { - char d = *str++; - c *= 16; - if (d >= '0' && d <= '9') c += (d - '0'); - else c += (d - 'a' + 10); - } - break; - default: - c = c - '0'; - while (str < end) { - char d = *str++; - c = c*8 + (d - '0'); - } - break; - } - } - } - return c; -} - -static int try_cast(lua_State* L) -{ - struct parser* P = (struct parser*) lua_touserdata(L, 1); - struct ctype ct; - struct token name, tok; - memset(&name, 0, sizeof(name)); - - parse_type(L, P, &ct); - parse_argument(L, P, -1, &ct, &name, NULL); - - require_token(L, P, &tok); - if (tok.type != TOK_CLOSE_PAREN || name.size) { - return luaL_error(L, "invalid cast"); - } - - if (ct.pointers/* || ct.type != INT32_TYPE*/) { - return luaL_error(L, "unsupported cast on line %d", P->line); - } - - return 0; -} - -static int64_t calculate_constant2(lua_State* L, struct parser* P, struct token* tok); - -/* () */ -static int64_t calculate_constant1(lua_State* L, struct parser* P, struct token* tok) -{ - int64_t ret; - - if (tok->type == TOK_NUMBER) { - ret = tok->integer; - next_token(L, P, tok); - return ret; - - } else if (tok->type == TOK_TOKEN) { - /* look up name in constants table */ - push_upval(L, &constants_key); - lua_pushlstring(L, tok->str, tok->size); - lua_rawget(L, -2); - lua_remove(L, -2); /* constants table */ - - if (!lua_isnumber(L, -1)) { - lua_pushlstring(L, tok->str, tok->size); - luaL_error(L, "use of undefined constant %s on line %d", lua_tostring(L, -1), P->line); - } - - ret = (int64_t) lua_tonumber(L, -1); - lua_pop(L, 1); - next_token(L, P, tok); - return ret; - - } else if (tok->type == TOK_OPEN_PAREN) { - struct parser before_cast = *P; - int top = lua_gettop(L); - - /* see if this is a numeric cast, which we ignore */ - lua_pushcfunction(L, &try_cast); - lua_pushlightuserdata(L, P); - if (!lua_pcall(L, 1, 0, 0)) { - next_token(L, P, tok); - return calculate_constant2(L, P, tok); - } - lua_settop(L, top); - - *P = before_cast; - ret = calculate_constant(L, P); - - require_token(L, P, tok); - if (tok->type != TOK_CLOSE_PAREN) { - luaL_error(L, "error whilst parsing constant at line %d", P->line); - } - - next_token(L, P, tok); - return ret; - - } else if (tok->type == TOK_STRING) { - ret = string_to_int(tok->str, tok->size); - - next_token(L, P, tok); - return ret; - - } else { - return luaL_error(L, "unexpected token whilst parsing constant at line %d", P->line); - } -} - -/* ! and ~, unary + and -, and sizeof */ -static int64_t calculate_constant2(lua_State* L, struct parser* P, struct token* tok) -{ - if (tok->type == TOK_LOGICAL_NOT) { - require_token(L, P, tok); - return !calculate_constant2(L, P, tok); - - } else if (tok->type == TOK_BITWISE_NOT) { - require_token(L, P, tok); - return ~calculate_constant2(L, P, tok); - - } else if (tok->type == TOK_PLUS) { - require_token(L, P, tok); - return calculate_constant2(L, P, tok); - - } else if (tok->type == TOK_MINUS) { - require_token(L, P, tok); - return -calculate_constant2(L, P, tok); - - } else if (tok->type == TOK_TOKEN && - (IS_LITERAL(*tok, "sizeof") - || IS_LITERAL(*tok, "alignof") - || IS_LITERAL(*tok, "__alignof__") - || IS_LITERAL(*tok, "__alignof"))) { - - bool issize = IS_LITERAL(*tok, "sizeof"); - struct ctype type; - - require_token(L, P, tok); - if (tok->type != TOK_OPEN_PAREN) { - luaL_error(L, "invalid sizeof at line %d", P->line); - } - - parse_type(L, P, &type); - parse_argument(L, P, -1, &type, NULL, NULL); - lua_pop(L, 2); - - require_token(L, P, tok); - if (tok->type != TOK_CLOSE_PAREN) { - luaL_error(L, "invalid sizeof at line %d", P->line); - } - - next_token(L, P, tok); - - return issize ? ctype_size(L, &type) : type.align_mask + 1; - - } else { - return calculate_constant1(L, P, tok); - } -} - -/* binary * / and % (left associative) */ -static int64_t calculate_constant3(lua_State* L, struct parser* P, struct token* tok) -{ - int64_t left = calculate_constant2(L, P, tok); - - for (;;) { - if (tok->type == TOK_MULTIPLY) { - require_token(L, P, tok); - left *= calculate_constant2(L, P, tok); - - } else if (tok->type == TOK_DIVIDE) { - require_token(L, P, tok); - left /= calculate_constant2(L, P, tok); - - } else if (tok->type == TOK_MODULUS) { - require_token(L, P, tok); - left %= calculate_constant2(L, P, tok); - - } else { - return left; - } - } -} - -/* binary + and - (left associative) */ -static int64_t calculate_constant4(lua_State* L, struct parser* P, struct token* tok) -{ - int64_t left = calculate_constant3(L, P, tok); - - for (;;) { - if (tok->type == TOK_PLUS) { - require_token(L, P, tok); - left += calculate_constant3(L, P, tok); - - } else if (tok->type == TOK_MINUS) { - require_token(L, P, tok); - left -= calculate_constant3(L, P, tok); - - } else { - return left; - } - } -} - -/* binary << and >> (left associative) */ -static int64_t calculate_constant5(lua_State* L, struct parser* P, struct token* tok) -{ - int64_t left = calculate_constant4(L, P, tok); - - for (;;) { - if (tok->type == TOK_LEFT_SHIFT) { - require_token(L, P, tok); - left <<= calculate_constant4(L, P, tok); - - } else if (tok->type == TOK_RIGHT_SHIFT) { - require_token(L, P, tok); - left >>= calculate_constant4(L, P, tok); - - } else { - return left; - } - } -} - -/* binary <, <=, >, and >= (left associative) */ -static int64_t calculate_constant6(lua_State* L, struct parser* P, struct token* tok) -{ - int64_t left = calculate_constant5(L, P, tok); - - for (;;) { - if (tok->type == TOK_LESS) { - require_token(L, P, tok); - left = (left < calculate_constant5(L, P, tok)); - - } else if (tok->type == TOK_LESS_EQUAL) { - require_token(L, P, tok); - left = (left <= calculate_constant5(L, P, tok)); - - } else if (tok->type == TOK_GREATER) { - require_token(L, P, tok); - left = (left > calculate_constant5(L, P, tok)); - - } else if (tok->type == TOK_GREATER_EQUAL) { - require_token(L, P, tok); - left = (left >= calculate_constant5(L, P, tok)); - - } else { - return left; - } - } -} - -/* binary ==, != (left associative) */ -static int64_t calculate_constant7(lua_State* L, struct parser* P, struct token* tok) -{ - int64_t left = calculate_constant6(L, P, tok); - - for (;;) { - if (tok->type == TOK_EQUAL) { - require_token(L, P, tok); - left = (left == calculate_constant6(L, P, tok)); - - } else if (tok->type == TOK_NOT_EQUAL) { - require_token(L, P, tok); - left = (left != calculate_constant6(L, P, tok)); - - } else { - return left; - } - } -} - -/* binary & (left associative) */ -static int64_t calculate_constant8(lua_State* L, struct parser* P, struct token* tok) -{ - int64_t left = calculate_constant7(L, P, tok); - - for (;;) { - if (tok->type == TOK_BITWISE_AND) { - require_token(L, P, tok); - left = (left & calculate_constant7(L, P, tok)); - - } else { - return left; - } - } -} - -/* binary ^ (left associative) */ -static int64_t calculate_constant9(lua_State* L, struct parser* P, struct token* tok) -{ - int64_t left = calculate_constant8(L, P, tok); - - for (;;) { - if (tok->type == TOK_BITWISE_XOR) { - require_token(L, P, tok); - left = (left ^ calculate_constant8(L, P, tok)); - - } else { - return left; - } - } -} - -/* binary | (left associative) */ -static int64_t calculate_constant10(lua_State* L, struct parser* P, struct token* tok) -{ - int64_t left = calculate_constant9(L, P, tok); - - for (;;) { - if (tok->type == TOK_BITWISE_OR) { - require_token(L, P, tok); - left = (left | calculate_constant9(L, P, tok)); - - } else { - return left; - } - } -} - -/* binary && (left associative) */ -static int64_t calculate_constant11(lua_State* L, struct parser* P, struct token* tok) -{ - int64_t left = calculate_constant10(L, P, tok); - - for (;;) { - if (tok->type == TOK_LOGICAL_AND) { - require_token(L, P, tok); - left = (left && calculate_constant10(L, P, tok)); - - } else { - return left; - } - } -} - -/* binary || (left associative) */ -static int64_t calculate_constant12(lua_State* L, struct parser* P, struct token* tok) -{ - int64_t left = calculate_constant11(L, P, tok); - - for (;;) { - if (tok->type == TOK_LOGICAL_OR) { - require_token(L, P, tok); - left = (left || calculate_constant11(L, P, tok)); - - } else { - return left; - } - } -} - -/* ternary ?: (right associative) */ -static int64_t calculate_constant13(lua_State* L, struct parser* P, struct token* tok) -{ - int64_t left = calculate_constant12(L, P, tok); - - if (tok->type == TOK_QUESTION) { - int64_t middle, right; - require_token(L, P, tok); - middle = calculate_constant13(L, P, tok); - if (tok->type != TOK_COLON) { - luaL_error(L, "invalid ternery (? :) in constant on line %d", P->line); - } - require_token(L, P, tok); - right = calculate_constant13(L, P, tok); - return left ? middle : right; - - } else { - return left; - } -} - -int64_t calculate_constant(lua_State* L, struct parser* P) -{ - struct token tok; - int64_t ret; - require_token(L, P, &tok); - ret = calculate_constant13(L, P, &tok); - - if (tok.type != TOK_NIL) { - put_back(P); - } - - return ret; -} diff --git a/script/test_ffi.lua b/script/test_ffi.lua deleted file mode 100644 index f48c8123..00000000 --- a/script/test_ffi.lua +++ /dev/null @@ -1,37 +0,0 @@ -local Log = require "logging":new() -local ffi = require "lffi" - --- 数据类型长度测试 -Log:DEBUG("uint8_t长度为:"..ffi.sizeof(ffi.new("uint8_t"))) -Log:DEBUG("uint16_t长度为:"..ffi.sizeof(ffi.new("uint16_t"))) -Log:DEBUG("uint32_t长度为:"..ffi.sizeof(ffi.new("uint32_t"))) -Log:DEBUG("uint64_t长度为:"..ffi.sizeof(ffi.new("uint64_t"))) - --- 字符串测试 -local cdata = ffi.new("char [?]", #"admin", "admin") -Log:DEBUG("将lua字符串转换为cdata:", cdata) -local str = ffi.string(cdata) -Log:DEBUG("将cdata转换为lua字符串:", str) - -Log:DEBUG("测试cdata字符串类型是否可以索引:", cdata[0], cdata[1], cdata[2], cdata[3], cdata[4]) -Log:DEBUG("测试cdata字符串类型是否可以转换:", string.char(cdata[0])..string.char(cdata[1])..string.char(cdata[2])..string.char(cdata[3])..string.char(cdata[4])) - --- 整型数组测试 -local array = ffi.new("int[?]", 3, 1, 2, 3) -- 初始化方法 1 -local array = ffi.new("int[3]", 1, 2, 3) -- 初始化方法 2 -Log:DEBUG(array[0], array[1], array[2]) - --- 结构体创建测试 -ffi.cdef [[ - typedef struct cuboid { uint8_t h, w, l; } cuboid_t; -]] - -local cuboid = ffi.new("cuboid_t", 2 ^ 4, 2 ^ 5, 2 ^ 6) -Log:DEBUG("创建长方体", cuboid, cuboid.h, cuboid.w, cuboid.l) -Log:DEBUG("计算体积", cuboid.h * cuboid.w * cuboid.l) - -local cuboid_array = ffi.new("cuboid_t[3]", {{11, 12, 13}, {21, 22, 23}, {31, 32, 33}}) -Log:DEBUG("创建3个长方体并且初始化", cuboid_array) -Log:DEBUG("3个长方体的长度分别为:", cuboid_array[0].l, cuboid_array[1].l, cuboid_array[2].l) -Log:DEBUG("3个长方体的宽度分别为:", cuboid_array[0].w, cuboid_array[1].w, cuboid_array[2].w) -Log:DEBUG("3个长方体的高度分别为:", cuboid_array[0].h, cuboid_array[1].h, cuboid_array[2].h) From a178da903aa9bbb630f2718f334f2118035b88dd Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 12 Oct 2021 20:27:31 +0800 Subject: [PATCH 845/956] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E6=9C=AA=E5=BC=95?= =?UTF-8?q?=E7=94=A8=E5=8F=AF=E8=83=BD=E5=AF=BC=E8=87=B4=E7=9A=84=E5=B4=A9?= =?UTF-8?q?=E6=BA=83=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/TCP.lua | 6 ++++++ lualib/internal/UDP.lua | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index 8643b544..47d949b3 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -65,6 +65,10 @@ local tcp_ssl_set_privatekey = tcp.ssl_set_privatekey local tcp_ssl_set_certificate = tcp.ssl_set_certificate local tcp_ssl_set_userdata_key = tcp.ssl_set_userdata_key +local G_Reference = {} +local tab = debug.getregistry() +tab['__G_TCP__'] = G_Reference + local EVENT_READ = 0x01 local EVENT_WRITE = 0x02 @@ -106,6 +110,7 @@ function TCP:ctor(...) -- 配套密码 self.ssl_password = nil --]] + G_Reference[self] = true end -- 超时时间 @@ -870,6 +875,7 @@ function TCP:close() self.sfd = nil end + G_Reference[self] = nil end return TCP \ No newline at end of file diff --git a/lualib/internal/UDP.lua b/lualib/internal/UDP.lua index b0604245..d054acc2 100644 --- a/lualib/internal/UDP.lua +++ b/lualib/internal/UDP.lua @@ -8,10 +8,15 @@ local co_self = co.self local co_wakeup = co.wakeup local co_wait = co.wait +local G_Reference = {} +local tab = debug.getregistry() +tab['__G_UDP__'] = G_Reference + local UDP = class("UDP") function UDP:ctor(opt) self.udp = udp.new() + G_Reference[self] = true end -- 超时时间 @@ -83,6 +88,8 @@ function UDP:close() if self._timeout then self._timeout = nil end + + G_Reference[self] = nil end return UDP From a5264a19376b51617cdf75af6684877c739ddf1d Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 16 Oct 2021 21:26:43 +0800 Subject: [PATCH 846/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0cjson=E5=BA=93?= =?UTF-8?q?=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcjson/Makefile | 2 +- luaclib/src/lcjson/lua_cjson.c | 32 +++++++++++++++++++++++++++----- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/luaclib/src/lcjson/Makefile b/luaclib/src/lcjson/Makefile index 7f26bcfb..067bbf71 100644 --- a/luaclib/src/lcjson/Makefile +++ b/luaclib/src/lcjson/Makefile @@ -12,7 +12,7 @@ default : CFLAGS = -O3 -Wall -shared -fPIC CC = cc -INCLUDES = -I../../../src -I/usr/local/include +INCLUDES = -I. -I../../../src -I/usr/local/include LIBS = -L../ -L../../ -L../../../ -L/usr/local/lib DLL = -lcore diff --git a/luaclib/src/lcjson/lua_cjson.c b/luaclib/src/lcjson/lua_cjson.c index c09e83ac..6f6d6c18 100644 --- a/luaclib/src/lcjson/lua_cjson.c +++ b/luaclib/src/lcjson/lua_cjson.c @@ -38,15 +38,15 @@ #include -#include "strbuf.h" -#include "fpconv.h" +#include +#include #ifndef CJSON_MODNAME #define CJSON_MODNAME "cjson" #endif #ifndef CJSON_VERSION -#define CJSON_VERSION "2.1.0.6" +#define CJSON_VERSION "2.1.0.9" #endif #ifdef _MSC_VER @@ -75,6 +75,7 @@ #define DEFAULT_ENCODE_NUMBER_PRECISION 14 #define DEFAULT_ENCODE_EMPTY_TABLE_AS_OBJECT 1 #define DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT 0 +#define DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH 1 #ifdef DISABLE_INVALID_NUMBERS #undef DEFAULT_DECODE_INVALID_NUMBERS @@ -95,6 +96,10 @@ #define json_lightudata_mask(ludata) (ludata) #endif +#if LUA_VERSION_NUM > 501 +#define lua_objlen(L,i) lua_rawlen(L, (i)) +#endif + static const char * const *json_empty_array; static const char * const *json_array; @@ -149,6 +154,7 @@ typedef struct { int encode_number_precision; int encode_keep_buffer; int encode_empty_table_as_object; + int encode_escape_forward_slash; int decode_invalid_numbers; int decode_max_depth; @@ -400,6 +406,20 @@ static int json_cfg_decode_invalid_numbers(lua_State *l) return 1; } +static int json_cfg_encode_escape_forward_slash(lua_State *l) +{ + int ret; + json_config_t *cfg = json_arg_init(l, 1); + + ret = json_enum_option(l, 1, &cfg->encode_escape_forward_slash, NULL, 1); + if (cfg->encode_escape_forward_slash) { + char2escape['/'] = "\\/"; + } else { + char2escape['/'] = NULL; + } + return ret; +} + static int json_destroy_config(lua_State *l) { json_config_t *cfg; @@ -436,6 +456,7 @@ static void json_create_config(lua_State *l) cfg->encode_number_precision = DEFAULT_ENCODE_NUMBER_PRECISION; cfg->encode_empty_table_as_object = DEFAULT_ENCODE_EMPTY_TABLE_AS_OBJECT; cfg->decode_array_with_array_mt = DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT; + cfg->encode_escape_forward_slash = DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH; #if DEFAULT_ENCODE_KEEP_BUFFER > 0 strbuf_init(&cfg->encode_buf, 0); @@ -743,7 +764,7 @@ static void json_append_data(lua_State *l, json_config_t *cfg, } if (as_array) { - len = lua_rawlen(l, -1); + len = lua_objlen(l, -1); json_append_array(l, cfg, current_depth, json, len); } else { len = lua_array_length(l, cfg, json); @@ -1451,6 +1472,7 @@ static int lua_cjson_new(lua_State *l) { "encode_keep_buffer", json_cfg_encode_keep_buffer }, { "encode_invalid_numbers", json_cfg_encode_invalid_numbers }, { "decode_invalid_numbers", json_cfg_decode_invalid_numbers }, + { "encode_escape_forward_slash", json_cfg_encode_escape_forward_slash }, { "new", lua_cjson_new }, { NULL, NULL } }; @@ -1559,4 +1581,4 @@ int luaopen_cjson_safe(lua_State *l) } /* vi:ai et sw=4 ts=4: - */ + */ \ No newline at end of file From 5492b200259c0ac7c8b5ce876ef9f8f4baf3ab13 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Fri, 29 Oct 2021 00:49:37 +0800 Subject: [PATCH 847/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=9B=B4=E5=A4=9A?= =?UTF-8?q?=E7=9A=84=E7=BD=91=E7=BB=9C=E4=BF=A1=E5=8F=B7=E5=B1=8F=E8=94=BD?= =?UTF-8?q?=E6=9C=BA=E5=88=B6.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 11bd6d51..a8a9f5aa 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -9,6 +9,10 @@ #define alloca __alloca #endif +#ifndef MSG_NOSIGNAL + #define MSG_NOSIGNAL (0) +#endif + #define MBSIZE (262144) #define None (-1) @@ -47,6 +51,16 @@ static inline void SETSOCKETOPT(int sockfd, int mode){ } #endif +#ifdef SO_NOSIGPIPE + // 屏蔽信号 + ret = setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, &Enable, sizeof(Enable)); + if (ret < 0) { + LOG("ERROR", "Setting SO_NOSIGPIPE failed."); + LOG("ERROR", strerror(errno)); + return core_exit(); + } +#endif + /* 关闭连接不会阻塞 */ #ifdef SO_LINGER struct linger lin = { .l_onoff = 0, .l_linger = 0 }; @@ -309,6 +323,7 @@ static int create_client_unixsock(const char* path, size_t path_len) { int ret = connect(sockfd, (struct sockaddr*)&UN, sizeof(UN)); if (0 > ret) { + LOG("ERROR", strerror(errno)); close(sockfd); return -1; } @@ -467,7 +482,7 @@ IO_SENDFILE(CORE_P_ core_io *io, int revents){ for(;;) { int rBytes = pread(sf->fd, buf, sf->offset, sf->pos); if (rBytes == 0) { lua_pushboolean(sf->L, 1); break; } // 所有数据写入发送完毕. - int wBytes = write(io->fd, buf, rBytes); + int wBytes = send(io->fd, buf, rBytes, MSG_DONTWAIT | MSG_NOSIGNAL); if (wBytes <= 0) { if (errno == EINTR) continue; if (errno == EWOULDBLOCK) return; @@ -665,9 +680,7 @@ static int tcp_write(lua_State *L){ errno = 0; int offset = lua_tointeger(L, 3); do { - - int wsize = write(fd, response + offset, resp_len - offset); - + int wsize = send(fd, response + offset, resp_len - offset, MSG_DONTWAIT | MSG_NOSIGNAL); if (wsize > 0) { lua_pushinteger(L, wsize); return 1; } if (wsize < 0){ From a5307f99b096c25f1ff35c306644c8b7f6c7d4f9 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 7 Nov 2021 14:20:34 +0800 Subject: [PATCH 848/956] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index a8a9f5aa..d7eb5eef 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -262,8 +262,7 @@ static int create_client_fd(const char *ipaddr, int port){ } int ret = connect(sockfd, (struct sockaddr*)&SA, sizeof(SA)); - if (ret < 0 && errno != EINPROGRESS){ - LOG("ERROR", strerror(errno)); + if (ret != 0 && errno != EINPROGRESS) { close(sockfd); return -1; } From cfc21b8e80b59cc2f3833c41b05d6de545bb6e60 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 7 Nov 2021 14:21:02 +0800 Subject: [PATCH 849/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=9B=BE=E6=A0=87?= =?UTF-8?q?=E9=93=BE=E6=8E=A5=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/html/system/menu/menu-add.html | 2 +- lualib/admin/html/system/menu/menu-edit.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lualib/admin/html/system/menu/menu-add.html b/lualib/admin/html/system/menu/menu-add.html index a8b3a364..ee0eaab6 100644 --- a/lualib/admin/html/system/menu/menu-add.html +++ b/lualib/admin/html/system/menu/menu-add.html @@ -52,7 +52,7 @@
                  *{*locale['dashboard.menu.menu_manage.menu_add.form.icon.notice']*} - +
                  diff --git a/lualib/admin/html/system/menu/menu-edit.html b/lualib/admin/html/system/menu/menu-edit.html index 40cef0fc..20b46a19 100644 --- a/lualib/admin/html/system/menu/menu-edit.html +++ b/lualib/admin/html/system/menu/menu-edit.html @@ -52,7 +52,7 @@
                  *{*locale['dashboard.menu.menu_manage.menu_edit.form.icon.notice']*} - +
                  From e8c7a911864e80fd4d0507cbabf5340ff42286cc Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 7 Nov 2021 14:21:27 +0800 Subject: [PATCH 850/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=91=BD=E5=90=8D?= =?UTF-8?q?=E9=81=BF=E5=85=8D=E6=BD=9C=E5=9C=A8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/cf/init.lua | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/lualib/cf/init.lua b/lualib/cf/init.lua index aa39bd2f..675bd63b 100644 --- a/lualib/cf/init.lua +++ b/lualib/cf/init.lua @@ -1,12 +1,12 @@ local Co = require "internal.Co" -local self = Co.self -local fork = Co.spawn -local wait = Co.wait -local wakeup = Co.wakeup +local co_self = Co.self +local co_fork = Co.spawn +local co_wait = Co.wait +local co_wakeup = Co.wakeup local Timer = require "internal.Timer" local time_at = Timer.at -local sleep = Timer.sleep +local time_sleep = Timer.sleep local time_out = Timer.timeout local type = type @@ -33,11 +33,11 @@ end ---comment 让出协程执行权 local function yield () - local co = self() - fork(function () - wakeup(co) + local co = co_self() + co_fork(function () + co_wakeup(co) end) - return wait() + return co_wait() end cf.yield = yield @@ -46,7 +46,7 @@ cf.yield = yield function cf.sleep(timeout) timeout = tonumber(timeout) if timeout and timeout > 0 then - return sleep(timeout) + return time_sleep(timeout) end return yield() end @@ -54,33 +54,33 @@ end ---comment 获取调用此方法的协程对象. ---@return thread function cf.self () - return self() + return co_self() end ---comment 让出协程 function cf.wait() - return wait() + return co_wait() end ---comment 创建一个由cf框架调度的协程 ---@param func function @协程的执行的函数 ---@return thread 协程对象 function cf.fork(func, ...) - return fork(func, ...) + return co_fork(func, ...) end ---comment 唤醒一个由cf框架创建的协程 ---@param co thread @被唤醒的协程对象与需要传递给协程的参数 ---@return nil function cf.wakeup(co, ...) - return wakeup(co, ...) + return co_wakeup(co, ...) end local function cb(f, ctx) local ok, errinfo = pcall(f) ctx.waits = ctx.waits - 1 if ctx.waits == 0 then - wakeup(ctx.co) + co_wakeup(ctx.co) end if not ok then return error(errinfo) @@ -92,16 +92,16 @@ end ---@return nil @始终不存在返回值. function cf.join(fn, ...) local qlist = {fn, ...} - local ctx = { co = self(), waits = nil } + local ctx = { co = co_self(), waits = nil } local waits = 0 for _, f in ipairs(qlist) do if type(f) == 'function' then - fork(cb, f, ctx) + co_fork(cb, f, ctx) waits = waits + 1 end end ctx.waits = waits - return wait() + return co_wait() end return cf From a03884cfb1f2a2a277ed28aee26aa7f916721cf5 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 7 Nov 2021 14:37:22 +0800 Subject: [PATCH 851/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0set=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/utils/set.lua | 136 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 lualib/utils/set.lua diff --git a/lualib/utils/set.lua b/lualib/utils/set.lua new file mode 100644 index 00000000..f1f909fe --- /dev/null +++ b/lualib/utils/set.lua @@ -0,0 +1,136 @@ +local type = type +local pairs = pairs +local assert = assert +local tostring = tostring +local setmetatable = setmetatable + +local tconcat = table.concat + +---@class Set @集合 +local MetaSet = { map = {} } + +MetaSet.__index = MetaSet + +---comment 实例化`Set` +---@return Set +function MetaSet:new() + return setmetatable({ count = 0, map = {} }, MetaSet) +end + +---comment 插入元素 +---@param value any @待插入的元素 +---@return boolean @已存在返回`false`, 否则返回`true` +function MetaSet:insert(value) + if not self.map[value] then + self.map[value] = true + self.count = self.count + 1 + return true + end + return false +end + +---comment 删除元素 +---@param value any @待删除的元素 +---@return boolean @删除成功返回`true`, 否则返回`false` +function MetaSet:remove(value) + if self.map[value] then + self.map[value] = nil + self.count = self.count - 1 + return true + end + return false +end + +---comment 返回集合内的元素数量 +---@return integer +function MetaSet:len() + return self.count +end + +---comment 是否为空 +---@return boolean +function MetaSet:is_empty() + return self.count == 0 +end + +---comment 美化打印输出 +---@return string +function MetaSet:__tostring() + local tab = {} + for element in pairs(self.map) do + tab[#tab+1] = tostring(element) + end + return "Set([" .. tconcat(tab, ', ') .. "])" +end + +---comment 求差集 +---@param t1 Set @集合1 +---@param t2 Set @集合2 +---@return Set @新集合 +function MetaSet.__sub(t1, t2) + assert(type(t1) == 'table' and type(t2) == 'table', "[Set ERROR]: Invalid Set OP.") + local t1_map, t2_map = t1.map, t2.map + assert(type(t1_map) == 'table' and type(t2_map) == 'table', "[Set ERROR]: Invalid Set OP.") + local Set = MetaSet.new() + for k in pairs(t1_map) do + if not t2_map[k] then + Set:insert(k) + end + end + return Set +end + +---comment 求交集 +---@param t1 Set @集合1 +---@param t2 Set @集合2 +---@return Set @新集合 +function MetaSet.__band (t1, t2) + assert(type(t1) == 'table' and type(t2) == 'table', "[Set ERROR]: Invalid Set OP.") + local t1_map, t2_map = t1.map, t2.map + assert(type(t1_map) == 'table' and type(t2_map) == 'table', "[Set ERROR]: Invalid Set OP.") + local Set = MetaSet.new() + for k in pairs(t1_map) do + if t2_map[k] then + Set:insert(k) + end + end + return Set +end + +---comment 求并集 +---@param t1 Set @集合1 +---@param t2 Set @集合2 +---@return Set @新集合 +function MetaSet.__bor (t1, t2) + assert(type(t1) == 'table' and type(t2) == 'table', "[Set ERROR]: Invalid Set OP.") + local t1_map, t2_map = t1.map, t2.map + assert(type(t1_map) == 'table' and type(t2_map) == 'table', "[Set ERROR]: Invalid Set OP.") + local Set = MetaSet.new() + for k in pairs(t1_map) do + Set:insert(k) + end + for k in pairs(t2_map) do + Set:insert(k) + end + return Set +end + +---comment 求并集 +---@param t1 Set @集合1 +---@param t2 Set @集合2 +---@return Set @新集合 +function MetaSet.__add (t1, t2) + assert(type(t1) == 'table' and type(t2) == 'table', "[Set ERROR]: Invalid Set OP.") + local t1_map, t2_map = t1.map, t2.map + assert(type(t1_map) == 'table' and type(t2_map) == 'table', "[Set ERROR]: Invalid Set OP.") + local Set = MetaSet.new() + for k in pairs(t1_map) do + Set:insert(k) + end + for k in pairs(t2_map) do + Set:insert(k) + end + return Set +end + +return MetaSet \ No newline at end of file From 465206017cb707df49ed2292af3e26d853b42ded Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 15 Nov 2021 23:51:03 +0800 Subject: [PATCH 852/956] =?UTF-8?q?cfadmin=20-w=E5=8F=82=E6=95=B0=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E2=80=98auto=E2=80=99=E8=87=AA=E5=8A=A8=E6=A3=80?= =?UTF-8?q?=E6=9F=A5=E6=A0=B8=E5=BF=83=E6=95=B0=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_start.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core_start.c b/src/core_start.c index b0c0eea6..1d581ce2 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -90,8 +90,12 @@ static inline void cfadmin_specify_kill_process(const char *spid) { /* 指定子进程数量 */ static inline void cfadmin_specify_nprocess(const char* w) { nprocess = atoi(w); + /* 检查核心数量是否有效 */ if (nprocess <= 0 || nprocess > 255 ) nprocess = 1; + /* 自动检查可用核心数量 */ + if (!strcmp(w, "auto")) + nprocess = sysconf(_SC_NPROCESSORS_ONLN) < 1 ? 1 : sysconf(_SC_NPROCESSORS_ONLN); } /* 后台运行 */ From d2e93bd7d9b553c0d414f8dbc32e94ac2292b309 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 16 Nov 2021 00:18:41 +0800 Subject: [PATCH 853/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9socket=E7=9A=84timeou?= =?UTF-8?q?t=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/TCP.lua | 2 +- lualib/internal/UDP.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index 47d949b3..49770f56 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -115,7 +115,7 @@ end -- 超时时间 function TCP:timeout(Interval) - if Interval and Interval > 0 then + if type(Interval) == 'number' and Interval >= 0 then self._timeout = Interval end return self diff --git a/lualib/internal/UDP.lua b/lualib/internal/UDP.lua index d054acc2..d440e522 100644 --- a/lualib/internal/UDP.lua +++ b/lualib/internal/UDP.lua @@ -21,7 +21,7 @@ end -- 超时时间 function UDP:timeout(Interval) - if Interval and Interval > 0 then + if type(Interval) == 'number' and Interval >= 0 then self._timeout = Interval end return self From 8348bcf79e3b8fc2c28f6e8443f5cdf9ff7f5992 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 21 Nov 2021 22:48:19 +0800 Subject: [PATCH 854/956] =?UTF-8?q?=E5=8E=BB=E9=99=A4mqtt=E7=9A=84?= =?UTF-8?q?=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/mqtt/bit.lua | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lualib/protocol/mqtt/bit.lua b/lualib/protocol/mqtt/bit.lua index b8066ade..895d9211 100644 --- a/lualib/protocol/mqtt/bit.lua +++ b/lualib/protocol/mqtt/bit.lua @@ -1,9 +1,4 @@ -- wrapper around BitOp module - -if _VERSION == "Lua 5.1" or _VERSION == "Lua 5.2" then - return require("bit") -else - return require("protocol.mqtt.bit53") -end +return require("protocol.mqtt.bit53") -- vim: ts=4 sts=4 sw=4 noet ft=lua From d38d8d55d3073e472554e6b1f1d60623fecad7ce Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 21 Nov 2021 22:48:45 +0800 Subject: [PATCH 855/956] =?UTF-8?q?=E5=8E=BB=E9=99=A4xml=E7=9A=84=E8=AD=A6?= =?UTF-8?q?=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/xml2lua/XmlParser.lua | 2 +- lualib/xml2lua/xml2lua.lua | 2 +- lualib/xml2lua/xmlhandler/dom.lua | 2 +- lualib/xml2lua/xmlhandler/print.lua | 6 +++--- lualib/xml2lua/xmlhandler/tree.lua | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lualib/xml2lua/XmlParser.lua b/lualib/xml2lua/XmlParser.lua index 7482c3c2..b5dc250f 100644 --- a/lualib/xml2lua/XmlParser.lua +++ b/lualib/xml2lua/XmlParser.lua @@ -1,4 +1,4 @@ ---- @module Class providing the actual XML parser. +-- @module Class providing the actual XML parser. -- Available options are: -- * stripWS -- Strip non-significant whitespace (leading/trailing) diff --git a/lualib/xml2lua/xml2lua.lua b/lualib/xml2lua/xml2lua.lua index a4b5ab1a..d7222be3 100755 --- a/lualib/xml2lua/xml2lua.lua +++ b/lualib/xml2lua/xml2lua.lua @@ -1,4 +1,4 @@ ---- @module Module providing a non-validating XML stream parser in Lua. +-- @module Module providing a non-validating XML stream parser in Lua. -- -- Features: -- ========= diff --git a/lualib/xml2lua/xmlhandler/dom.lua b/lualib/xml2lua/xmlhandler/dom.lua index e37f3591..236bbb45 100755 --- a/lualib/xml2lua/xmlhandler/dom.lua +++ b/lualib/xml2lua/xmlhandler/dom.lua @@ -6,7 +6,7 @@ local function init() } end ---- @module Handler to generate a DOM-like node tree structure with +-- @module Handler to generate a DOM-like node tree structure with -- a single ROOT node parent - each node is a table comprising -- the fields below. -- diff --git a/lualib/xml2lua/xmlhandler/print.lua b/lualib/xml2lua/xmlhandler/print.lua index ca153c6f..83c4a146 100755 --- a/lualib/xml2lua/xmlhandler/print.lua +++ b/lualib/xml2lua/xmlhandler/print.lua @@ -1,6 +1,6 @@ ----@module Handler to generate a simple event trace which ---outputs messages to the terminal during the XML ---parsing, usually for debugging purposes. +-- @module Handler to generate a simple event trace which +-- outputs messages to the terminal during the XML +-- parsing, usually for debugging purposes. -- -- License: -- ======== diff --git a/lualib/xml2lua/xmlhandler/tree.lua b/lualib/xml2lua/xmlhandler/tree.lua index 19d22c56..69320976 100755 --- a/lualib/xml2lua/xmlhandler/tree.lua +++ b/lualib/xml2lua/xmlhandler/tree.lua @@ -7,7 +7,7 @@ local function init() return obj end ---- @module XML Tree Handler. +-- @module XML Tree Handler. -- Generates a lua table from an XML content string. -- It is a simplified handler which attempts -- to generate a more 'natural' table based structure which From 56a286b25db8fe6afc7464a741409c1ff4fe6f1e Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 29 Nov 2021 00:47:03 +0800 Subject: [PATCH 856/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=A4=9A=E8=BF=9B?= =?UTF-8?q?=E7=A8=8B=E6=A8=A1=E5=BC=8F=E7=9A=84process=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/Makefile | 6 +- luaclib/src/lchild.c | 42 +++ luaclib/src/lpack.c | 741 +++++++++++++++++++++++++++++++++++++ luaclib/src/ltcp.c | 2 +- lualib/process/channel.lua | 127 +++++++ lualib/process/dataset.lua | 26 ++ lualib/process/event.lua | 32 ++ lualib/process/master.lua | 61 +++ lualib/process/session.lua | 57 +++ lualib/process/utils.lua | 11 + lualib/process/worker.lua | 43 +++ src/core.c | 338 ++++++++++------- 12 files changed, 1348 insertions(+), 138 deletions(-) create mode 100644 luaclib/src/lchild.c create mode 100644 luaclib/src/lpack.c create mode 100644 lualib/process/channel.lua create mode 100644 lualib/process/dataset.lua create mode 100644 lualib/process/event.lua create mode 100644 lualib/process/master.lua create mode 100644 lualib/process/session.lua create mode 100644 lualib/process/utils.lua create mode 100644 lualib/process/worker.lua diff --git a/luaclib/Makefile b/luaclib/Makefile index 3a7888f6..f0608d5c 100644 --- a/luaclib/Makefile +++ b/luaclib/Makefile @@ -11,11 +11,15 @@ INCLUDES += -I../src -I/usr/local/include LIBS += -L./ -L../ -L/usr/local/lib # CFLAGS = -Wall -O3 -fPIC --shared -DJEMALLOC -ljemalloc -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib # CFLAGS = -Wall -O3 -fPIC --shared -DTCMALLOC -ltcmalloc -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib -CFLAGS = -Wall -O3 -fPIC --shared -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib +CFLAGS = -Wall -O3 -fPIC --shared -Wno-unused-function -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib internal : @echo "CC - lsys" @$(CC) -o sys.so src/lsys.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore + @echo "CC - lpack" + @$(CC) -o pack.so src/lpack.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore + @echo "CC - lchild" + @$(CC) -o child.so src/lchild.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore @echo "CC - ludp" @$(CC) -o udp.so src/ludp.c $(CFLAGS) $(INCLUDES) $(LIBS) -lcore @echo "CC - ltask" diff --git a/luaclib/src/lchild.c b/luaclib/src/lchild.c new file mode 100644 index 00000000..771d9fba --- /dev/null +++ b/luaclib/src/lchild.c @@ -0,0 +1,42 @@ +#define LUA_LIB + +#include + +static void CHILD_CB (core_loop *loop, ev_child *w, int revents){ + ev_child_stop(loop, w); + lua_State *co = core_get_watcher_userdata(w); + if (co) { + lua_pushinteger(co, w->rpid); + lua_pushinteger(co, w->rstatus); + int status = CO_RESUME(co, NULL, lua_status(co) == LUA_YIELD ? lua_gettop(co) : lua_gettop(co) - 1); + if (status != LUA_YIELD && status != LUA_OK){ + LOG("ERROR", lua_tostring(co, -1)); + } + } +} + +/* 监听子进程状态 */ +static int lwatch(lua_State *L){ + core_child* child = lua_newuserdata(L, sizeof(core_child)); + core_child_init(child, CHILD_CB, luaL_checkinteger(L, 1), 0); + core_child_start(core_default_loop(), child); + core_set_watcher_userdata(child, (void*)lua_tothread(L, 2)); + return 1; +} + +/* 发信号杀死子进程 */ +static int lkill(lua_State *L){ + kill(luaL_checkinteger(L, 1), SIGQUIT); + return 0; +} + +LUAMOD_API int luaopen_child(lua_State *L) { + luaL_checkversion(L); + luaL_Reg child_libs[] = { + {"watch", lwatch}, + {"kill", lkill}, + {NULL, NULL}, + }; + luaL_newlib(L, child_libs); + return 1; +} \ No newline at end of file diff --git a/luaclib/src/lpack.c b/luaclib/src/lpack.c new file mode 100644 index 00000000..64d60cc4 --- /dev/null +++ b/luaclib/src/lpack.c @@ -0,0 +1,741 @@ +#define LUA_LIB + +#include + +#define TYPE_NIL 0 +#define TYPE_BOOLEAN 1 +// hibits 0 false 1 true + +#define TYPE_NUMBER 2 +// hibits 0 : 0 , 1: byte, 2:word, 4: dword, 6: qword, 8 : double +#define TYPE_NUMBER_ZERO 0 +#define TYPE_NUMBER_BYTE 1 +#define TYPE_NUMBER_WORD 2 +#define TYPE_NUMBER_DWORD 4 +#define TYPE_NUMBER_QWORD 6 +#define TYPE_NUMBER_REAL 8 + +#define TYPE_USERDATA 3 +#define TYPE_SHORT_STRING 4 +// hibits 0~31 : len +#define TYPE_LONG_STRING 5 +#define TYPE_TABLE 6 + +#define MAX_COOKIE 32 +#define COMBINE_TYPE(t,v) ((t) | (v) << 3) + +#define BLOCK_SIZE 128 +#define MAX_DEPTH 32 + +struct block { + struct block * next; + char buffer[BLOCK_SIZE]; +}; + +struct write_block { + struct block * head; + int len; + struct block * current; + int ptr; +}; + +struct read_block { + char * buffer; + struct block * current; + int len; + int ptr; +}; + +inline static struct block * +blk_alloc(void) { + struct block *b = malloc(sizeof(struct block)); + b->next = NULL; + return b; +} + +inline static void wb_push(struct write_block *b, const void *buf, int sz) { + const char * buffer = buf; + if (b->ptr == BLOCK_SIZE) { +_again: + b->current = b->current->next = blk_alloc(); + b->ptr = 0; + } + if (b->ptr <= BLOCK_SIZE - sz) { + memcpy(b->current->buffer + b->ptr, buffer, sz); + b->ptr+=sz; + b->len+=sz; + } else { + int copy = BLOCK_SIZE - b->ptr; + memcpy(b->current->buffer + b->ptr, buffer, copy); + buffer += copy; + b->len += copy; + sz -= copy; + goto _again; + } +} + +static void wb_init(struct write_block *wb , struct block *b) { + if (b==NULL) { + wb->head = blk_alloc(); + wb->len = 0; + wb->current = wb->head; + wb->ptr = 0; + wb_push(wb, &wb->len, sizeof(wb->len)); + } else { + wb->head = b; + int * plen = (int *)b->buffer; + int sz = *plen; + wb->len = sz; + while (b->next) { + sz -= BLOCK_SIZE; + b = b->next; + } + wb->current = b; + wb->ptr = sz; + } +} + +static struct block* wb_close(struct write_block *b) { + b->current = b->head; + b->ptr = 0; + wb_push(b, &b->len, sizeof(b->len)); + b->current = NULL; + return b->head; +} + +static void wb_free(struct write_block *wb) { + struct block *blk = wb->head; + while (blk) { + struct block * next = blk->next; + free(blk); + blk = next; + } + wb->head = NULL; + wb->current = NULL; + wb->ptr = 0; + wb->len = 0; +} + +static void rball_init(struct read_block * rb, char * buffer) { + rb->buffer = buffer; + rb->current = NULL; + uint8_t header[4]; + memcpy(header,buffer,4); + rb->len = header[0] | header[1] <<8 | header[2] << 16 | header[3] << 24; + rb->ptr = 4; + rb->len -= rb->ptr; +} + +static int rb_init(struct read_block *rb, struct block *b) { + rb->buffer = NULL; + rb->current = b; + memcpy(&(rb->len),b->buffer,sizeof(rb->len)); + rb->ptr = sizeof(rb->len); + rb->len -= rb->ptr; + return rb->len; +} + +static void* rb_read(struct read_block *rb, void *buffer, int sz) { + if (rb->len < sz) { + return NULL; + } + + if (rb->buffer) { + int ptr = rb->ptr; + rb->ptr += sz; + rb->len -= sz; + return rb->buffer + ptr; + } + + if (rb->ptr == BLOCK_SIZE) { + struct block * next = rb->current->next; + free(rb->current); + rb->current = next; + rb->ptr = 0; + } + + int copy = BLOCK_SIZE - rb->ptr; + + if (sz <= copy) { + void * ret = rb->current->buffer + rb->ptr; + rb->ptr += sz; + rb->len -= sz; + return ret; + } + + char * tmp = buffer; + + memcpy(tmp, rb->current->buffer + rb->ptr, copy); + sz -= copy; + tmp += copy; + rb->len -= copy; + + for (;;) { + struct block * next = rb->current->next; + free(rb->current); + rb->current = next; + + if (sz < BLOCK_SIZE) { + memcpy(tmp, rb->current->buffer, sz); + rb->ptr = sz; + rb->len -= sz; + return buffer; + } + memcpy(tmp, rb->current->buffer, BLOCK_SIZE); + sz -= BLOCK_SIZE; + tmp += BLOCK_SIZE; + rb->len -= BLOCK_SIZE; + } +} + +static void rb_close(struct read_block *rb) { + while (rb->current) { + struct block * next = rb->current->next; + free(rb->current); + rb->current = next; + } + rb->len = 0; + rb->ptr = 0; +} + +static inline void wb_nil(struct write_block *wb) { + int n = TYPE_NIL; + wb_push(wb, &n, 1); +} + +static inline void wb_boolean(struct write_block *wb, int boolean) { + int n = COMBINE_TYPE(TYPE_BOOLEAN , boolean ? 1 : 0); + wb_push(wb, &n, 1); +} + +static inline void wb_integer(struct write_block *wb, lua_Integer v) { + int type = TYPE_NUMBER; + if (v == 0) { + uint8_t n = COMBINE_TYPE(type , TYPE_NUMBER_ZERO); + wb_push(wb, &n, 1); + } else if (v != (int32_t)v) { + uint8_t n = COMBINE_TYPE(type , TYPE_NUMBER_QWORD); + int64_t v64 = v; + wb_push(wb, &n, 1); + wb_push(wb, &v64, sizeof(v64)); + } else if (v < 0) { + int32_t v32 = (int32_t)v; + uint8_t n = COMBINE_TYPE(type , TYPE_NUMBER_DWORD); + wb_push(wb, &n, 1); + wb_push(wb, &v32, sizeof(v32)); + } else if (v<0x100) { + uint8_t n = COMBINE_TYPE(type , TYPE_NUMBER_BYTE); + wb_push(wb, &n, 1); + uint8_t byte = (uint8_t)v; + wb_push(wb, &byte, sizeof(byte)); + } else if (v<0x10000) { + uint8_t n = COMBINE_TYPE(type , TYPE_NUMBER_WORD); + wb_push(wb, &n, 1); + uint16_t word = (uint16_t)v; + wb_push(wb, &word, sizeof(word)); + } else { + uint8_t n = COMBINE_TYPE(type , TYPE_NUMBER_DWORD); + wb_push(wb, &n, 1); + uint32_t v32 = (uint32_t)v; + wb_push(wb, &v32, sizeof(v32)); + } +} + +static inline void +wb_real(struct write_block *wb, double v) { + uint8_t n = COMBINE_TYPE(TYPE_NUMBER , TYPE_NUMBER_REAL); + wb_push(wb, &n, 1); + wb_push(wb, &v, sizeof(v)); +} + +static inline void +wb_pointer(struct write_block *wb, void *v) { + int n = TYPE_USERDATA; + wb_push(wb, &n, 1); + wb_push(wb, &v, sizeof(v)); +} + +static inline void +wb_string(struct write_block *wb, const char *str, int len) { + if (len < MAX_COOKIE) { + int n = COMBINE_TYPE(TYPE_SHORT_STRING, len); + wb_push(wb, &n, 1); + if (len > 0) { + wb_push(wb, str, len); + } + } else { + int n; + if (len < 0x10000) { + n = COMBINE_TYPE(TYPE_LONG_STRING, 2); + wb_push(wb, &n, 1); + uint16_t x = (uint16_t) len; + wb_push(wb, &x, 2); + } else { + n = COMBINE_TYPE(TYPE_LONG_STRING, 4); + wb_push(wb, &n, 1); + uint32_t x = (uint32_t) len; + wb_push(wb, &x, 4); + } + wb_push(wb, str, len); + } +} + +static void pack_one(lua_State *L, struct write_block *b, int index, int depth); + +static int wb_table_array(lua_State *L, struct write_block * wb, int index, int depth) { + int array_size = lua_rawlen(L,index); + if (array_size >= MAX_COOKIE-1) { + int n = COMBINE_TYPE(TYPE_TABLE, MAX_COOKIE-1); + wb_push(wb, &n, 1); + wb_integer(wb, array_size); + } else { + int n = COMBINE_TYPE(TYPE_TABLE, array_size); + wb_push(wb, &n, 1); + } + + int i; + for (i=1;i<=array_size;i++) { + lua_rawgeti(L,index,i); + pack_one(L, wb, -1, depth); + lua_pop(L,1); + } + + return array_size; +} + +static void wb_table_hash(lua_State *L, struct write_block * wb, int index, int depth, int array_size) { + lua_pushnil(L); + while (lua_next(L, index) != 0) { + if (lua_type(L,-2) == LUA_TNUMBER) { + if (lua_isinteger(L, -2)) { + lua_Integer x = lua_tointeger(L,-2); + if (x>0 && x<=array_size) { + lua_pop(L,1); + continue; + } + } + } + pack_one(L,wb,-2,depth); + pack_one(L,wb,-1,depth); + lua_pop(L, 1); + } + wb_nil(wb); +} + +static void wb_table(lua_State *L, struct write_block *wb, int index, int depth) { + luaL_checkstack(L,LUA_MINSTACK,NULL); + if (index < 0) { + index = lua_gettop(L) + index + 1; + } + int array_size = wb_table_array(L, wb, index, depth); + wb_table_hash(L, wb, index, depth, array_size); +} + +#if LUA_VERSION_NUM < 503 + +static int lua_isinteger(lua_State *L, int index) { + int32_t x = (int32_t)lua_tointeger(L,index); + lua_Number n = lua_tonumber(L,index); + return ((lua_Number)x==n); +} + +#endif + +static void pack_one(lua_State *L, struct write_block *b, int index, int depth) { + if (depth > MAX_DEPTH) { + wb_free(b); + luaL_error(L, "serialize can't pack too depth table"); + } + int type = lua_type(L,index); + switch(type) { + case LUA_TNIL: + wb_nil(b); + break; + case LUA_TNUMBER: { + if (lua_isinteger(L, index)) { + lua_Integer x = lua_tointeger(L,index); + wb_integer(b, x); + } else { + lua_Number n = lua_tonumber(L,index); + wb_real(b,n); + } + break; + } + case LUA_TBOOLEAN: + wb_boolean(b, lua_toboolean(L,index)); + break; + case LUA_TSTRING: { + size_t sz = 0; + const char *str = lua_tolstring(L,index,&sz); + wb_string(b, str, (int)sz); + break; + } + case LUA_TLIGHTUSERDATA: + wb_pointer(b, lua_touserdata(L,index)); + break; + case LUA_TTABLE: { + if (index < 0) { + index = lua_gettop(L) + index + 1; + } + wb_table(L, b, index, depth+1); + break; + } + default: + wb_free(b); + luaL_error(L, "Unsupport type %s to serialize", lua_typename(L, type)); + } +} + +static void pack_from(lua_State *L, struct write_block *b, int from) { + int n = lua_gettop(L) - from; + int i; + for (i=1;i<=n;i++) { + pack_one(L, b , from + i, 0); + } +} + +static int lpack(lua_State *L) { + struct write_block b; + wb_init(&b, NULL); + pack_from(L,&b,0); + struct block * ret = wb_close(&b); + lua_pushlightuserdata(L,ret); + return 1; +} + +static int lappend(lua_State *L) { + struct write_block b; + wb_init(&b, lua_touserdata(L,1)); + pack_from(L,&b,1); + struct block * ret = wb_close(&b); + lua_pushlightuserdata(L,ret); + return 1; +} + +static inline void invalid_stream_line(lua_State *L, struct read_block *rb, int line) { + int len = rb->len; + luaL_error(L, "Invalid serialize stream %d (line:%d)", len, line); +} + +#define invalid_stream(L,rb) invalid_stream_line(L, rb, __LINE__) + +static lua_Integer get_integer(lua_State *L, struct read_block *rb, int cookie) { + switch (cookie) { + case TYPE_NUMBER_ZERO: + return 0; + case TYPE_NUMBER_BYTE: { + uint8_t n = 0; + uint8_t * pn = rb_read(rb, &n, sizeof(n)); + if (pn == NULL) + invalid_stream(L,rb); + n = *pn; + return n; + } + case TYPE_NUMBER_WORD: { + uint16_t n = 0; + uint16_t * pn = rb_read(rb, &n, sizeof(n)); + if (pn == NULL) + invalid_stream(L,rb); + memcpy(&n, pn, sizeof(n)); + return n; + } + case TYPE_NUMBER_DWORD: { + int32_t n = 0; + int32_t * pn = rb_read(rb, &n, sizeof(n)); + if (pn == NULL) + invalid_stream(L,rb); + memcpy(&n, pn, sizeof(n)); + return n; + } + case TYPE_NUMBER_QWORD: { + int64_t n=0; + int64_t * pn = rb_read(rb, &n, sizeof(n)); + if (pn == NULL) + invalid_stream(L,rb); + memcpy(&n, pn, sizeof(n)); + return n; + } + default: + invalid_stream(L,rb); + return 0; + } +} + +static double get_real(lua_State *L, struct read_block *rb) { + double n = 0; + double * pn = rb_read(rb, &n, sizeof(n)); + if (pn == NULL) + invalid_stream(L,rb); + memcpy(&n, pn, sizeof(n)); + return n; +} + +static void* get_pointer(lua_State *L, struct read_block *rb) { + void * userdata = 0; + void ** v = (void **)rb_read(rb,&userdata,sizeof(userdata)); + if (v == NULL) { + invalid_stream(L,rb); + } + return *v; +} + +static void get_buffer(lua_State *L, struct read_block *rb, int len) { + char tmp[len]; + char * p = rb_read(rb,tmp,len); + if (p == NULL) { + invalid_stream(L, rb); + } + lua_pushlstring(L,p,len); +} + +static void unpack_one(lua_State *L, struct read_block *rb); + +static void unpack_table(lua_State *L, struct read_block *rb, int array_size) { + if (array_size == MAX_COOKIE-1) { + uint8_t type = 0; + uint8_t *t = rb_read(rb, &type, sizeof(type)); + if (t==NULL) { + invalid_stream(L,rb); + } + type = *t; + int cookie = type >> 3; + if ((type & 7) != TYPE_NUMBER || cookie == TYPE_NUMBER_REAL) { + invalid_stream(L,rb); + } + array_size = get_integer(L,rb,cookie); + } + luaL_checkstack(L,LUA_MINSTACK,NULL); + lua_createtable(L,array_size,0); + int i; + for (i=1;i<=array_size;i++) { + unpack_one(L,rb); + lua_rawseti(L,-2,i); + } + for (;;) { + unpack_one(L,rb); + if (lua_isnil(L,-1)) { + lua_pop(L,1); + return; + } + unpack_one(L,rb); + lua_rawset(L,-3); + } +} + +static void push_value(lua_State *L, struct read_block *rb, int type, int cookie) { + switch(type) { + case TYPE_NIL: + lua_pushnil(L); + break; + case TYPE_BOOLEAN: + lua_pushboolean(L,cookie); + break; + case TYPE_NUMBER: + if (cookie == TYPE_NUMBER_REAL) { + lua_pushnumber(L,get_real(L,rb)); + } else { + lua_pushinteger(L, get_integer(L, rb, cookie)); + } + break; + case TYPE_USERDATA: + lua_pushlightuserdata(L,get_pointer(L,rb)); + break; + case TYPE_SHORT_STRING: + get_buffer(L,rb,cookie); + break; + case TYPE_LONG_STRING: { + uint32_t len = 0; + if (cookie == 2) { + uint16_t *plen = rb_read(rb, &len, 2); + if (plen == NULL) { + invalid_stream(L,rb); + } + uint16_t n; + memcpy(&n, plen, sizeof(n)); + get_buffer(L,rb,n); + } else { + if (cookie != 4) { + invalid_stream(L,rb); + } + uint32_t *plen = rb_read(rb, &len, 4); + if (plen == NULL) { + invalid_stream(L,rb); + } + uint32_t n; + memcpy(&n, plen, sizeof(n)); + get_buffer(L,rb,n); + } + break; + } + case TYPE_TABLE: { + unpack_table(L,rb,cookie); + break; + } + default: { + invalid_stream(L,rb); + break; + } + } +} + +static void unpack_one(lua_State *L, struct read_block *rb) { + uint8_t type = 0; + uint8_t *t = rb_read(rb, &type, sizeof(type)); + if (t==NULL) { + invalid_stream(L, rb); + } + type = *t; + push_value(L, rb, type & 0x7, type>>3); +} + +static int lunpack(lua_State *L) { + struct block * blk = lua_touserdata(L,1); + if (blk == NULL) { + return luaL_error(L, "Need a block to unpack"); + } + lua_settop(L,0); + struct read_block rb; + rb_init(&rb, blk); + + int i; + for (i=0;;i++) { + if (i%8==7) { + luaL_checkstack(L,LUA_MINSTACK,NULL); + } + uint8_t type = 0; + uint8_t *t = rb_read(&rb, &type, 1); + if (t==NULL) + break; + push_value(L, &rb, *t & 0x7, *t>>3); + } + + rb_close(&rb); + + return lua_gettop(L); +} + +static int _dump_mem(const char * buffer, int len, int size) { + int i; + for (i=0;ibuffer ,sizeof(len)); + len -= sizeof(len); + printf("Len = %d\n",len); + len = _dump_mem(b->buffer + sizeof(len), BLOCK_SIZE - sizeof(len) , len); + while (len > 0) { + b=b->next; + len = _dump_mem(b->buffer, BLOCK_SIZE , len); + } + printf("\n"); + return 0; +} + +static int lserialize(lua_State *L) { + struct block *b = lua_touserdata(L,1); + if (b==NULL) { + return luaL_error(L, "dump null pointer"); + } + + uint32_t len = 0; + memcpy(&len, b->buffer ,sizeof(len)); + + uint8_t * buffer = malloc(len); + uint8_t * ptr = buffer; + int sz = len; + while(len>0) { + if (len >= BLOCK_SIZE) { + memcpy(ptr, b->buffer, BLOCK_SIZE); + ptr += BLOCK_SIZE; + len -= BLOCK_SIZE; + } else { + memcpy(ptr, b->buffer, len); + break; + } + b = b->next; + } + + buffer[0] = sz & 0xff; + buffer[1] = (sz>>8) & 0xff; + buffer[2] = (sz>>16) & 0xff; + buffer[3] = (sz>>24) & 0xff; + + lua_pushlightuserdata(L, buffer); + lua_pushinteger(L, sz); + + return 2; +} + +static void deserialize_buffer(lua_State *L, void * buffer) { + struct read_block rb; + rball_init(&rb, buffer); + + int i; + for (i=0;;i++) { + if (i%16==15) { + lua_checkstack(L,i); + } + uint8_t type = 0; + uint8_t *t = rb_read(&rb, &type, 1); + if (t==NULL) + break; + push_value(L, &rb, *t & 0x7, *t>>3); + } +} + +static int ldeserialize(lua_State *L) { + void * buffer = lua_touserdata(L,1); + if (buffer == NULL) { + return luaL_error(L, "deserialize null pointer"); + } + + lua_settop(L,0); + deserialize_buffer(L, buffer); + + // Need not free buffer + + return lua_gettop(L); +} + +static int seristring(lua_State *L) { + struct write_block b; + wb_init(&b, NULL); + pack_from(L,&b,0); + struct block * ret = wb_close(&b); + lua_settop(L,0); + lua_pushlightuserdata(L,ret); + lserialize(L); + wb_free(&b); + void *buffer = lua_touserdata(L, -2); + int sz = lua_tointeger(L, -1); + lua_pushlstring(L, buffer, sz); + free(buffer); + return 1; +} + +static int deseristring(lua_State *L) { + const char * buffer = luaL_checkstring(L, 1); + deserialize_buffer(L, (void *)buffer); + + return lua_gettop(L) - 1; +} + +int luaopen_pack(lua_State *L) { + luaL_checkversion(L); + luaL_Reg lpack_libs[] = { + { "encode", seristring }, + { "decode", deseristring }, + { NULL, NULL }, + }; + luaL_newlib(L, lpack_libs); + return 1; +} \ No newline at end of file diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index d7eb5eef..5e52c27b 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -322,7 +322,7 @@ static int create_client_unixsock(const char* path, size_t path_len) { int ret = connect(sockfd, (struct sockaddr*)&UN, sizeof(UN)); if (0 > ret) { - LOG("ERROR", strerror(errno)); + // LOG("ERROR", strerror(errno)); close(sockfd); return -1; } diff --git a/lualib/process/channel.lua b/lualib/process/channel.lua new file mode 100644 index 00000000..c6acb069 --- /dev/null +++ b/lualib/process/channel.lua @@ -0,0 +1,127 @@ +local TCP = require "internal.TCP" + +local dataset = require "process.dataset" + +local session = require "process.session" +local session_wakeup = session.wakeup + +local lpack = require "pack" +local lpack_encode = lpack.encode +local lpack_decode = lpack.decode + +local cf = require "cf" +local cf_fork = cf.fork + + +local ipairs = ipairs +local assert = assert + +local strunpack = string.unpack +local tconcat = table.concat +local tunpack = table.unpack + +local class = require "class" + +local Channel = class("Channel") + +function Channel:ctor() + self.id = dataset.get('pid') +end + +function Channel:send(...) + if not self.queue then + self.queue = {} + cf_fork(function () + for _, buf in ipairs(self.queue) do + self.sock:send(buf) + end + self.queue = nil + end) + end + self.queue[#self.queue + 1] = lpack_encode(...) + return true +end + +function Channel:recv() + local data = self.sock:recv(4) + if not data then + return self.sock:close() + end + local buffers = {data} + local len = strunpack("> 32) & 0xFFFFFFFF +end + +---comment 等待`sessionid` +---@param sessionid integer @会话`ID` +function session.wait(sessionid) + assert(mtype(sessionid) == 'integer', "Invalide `sessionid`") + session_map[sessionid] = cf_self() + return cf_wait() +end + +---comment 唤醒`sessionid` +---@param sessionid integer @会话`ID` +function session.wakeup(sessionid, ...) + local co = session_map[sessionid] + if co then + session_map[sessionid] = nil + cf_wakeup(co, ...) + return true + end + return false +end + +return session \ No newline at end of file diff --git a/lualib/process/utils.lua b/lualib/process/utils.lua new file mode 100644 index 00000000..11d5f9c7 --- /dev/null +++ b/lualib/process/utils.lua @@ -0,0 +1,11 @@ +local utils = {} + +function utils.copy(tab) + local t = {} + for k, v in pairs(tab) do + t[k] = v + end + return t +end + +return utils \ No newline at end of file diff --git a/lualib/process/worker.lua b/lualib/process/worker.lua new file mode 100644 index 00000000..963c05e6 --- /dev/null +++ b/lualib/process/worker.lua @@ -0,0 +1,43 @@ +local channel = require "process.channel" + +local dataset = require "process.dataset" + +local session = require "process.session" +local session_getsid = session.getsid +local sessionid_wait = session.wait + +local process = { isWorker = true, pid = dataset.get('pid') } + +local mchan = nil + +---comment 由框架完成进程初始化. +function process.init() + -- 构建进程通信通道 + mchan = channel:new() + mchan:connect('worker') +end + +---comment 向`Master`进程发送消息(非阻塞) +function process.send(...) + mchan:send(nil, ...) +end + +---comment 向`Master`进程发送消息(会阻塞) +function process.call(...) + local sessionid = session_getsid() + mchan:send(sessionid, ...) + return sessionid_wait(sessionid) +end + +---comment 注册进程事件 +---@param msg_type string @事件类型: `message` +---@param func function @回调函数: function(sessionid, ...) +function process.on(msg_type, func) + -- 注册消息事件 + if msg_type == 'message' and type(func) == 'function' then + mchan:setcb(func) + return + end +end + +return process \ No newline at end of file diff --git a/src/core.c b/src/core.c index 3abc8298..a808b6f7 100644 --- a/src/core.c +++ b/src/core.c @@ -1,5 +1,10 @@ #include "core.h" +#define MASTER (1) +#define WORKER (2) +#define IS_MASTER(mode) (mode == MASTER) +#define IS_WORKER(mode) (mode == WORKER) + #define LUALIBS_PATH \ "lualib/?.lua;;lualib/?/init.lua;;" \ "lualib/?.lc;;lualib/?/init.lc;;" \ @@ -21,69 +26,119 @@ "luaclib/?.dll;;luaclib/msys-?.dll;;" \ "3rd/?.dll;;3rd/msys-?.dll;;" +/* Master 进程 `Main`函数 */ +static const char* master_boot = "\n\ +local process = require 'process.master'\n\ +\ +process.init(...)\n\ +\ +local f = loadfile('script/boot.lua')\n\ +\ +if f then\n\ +\ + require 'cf'.fork(f)\n\ +\ +end\n\ +require 'cf'.wait()\n\ +"; + +/* Worker 进程 `Main`函数 */ +static const char* worker_boot = "\n\ +local process = require 'process.worker'\n\ +\ +process.init()\n\ +\ +local f = assert(loadfile(...))\n\ +\ +require 'cf'.fork(f)\n\ +\ +require 'cf'.wait()\n\ +"; /* 忽略信号 */ static void SIG_IGNORE(core_loop *loop, core_signal *signal, int revents){ - return ; + return ; } /* 退出信号 */ static void SIG_EXIT(core_loop *loop, core_signal *signal, int revents){ - if (ev_userdata(loop) && core_get_watcher_userdata(signal)) { - if (ev_userdata(loop)) - kill(0, SIGQUIT); - } - return exit(EXIT_SUCCESS); -} + /* 只有主进程退出的时候才需要通知子进程退出 */ + if (ev_userdata(loop)) + kill(0, SIGQUIT); -/* 子进程退出则打印异常 */ -static void CHILD_CB (core_loop *loop, ev_child *w, int revents){ - ev_child_stop(loop, w); - ev_feed_signal_event(loop, SIGQUIT); - if (w->rstatus){ - printf("[WARNING]: sub process %d exited with signal: %d\n", w->rpid, w->rstatus); - fflush(stdout); - } + return exit(EXIT_SUCCESS); } /* 内部异常 */ static void EV_ERROR_CB(const char *msg){ - LOG("ERROR", msg); - LOG("ERROR", strerror(errno)); - if (core_default_loop()) { - pid_t *pids = (pid_t *)ev_userdata(core_default_loop()); - if (pids) - kill(0, SIGKILL); - } - /* 减少无效打印, 专注错误提示 */ - return exit(EXIT_SUCCESS); + LOG("ERROR", msg); + LOG("ERROR", strerror(errno)); + if (core_default_loop()) { + pid_t *pids = (pid_t *)ev_userdata(core_default_loop()); + if (pids) + kill(0, SIGKILL); + } + /* 减少无效打印, 专注错误提示 */ + return exit(EXIT_SUCCESS); } /* 为libev内存hook注入日志 */ static void *EV_ALLOC(void *ptr, long nsize){ - if (nsize == 0) return xfree(ptr), NULL; - for (;;) { - void *newptr = xrealloc(ptr, nsize); - if (newptr) return newptr; - LOG("WARN", "Allocate failed, Sleep sometime.."); - sleep(1); - } + if (nsize == 0) return xfree(ptr), NULL; + for (;;) { + void *newptr = xrealloc(ptr, nsize); + if (newptr) return newptr; + LOG("WARN", "Allocate failed, Sleep sometime.."); + sleep(1); + } } /* 为lua内存hook注入日志 */ static void* L_ALLOC(void *ud, void *ptr, size_t osize, size_t nsize){ - /* 用户自定义数据 */ - (void)ud; (void)osize; - if (nsize == 0) return xfree(ptr), NULL; - for (;;) { - void *newptr = xrealloc(ptr, nsize); - if (newptr) return newptr; - LOG("WARN", "Allocate failed, Sleep sometime.."); - sleep(1); - } + /* 用户自定义数据 */ + (void)ud; (void)osize; + if (nsize == 0) return xfree(ptr), NULL; + for (;;) { + void *newptr = xrealloc(ptr, nsize); + if (newptr) return newptr; + LOG("WARN", "Allocate failed, Sleep sometime.."); + sleep(1); + } +} + +void init_lua_mode(lua_State *L, int mode) { + /* worker */ + if (IS_WORKER(mode) && getenv("cfadmin_isWorker")) { + lua_pushliteral(L, "worker"); + lua_createtable(L, 0, 0); + lua_pushliteral(L, "id"); + lua_pushinteger(L, getpid() - getppid()); + lua_rawset(L, -3); + lua_pushliteral(L, "pid"); + lua_pushinteger(L, getpid()); + lua_rawset(L, -3); + lua_pushliteral(L, "ppid"); + lua_pushinteger(L, getppid()); + lua_rawset(L, -3); + lua_pushliteral(L, "nprocess"); + lua_pushinteger(L, atoi(getenv("cfadmin_nprocess"))); + lua_rawset(L, -3); + lua_rawset(L, -3); + /* Master */ + } else if (IS_MASTER(mode)) { + lua_pushliteral(L, "master"); + lua_createtable(L, 0, 0); + lua_pushliteral(L, "pid"); + lua_pushinteger(L, getpid()); + lua_rawset(L, -3); + lua_pushliteral(L, "nprocess"); + lua_pushinteger(L, atoi(getenv("cfadmin_nprocess"))); + lua_rawset(L, -3); + lua_rawset(L, -3); + } } -void init_lua_libs(lua_State *L){ +void init_lua_libs(lua_State *L, int mode){ /* lua 标准库 */ luaL_openlibs(L); @@ -95,24 +150,8 @@ void init_lua_libs(lua_State *L){ lua_pushlightuserdata(L, NULL); lua_rawset(L, -3); - /* worker */ - if (getenv("cfadmin_isWorker") || getenv("cfadmin_isMaster")) { - lua_pushliteral(L, "worker"); - lua_createtable(L, 0, 3); - lua_pushliteral(L, "id"); - lua_pushinteger(L, getpid() - getppid()); - lua_rawset(L, -3); - lua_pushliteral(L, "pid"); - lua_pushinteger(L, getpid()); - lua_rawset(L, -3); - lua_pushliteral(L, "ppid"); - lua_pushinteger(L, getppid()); - lua_rawset(L, -3); - lua_pushliteral(L, "nprocess"); - lua_pushinteger(L, atoi(getenv("cfadmin_nprocess"))); - lua_rawset(L, -3); - lua_rawset(L, -3); - } + /*注入*/ + init_lua_mode(L, mode); lua_settop(L, 0); @@ -143,94 +182,121 @@ core_signal sigint; core_signal sigterm; core_signal sigquit; -static inline void signal_init(int* nprocess){ +static inline void signal_init(){ - /* 忽略连接中断信号 */ - core_signal_init(&sighup, SIG_IGNORE, SIGHUP); - core_signal_start(CORE_LOOP_ &sighup); + /* 忽略连接中断信号 */ + core_signal_init(&sighup, SIG_IGNORE, SIGHUP); + core_signal_start(CORE_LOOP_ &sighup); - /* 忽略无效的管道读/写信号 */ - core_signal_init(&sigpipe, SIG_IGNORE, SIGPIPE); - core_signal_start(CORE_LOOP_ &sigpipe); + /* 忽略无效的管道读/写信号 */ + core_signal_init(&sigpipe, SIG_IGNORE, SIGPIPE); + core_signal_start(CORE_LOOP_ &sigpipe); - /* 忽略Ctrl-Z操作信号 */ - core_signal_init(&sigtstp, SIG_IGNORE, SIGTSTP); - core_signal_start(CORE_LOOP_ &sigtstp); + /* 忽略Ctrl-Z操作信号 */ + core_signal_init(&sigtstp, SIG_IGNORE, SIGTSTP); + core_signal_start(CORE_LOOP_ &sigtstp); - /* TERM信号 显示退出 */ - core_signal_init(&sigterm, SIG_EXIT, SIGTERM); - core_signal_start(CORE_LOOP_ &sigterm); - if (nprocess) - core_set_watcher_userdata(&sigterm, nprocess); + /* TERM信号 显示退出 */ + core_signal_init(&sigterm, SIG_EXIT, SIGTERM); + core_signal_start(CORE_LOOP_ &sigterm); - /* INT信号 显示退出 */ - core_signal_init(&sigint, SIG_EXIT, SIGINT); - core_signal_start(CORE_LOOP_ &sigint); - if (nprocess) - core_set_watcher_userdata(&sigint, nprocess); + /* INT信号 显示退出 */ + core_signal_init(&sigint, SIG_EXIT, SIGINT); + core_signal_start(CORE_LOOP_ &sigint); - /* QUIT信号 显示退出 */ - core_signal_init(&sigquit, SIG_EXIT, SIGQUIT); - core_signal_start(CORE_LOOP_ &sigquit); - if (nprocess) - core_set_watcher_userdata(&sigquit, nprocess); + /* QUIT信号 显示退出 */ + core_signal_init(&sigquit, SIG_EXIT, SIGQUIT); + core_signal_start(CORE_LOOP_ &sigquit); } int core_worker_run(const char entry[]) { - /* hook libev 内存分配 */ - core_ev_set_allocator(EV_ALLOC); - /* hook 事件循环错误信息 */ - core_ev_set_syserr_cb(EV_ERROR_CB); - /* 初始化事件循环对象 */ - core_loop *loop = core_loop_fork(core_default_loop()); - - int status = 0; - - lua_State *L = lua_newstate(L_ALLOC, NULL); - if (!L) - core_exit(); - - init_lua_libs(L); - - status = luaL_loadfile(L, entry); - if (status > 1){ - LOG("ERROR", lua_tostring(L, -1)); - lua_close(L); - core_exit(); - } - - status = CO_RESUME(L, NULL, 0); - if (status > 1){ - LOG("ERROR", lua_tostring(L, -1)); - lua_close(L); - core_exit(); - } - - if (status == LUA_YIELD) - signal_init(NULL); - - return core_start(loop, 0); + /* hook libev 内存分配 */ + core_ev_set_allocator(EV_ALLOC); + /* hook 事件循环错误信息 */ + core_ev_set_syserr_cb(EV_ERROR_CB); + /* 初始化事件循环对象 */ + core_loop *loop = core_loop_fork(core_default_loop()); + + int status = 0; + + lua_State *L = lua_newstate(L_ALLOC, NULL); + if (!L) + core_exit(); + + init_lua_libs(L, WORKER); + + /* 根据进程运行模式选择不同的启动方式 */ + status = getenv("cfadmin_isWorker") ? + luaL_loadbufferx(L, worker_boot, strlen(worker_boot), "=[boot.lua]", "t") : luaL_loadfile(L, entry); + if (status > 1){ + LOG("ERROR", lua_tostring(L, -1)); + lua_close(L); + core_exit(); + } + + if (getenv("cfadmin_isWorker")) { + lua_pushstring(L, entry); + } + + status = CO_RESUME(L, NULL, lua_gettop(L) - 1); + if (status > 1){ + LOG("ERROR", lua_tostring(L, -1)); + lua_close(L); + core_exit(); + } + + if (status == LUA_YIELD) + signal_init(); + + return core_start(loop, 0); } int core_master_run(pid_t *pids, int* pidcount) { - /* hook libev 内存分配 */ - core_ev_set_allocator(EV_ALLOC); - /* hook 事件循环错误信息 */ - core_ev_set_syserr_cb(EV_ERROR_CB); - /* 初始化事件循环对象 */ - core_loop *loop = core_loop_fork(core_default_loop()); - /* 初始化信号 */ - signal_init(pidcount); - /* 设置pid */ - ev_set_userdata(loop, pids); - /* 监听子进程 */ - core_child childs[*pidcount]; - int index; - for (index = 0; index < *pidcount; index++) { - core_child_init(&childs[index], CHILD_CB, pids[index], 0); - core_child_start(loop, &childs[index]); - } - /* 初始化主进程 */ - return core_start(loop, 0); + /* hook libev 内存分配 */ + core_ev_set_allocator(EV_ALLOC); + /* hook 事件循环错误信息 */ + core_ev_set_syserr_cb(EV_ERROR_CB); + /* 初始化事件循环对象 */ + core_loop *loop = core_loop_fork(core_default_loop()); + /* 初始化信号 */ + signal_init(); + /* 设置pid */ + ev_set_userdata(loop, pids); + /* 监听子进程 */ + + lua_State *L = lua_newstate(L_ALLOC, NULL); + if (!L){ + LOG("ERROR", "New Lua State failed."); + kill(0, SIGQUIT); + core_exit(); + } + + /* 加载 Lua 标准库 */ + init_lua_libs(L, MASTER); + // LOG("INFO", boot); + /* 读取文件或默认运行 */ + int status = luaL_loadbufferx(L, master_boot, strlen(master_boot), "=[boot.lua]", "t"); + if (status != LUA_OK) { + printf("[%d], %s\n", status, lua_tostring(L, -1)); + lua_close(L); + kill(0, SIGQUIT); + core_exit(); + } + /* 注入子进程Pid */ + lua_createtable(L, 0, 0); + for (int index = 0; index < *pidcount; index++) { + lua_pushinteger(L, pids[index]); + lua_seti(L, -2, index + 1); + } + /* 开始执行 */ + status = CO_RESUME(L, NULL, lua_gettop(L) - 1); + if (status != LUA_YIELD){ + LOG("ERROR", lua_tostring(L, -1)); + lua_close(L); + kill(0, SIGQUIT); + core_exit(); + } + /* 初始化主进程 */ + return core_start(loop, 0); } \ No newline at end of file From 766323c0a15da6ddfe263be8c2fb101f29207e74 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 29 Nov 2021 22:09:24 +0800 Subject: [PATCH 857/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9socket=E7=9A=84buffer?= =?UTF-8?q?=E5=A4=A7=E5=B0=8F=E4=B8=8D=E5=86=8D=E8=87=AA=E5=8A=A8=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/TCP.lua | 50 +++++++++-------------------------------- 1 file changed, 10 insertions(+), 40 deletions(-) diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index 49770f56..c412aacd 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -137,6 +137,16 @@ function TCP:set_backlog(backlog) return self end +-- 设置Read buffer大小 +function TCP:set_read_buffer_size(size) + tcp_set_read_buf(self.fd, size) +end + +-- 设置Write buffer大小 +function TCP:set_write_buffer_size(size) + tcp_set_write_buf(self.fd, size) +end + -- 开启验证 function TCP:ssl_set_verify() if not self.ssl or not self.ssl_ctx then @@ -244,16 +254,6 @@ function TCP:send(buf) return wlen == #buf end assert(not self.send_co, "[TCP ERROR]: Try to call the 'send' method multiple times.") - -- 缓解发送大量数据集的时候调用频繁的问题 - if not self.wsize or self.wsize < #buf then - self.wsize = #buf - if self.wsize > (1 << 16) then - if self.wsize > (1 << 20) then - self.wsize = 1 << 20 - end - tcp_set_write_buf(self.fd, self.wsize); - end - end local co = co_self() self.SEND_IO = tcp_pop() self.send_current_co = co_self() @@ -286,16 +286,6 @@ function TCP:ssl_send(buf) return wlen == #buf end assert(not self.send_co, "[TCP ERROR]: Try to call the 'send' method multiple times.") - -- 缓解发送大量数据集的时候调用频繁的问题 - if not self.wsize or self.wsize < #buf then - self.wsize = #buf - if self.wsize > (1 << 16) then - if self.wsize > (1 << 20) then - self.wsize = 1 << 20 - end - tcp_set_write_buf(self.fd, self.wsize); - end - end local co = co_self() self.SEND_IO = tcp_pop() self.send_current_co = co_self() @@ -452,16 +442,6 @@ function TCP:recv(bytes) return data, len end assert(not self.read_co, "[TCP ERROR]: Try to call the 'recv' method multiple times.") - -- 优化大数据集的调用次数太多的问题 - if not self.rsize or self.rsize < bytes then - self.rsize = bytes - if self.rsize > (1 << 16) then - if self.rsize > (1 << 20) then - self.rsize = 1 << 20 - end - tcp_set_read_buf(fd, self.rsize); - end - end local coctx = co_self() self.READ_IO = tcp_pop() self.read_current_co = co_self() @@ -501,16 +481,6 @@ function TCP:ssl_recv(bytes) return buf, len end assert(not self.read_co, "[TCP ERROR]: Try to call the 'recv' method multiple times.") - -- 优化大数据集的调用次数太多的问题 - if not self.rsize or self.rsize < bytes then - self.rsize = bytes - if self.rsize > (1 << 16) then - if self.rsize > (1 << 20) then - self.rsize = 1 << 20 - end - tcp_set_read_buf(self.fd, self.rsize); - end - end local coctx = co_self() self.READ_IO = tcp_pop() self.read_current_co = co_self() From 71c4070986239d2868ef48a6e81d2ca1c332a7d6 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 29 Nov 2021 22:10:58 +0800 Subject: [PATCH 858/956] =?UTF-8?q?=E4=BC=98=E5=8C=96Process=E5=BA=93?= =?UTF-8?q?=E7=9A=84=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/process/channel.lua | 42 ++++++++++++++++++++++++++------------ lualib/process/master.lua | 8 +++++--- lualib/process/worker.lua | 9 ++++---- 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/lualib/process/channel.lua b/lualib/process/channel.lua index c6acb069..b512d860 100644 --- a/lualib/process/channel.lua +++ b/lualib/process/channel.lua @@ -1,4 +1,7 @@ local TCP = require "internal.TCP" +local sock_recv = TCP.recv +local sock_send = TCP.send +local sock_close = TCP.close local dataset = require "process.dataset" @@ -11,14 +14,14 @@ local lpack_decode = lpack.decode local cf = require "cf" local cf_fork = cf.fork - +local cf_wait = cf.wait +local cf_wakeup = cf.wakeup local ipairs = ipairs -local assert = assert - local strunpack = string.unpack local tconcat = table.concat -local tunpack = table.unpack + +local MAX_BUFFER_SIZE = 4194304 local class = require "class" @@ -31,21 +34,31 @@ end function Channel:send(...) if not self.queue then self.queue = {} - cf_fork(function () - for _, buf in ipairs(self.queue) do - self.sock:send(buf) + local sock = self.sock + self.writer = cf_fork(function () + while true do + for _, buf in ipairs(self.queue) do + sock_send(sock, buf) + end + self.queue = {} + self.waited = true + cf_wait() end - self.queue = nil end) end + if self.waited then + self.waited = nil + cf_wakeup(self.writer) + end self.queue[#self.queue + 1] = lpack_encode(...) return true end function Channel:recv() - local data = self.sock:recv(4) + local sock = self.sock + local data = sock_recv(sock, 4) if not data then - return self.sock:close() + return sock_close(sock) end local buffers = {data} local len = strunpack(" Date: Mon, 29 Nov 2021 22:26:02 +0800 Subject: [PATCH 859/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=AE=A1=E9=81=93?= =?UTF-8?q?=E4=B8=8Esocket=E7=BC=93=E5=86=B2=E5=8C=BA=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 5e52c27b..e4a0ab92 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -1165,7 +1165,7 @@ static int tcp_set_write_buf(lua_State *L) { if (fd < 0) return 0; int bsize = lua_tointeger(L, 2); - if (bsize < (1 << 16) || bsize > (1 << 20)) + if (bsize <= 65535) return 0; #if defined(SO_SNDBUF) @@ -1182,7 +1182,7 @@ static int tcp_set_read_buf(lua_State *L) { if (fd < 0) return 0; int bsize = lua_tointeger(L, 2); - if (bsize < (1 << 16) || bsize > (1 << 20)) + if (bsize <= 65535) return 0; #if defined(SO_RCVBUF) From b77b1a8dc425d11b35a9a41f08faf5f50f5fcfb9 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 29 Nov 2021 23:28:04 +0800 Subject: [PATCH 860/956] =?UTF-8?q?=E4=BC=98=E5=8C=96process=E5=B9=BF?= =?UTF-8?q?=E6=92=AD=E6=B6=88=E6=81=AF=E7=BC=96=E7=A0=81=E6=AC=A1=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/process/channel.lua | 7 +++---- lualib/process/master.lua | 10 ++++++---- lualib/process/worker.lua | 7 +++++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/lualib/process/channel.lua b/lualib/process/channel.lua index b512d860..60d3bbe9 100644 --- a/lualib/process/channel.lua +++ b/lualib/process/channel.lua @@ -31,7 +31,7 @@ function Channel:ctor() self.id = dataset.get('pid') end -function Channel:send(...) +function Channel:send(data) if not self.queue then self.queue = {} local sock = self.sock @@ -50,8 +50,7 @@ function Channel:send(...) self.waited = nil cf_wakeup(self.writer) end - self.queue[#self.queue + 1] = lpack_encode(...) - return true + self.queue[#self.queue + 1] = data end function Channel:recv() @@ -132,7 +131,7 @@ function Channel:connect(mode) if self.sock:connect_ex(sockname) then self.sock:set_read_buffer_size(MAX_BUFFER_SIZE) self.sock:set_write_buffer_size(MAX_BUFFER_SIZE) - self:send(self.id) + self:send(lpack_encode(self.id)) break end end diff --git a/lualib/process/master.lua b/lualib/process/master.lua index d080aebd..6cf1d8bf 100644 --- a/lualib/process/master.lua +++ b/lualib/process/master.lua @@ -5,6 +5,9 @@ local dataset = require "process.dataset" local channel = require "process.channel" local channel_send = channel.send +local lpack = require "pack" +local lpack_encode = lpack.encode + local session = require "process.session" local session_get_pid = session.get_pid @@ -29,17 +32,16 @@ end ---comment 向所有`Worker`进程广播消息 function process.broadcast(...) + local data = lpack_encode(nil, ...) for _, chan in pairs(channels) do - channel_send(chan, nil, ...) + channel_send(chan, data) end end ---comment 响应消息 ---@param sessionid integer @响应`sessionid`的消息 function process.ret(sessionid, ...) - local pid = session_get_pid(assert(sessionid, "Invalid `sessionid`")) - local chan = channels[pid] - channel_send(chan, sessionid, ...) + channel_send(channels[session_get_pid(assert(sessionid, "Invalid `sessionid`"))], lpack_encode(sessionid, ...)) end ---comment 注册进程事件 diff --git a/lualib/process/worker.lua b/lualib/process/worker.lua index fe1fefdb..c2e1be45 100644 --- a/lualib/process/worker.lua +++ b/lualib/process/worker.lua @@ -3,6 +3,9 @@ local dataset = require "process.dataset" local channel = require "process.channel" local channel_send = channel.send +local lpack = require "pack" +local lpack_encode = lpack.encode + local session = require "process.session" local session_getsid = session.getsid local sessionid_wait = session.wait @@ -20,13 +23,13 @@ end ---comment 向`Master`进程发送消息(非阻塞) function process.send(...) - channel_send(mchan, nil, ...) + channel_send(mchan, lpack_encode(nil, ...)) end ---comment 向`Master`进程发送消息(会阻塞) function process.call(...) local sessionid = session_getsid() - channel_send(mchan, sessionid, ...) + channel_send(mchan, lpack_encode(sessionid, ...)) return sessionid_wait(sessionid) end From 7e616cde3e4ed4081403519e3052d6d441876446 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 8 Dec 2021 22:18:24 +0800 Subject: [PATCH 861/956] =?UTF-8?q?=E4=BC=98=E5=8C=96process=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E7=9A=84=E6=8E=A5=E6=94=B6=E4=B8=8E=E5=8F=91=E9=80=81?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/process/channel.lua | 40 +++++++++++++++++++++++++------------- lualib/process/master.lua | 6 ++++++ 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/lualib/process/channel.lua b/lualib/process/channel.lua index 60d3bbe9..ba35bed1 100644 --- a/lualib/process/channel.lua +++ b/lualib/process/channel.lua @@ -17,7 +17,6 @@ local cf_fork = cf.fork local cf_wait = cf.wait local cf_wakeup = cf.wakeup -local ipairs = ipairs local strunpack = string.unpack local tconcat = table.concat @@ -32,17 +31,23 @@ function Channel:ctor() end function Channel:send(data) - if not self.queue then + if not self.writer then self.queue = {} - local sock = self.sock self.writer = cf_fork(function () + local sock = self.sock + local qlen, queue = #self.queue, self.queue while true do - for _, buf in ipairs(self.queue) do - sock_send(sock, buf) - end self.queue = {} - self.waited = true - cf_wait() + for idx = 1, qlen do + sock_send(sock, queue[idx]) + end + queue = self.queue + qlen = #queue + if qlen == 0 then + self.waited = true + cf_wait() + qlen = #queue + end end end) end @@ -59,22 +64,31 @@ function Channel:recv() if not data then return sock_close(sock) end - local buffers = {data} local len = strunpack(" Date: Wed, 8 Dec 2021 22:30:33 +0800 Subject: [PATCH 862/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0random=E6=89=A9?= =?UTF-8?q?=E5=B1=95=E5=88=B0utils?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/utils/random.lua | 173 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 lualib/utils/random.lua diff --git a/lualib/utils/random.lua b/lualib/utils/random.lua new file mode 100644 index 00000000..7829c461 --- /dev/null +++ b/lualib/utils/random.lua @@ -0,0 +1,173 @@ +local type = type +local assert = assert + +local ssub = string.sub +local char = string.char + +local mlog = math.log +local mexp = math.exp +local msqrt = math.sqrt +local mcos = math.cos +local msin = math.sin +local mrandom = math.random +local mrandomseed = math.randomseed + +local NV_MAGIC = 4 * mexp(-0.5) / msqrt(2.0) + +local PI2 = 2 * math.pi +local gauss_next = nil + +local random = {} + +---comment @同`math.randomseed` +function random.randomseed(...) + return mrandomseed(...) +end + +---comment @同`math.random` +---@return number +function random.random(...) + return mrandom(...) +end + +---comment 生成指定数量的字符数组 +---@param num integer @样品数量 +---@return table +function random.generatechar(num) + local list = {} + for i = 1, num do + list[i] = char(mrandom(0, 255)) + end + return list +end + +---comment 生成指定数量的随机整数数组 +---@param x integer @随机数的最小值, 结果包含该值. +---@param y integer @随机数的最大值, 结果包含该值. +---@param num integer @样品数量 +---@return table +function random.generateint (x, y, num) + local list = {} + for i = 1, num do + list[i] = mrandom(x, y) + end + return list +end + +---comment 生成指定数量的随机实数数组 +---@param x integer @随机数的最小值, 结果包含该值. +---@param y integer @随机数的最大值, 结果包含该值. +---@param num integer @样品数量 +---@return table +function random.generatefloat (x, y, num) + local list = {} + for i = 1, num do + list[i] = random.uniform(x, y) + end + return list +end + +---comment 将随机生成一个实数,它在`x`, `y`范围内。 +---@param x integer @随机数的最小值, 结果包含该值. +---@param y integer @随机数的最大值, 结果包含该值. +---@return number +function random.uniform(x, y) + return mrandom(x or 0, y or 4294967296) + mrandom() +end + +---comment 返回给定`sequence`内的随机项。 +---@param sequence string | table @可以是`数组`或`字符串`. +---@return any +function random.choice(sequence) + local tp = type(sequence) + if tp ~= 'string' and tp ~= 'table' then + return + end + local len = #sequence + if len < 1 then + return + end + local rv = mrandom(1, len) + if tp == 'table' then + return sequence[rv] + end + return ssub(sequence, rv, rv) +end + +---comment 打乱给定`sequence`内的顺序 +---@param sequence table @数组结构 +function random.shuffle(sequence) + local len = #assert(sequence, "Invalid `sequence`") + for i = 1, len, 1 do + local j = mrandom(1, len) + sequence[i], sequence[j] = sequence[j], sequence[i] + end + return sequence +end + +---comment 根据给定`sequence`选取`num`个样品 +---@param sequence table @样品数组 +---@param num integer @样品数量 +function random.sample(sequence, num) + local len = #assert(sequence, "Invalid `sequence`") + assert(num and num <= len, "Invalid `num` or `sample` larger than population.") + local idx_list = {} + for i = 1, len do + idx_list[i] = i + end + random.shuffle(idx_list) + local list = {} + for i = 1, num do + list[i] = sequence[idx_list[i]] + end + return list +end + +---comment 正态分布 +---@param mean number @平均值 +---@param sigma number @标准差 +---@return number +function random.normalvariate(mean, sigma) + local z, zz, u1, u2 + while true do + u1 = mrandom() + u2 = 1.0 - mrandom() + z = NV_MAGIC * (u1 - 0.5) / u2 + zz = z * z / 4.0 + if zz <= -mlog(u2) then + break + end + end + return mean + z * sigma +end + +---comment 对数正态分布 +---@param mean number @平均值 +---@param sigma number @标准差 +---@return number +function random.lognormvariate(mean, sigma) + return mexp(random.normalvariate(mean, sigma)) +end + +---comment 指数分布 +---@param lambd number @lambd 是1.0除以所需的平均值. +function random.expovariate(lambd) + return -mlog(1.0 - mrandom()) / lambd +end + +---comment 高斯分布 +---@param mean number @平均值 +---@param sigma number @标准差 +function random.gauss(mean, sigma) + local z = gauss_next + gauss_next = nil + if not z then + local x2pi = mrandom() * PI2 + local g2rad = msqrt(-2.0 * mlog(1.0 - mrandom())) + z = mcos(x2pi) * g2rad + gauss_next = msin(x2pi) * g2rad + end + return mean + z * sigma +end + +return random \ No newline at end of file From 8067eb9020183def98d743bdc2a6407dac1ef8d5 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 9 Dec 2021 16:12:40 +0800 Subject: [PATCH 863/956] =?UTF-8?q?=E4=BC=98=E5=8C=96process=E5=86=85?= =?UTF-8?q?=E9=83=A8channel=E5=AE=9E=E7=8E=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/process/channel.lua | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/lualib/process/channel.lua b/lualib/process/channel.lua index ba35bed1..69052750 100644 --- a/lualib/process/channel.lua +++ b/lualib/process/channel.lua @@ -17,8 +17,8 @@ local cf_fork = cf.fork local cf_wait = cf.wait local cf_wakeup = cf.wakeup +local assert = assert local strunpack = string.unpack -local tconcat = table.concat local MAX_BUFFER_SIZE = 4194304 @@ -73,24 +73,8 @@ function Channel:recv() if not buf then return sock_close(sock) end - if bsize == len then - return true, lpack_decode( data .. buf ) - end - local index = 3 - local buffers = {data, buf, nil} - while true do - buf, bsize = sock_recv(sock, len) - if not buf then - return sock_close(sock) - end - buffers[index] = buf - if bsize == len then - break - end - len = len - bsize - index = index + 1 - end - return true, lpack_decode(tconcat(buffers)) + assert(bsize == len, "Invalid message.") + return true, lpack_decode(data .. buf) end function Channel:setcb(func) @@ -108,10 +92,8 @@ function Channel:setcb(func) cf_fork(func, sessionid, ...) return true end - while true do - if not dispatch(self:recv()) then - break - end + while dispatch(self:recv()) do + -- Nothing todo. end end) end @@ -153,4 +135,4 @@ function Channel:connect(mode) end end -return Channel \ No newline at end of file +return Channel From 0e09abe74c52f7e1e2b95ebeb92e3300cf7a1be2 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 12 Dec 2021 01:25:31 +0800 Subject: [PATCH 864/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=AD=90=E8=BF=9B?= =?UTF-8?q?=E7=A8=8B=E9=80=80=E5=87=BA=E5=BC=82=E5=B8=B8=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/core.c b/src/core.c index a808b6f7..1ce0dedf 100644 --- a/src/core.c +++ b/src/core.c @@ -65,7 +65,6 @@ static void SIG_EXIT(core_loop *loop, core_signal *signal, int revents){ /* 只有主进程退出的时候才需要通知子进程退出 */ if (ev_userdata(loop)) kill(0, SIGQUIT); - return exit(EXIT_SUCCESS); } @@ -73,8 +72,8 @@ static void SIG_EXIT(core_loop *loop, core_signal *signal, int revents){ static void EV_ERROR_CB(const char *msg){ LOG("ERROR", msg); LOG("ERROR", strerror(errno)); - if (core_default_loop()) { - pid_t *pids = (pid_t *)ev_userdata(core_default_loop()); + if (CORE_LOOP) { + pid_t *pids = (pid_t *)ev_userdata(CORE_LOOP); if (pids) kill(0, SIGKILL); } @@ -196,18 +195,21 @@ static inline void signal_init(){ core_signal_init(&sigtstp, SIG_IGNORE, SIGTSTP); core_signal_start(CORE_LOOP_ &sigtstp); - /* TERM信号 显示退出 */ - core_signal_init(&sigterm, SIG_EXIT, SIGTERM); - core_signal_start(CORE_LOOP_ &sigterm); + if (ev_userdata(CORE_LOOP)) { + + /* TERM信号 显示退出 */ + core_signal_init(&sigterm, SIG_EXIT, SIGTERM); + core_signal_start(CORE_LOOP_ &sigterm); - /* INT信号 显示退出 */ - core_signal_init(&sigint, SIG_EXIT, SIGINT); - core_signal_start(CORE_LOOP_ &sigint); + /* INT信号 显示退出 */ + core_signal_init(&sigint, SIG_EXIT, SIGINT); + core_signal_start(CORE_LOOP_ &sigint); - /* QUIT信号 显示退出 */ - core_signal_init(&sigquit, SIG_EXIT, SIGQUIT); - core_signal_start(CORE_LOOP_ &sigquit); + /* QUIT信号 显示退出 */ + core_signal_init(&sigquit, SIG_EXIT, SIGQUIT); + core_signal_start(CORE_LOOP_ &sigquit); + } } int core_worker_run(const char entry[]) { @@ -216,7 +218,7 @@ int core_worker_run(const char entry[]) { /* hook 事件循环错误信息 */ core_ev_set_syserr_cb(EV_ERROR_CB); /* 初始化事件循环对象 */ - core_loop *loop = core_loop_fork(core_default_loop()); + core_loop *loop = core_loop_fork(CORE_LOOP); int status = 0; @@ -258,12 +260,11 @@ int core_master_run(pid_t *pids, int* pidcount) { /* hook 事件循环错误信息 */ core_ev_set_syserr_cb(EV_ERROR_CB); /* 初始化事件循环对象 */ - core_loop *loop = core_loop_fork(core_default_loop()); - /* 初始化信号 */ - signal_init(); - /* 设置pid */ + core_loop *loop = core_loop_fork(CORE_LOOP); + /* 设置pid */ ev_set_userdata(loop, pids); - /* 监听子进程 */ + /* 初始化信号 */ + signal_init(); lua_State *L = lua_newstate(L_ALLOC, NULL); if (!L){ @@ -274,11 +275,10 @@ int core_master_run(pid_t *pids, int* pidcount) { /* 加载 Lua 标准库 */ init_lua_libs(L, MASTER); - // LOG("INFO", boot); + /* 读取文件或默认运行 */ - int status = luaL_loadbufferx(L, master_boot, strlen(master_boot), "=[boot.lua]", "t"); + int status = luaL_loadbufferx(L, master_boot, strlen(master_boot), "=[main.lua]", "t"); if (status != LUA_OK) { - printf("[%d], %s\n", status, lua_tostring(L, -1)); lua_close(L); kill(0, SIGQUIT); core_exit(); From 4fec151fe92ba91f6340ca83a83e6a6293dcfd15 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 16 Dec 2021 16:48:43 +0800 Subject: [PATCH 865/956] Update channel.lua MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修改入口文件不存在会抛出的异常. --- lualib/process/channel.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lualib/process/channel.lua b/lualib/process/channel.lua index 69052750..d9d6a7e0 100644 --- a/lualib/process/channel.lua +++ b/lualib/process/channel.lua @@ -112,6 +112,9 @@ function Channel:connect(mode) chan.sock:set_read_buffer_size(MAX_BUFFER_SIZE) chan.sock:set_write_buffer_size(MAX_BUFFER_SIZE) local _, id = chan:recv() + if not id then + return chan.sock:close() + end chan.id = id channels[chan.id] = chan num = num + 1 From f279566bc165c18cde0fd9ec4d3f8a7776e5fae3 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 16 Dec 2021 17:26:08 +0800 Subject: [PATCH 866/956] Update core.c --- src/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core.c b/src/core.c index 1ce0dedf..9b8e3039 100644 --- a/src/core.c +++ b/src/core.c @@ -230,7 +230,7 @@ int core_worker_run(const char entry[]) { /* 根据进程运行模式选择不同的启动方式 */ status = getenv("cfadmin_isWorker") ? - luaL_loadbufferx(L, worker_boot, strlen(worker_boot), "=[boot.lua]", "t") : luaL_loadfile(L, entry); + luaL_loadbufferx(L, worker_boot, strlen(worker_boot), "=[worker.lua]", "t") : luaL_loadfile(L, entry); if (status > 1){ LOG("ERROR", lua_tostring(L, -1)); lua_close(L); @@ -277,7 +277,7 @@ int core_master_run(pid_t *pids, int* pidcount) { init_lua_libs(L, MASTER); /* 读取文件或默认运行 */ - int status = luaL_loadbufferx(L, master_boot, strlen(master_boot), "=[main.lua]", "t"); + int status = luaL_loadbufferx(L, master_boot, strlen(master_boot), "=[master.lua]", "t"); if (status != LUA_OK) { lua_close(L); kill(0, SIGQUIT); @@ -299,4 +299,4 @@ int core_master_run(pid_t *pids, int* pidcount) { } /* 初始化主进程 */ return core_start(loop, 0); -} \ No newline at end of file +} From 6670ca90377cc352c8c757659a43955adac9ec4d Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 16 Dec 2021 17:28:46 +0800 Subject: [PATCH 867/956] Update channel.lua --- lualib/process/channel.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/lualib/process/channel.lua b/lualib/process/channel.lua index d9d6a7e0..6a7a76cc 100644 --- a/lualib/process/channel.lua +++ b/lualib/process/channel.lua @@ -113,6 +113,7 @@ function Channel:connect(mode) chan.sock:set_write_buffer_size(MAX_BUFFER_SIZE) local _, id = chan:recv() if not id then + os.remove(sockname) return chan.sock:close() end chan.id = id From fcdd27aded44d5fa7ce670f268a29dfb24844e36 Mon Sep 17 00:00:00 2001 From: Nuctori <1018570975@qq.com> Date: Wed, 22 Dec 2021 01:10:38 +0800 Subject: [PATCH 868/956] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=82=AE=E4=BB=B6?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E5=8F=91=E9=80=81=E8=80=85=E5=90=8D?= =?UTF-8?q?=E7=A7=B0=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/smtp/init.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lualib/protocol/smtp/init.lua b/lualib/protocol/smtp/init.lua index 1a79f935..672fa864 100644 --- a/lualib/protocol/smtp/init.lua +++ b/lualib/protocol/smtp/init.lua @@ -38,6 +38,7 @@ function smtp:ctor (opt) self.port = opt.port self.to = opt.to self.from = opt.from + self.fromName = opt.fromName self.mime = opt.mime self.subject = opt.subject self.content = opt.content @@ -168,7 +169,7 @@ function smtp:send_content () self.mime = "MIME-Version: 1.0\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Transfer-Encoding: base64\r\n" end -- 发送邮件实体头部 - local ok = self:send(concat({fmt("From: <%s>\r\n", self.from), fmt("To: <%s>\r\n", self.to), fmt("Subject: %s\r\n", self.subject), self.mime, CRLF})) + local ok = self:send(concat({fmt("From: %s <%s>\r\n", self.fromName or "", self.from), fmt("To: <%s>\r\n", self.to), fmt("Subject: %s\r\n", self.subject), self.mime, CRLF})) if not ok then return nil, "[MAIL CONTENT ERROR]: 发送Content Headers失败." end From 286762eaa8b991ae98edbe617eff07b0167fd306 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 26 Dec 2021 16:46:10 +0800 Subject: [PATCH 869/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dpeek=E7=9A=84?= =?UTF-8?q?=E9=95=BF=E5=BA=A6=E5=88=A4=E6=96=AD=E9=97=AE=E9=A2=98.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index e4a0ab92..7055ca8c 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -527,11 +527,7 @@ static int tcp_peek(lua_State *L) { if (0 >= fd) return 0; int bsize = lua_tointeger(L, 2); - char* buffer = NULL; - if (bsize <= MBSIZE) - buffer = alloca(bsize); - else - buffer = lua_newuserdata(L, bsize); + char* buffer = lua_newuserdata(L, bsize); if (0 == lua_toboolean(L, 3)) { lua_pushinteger(L, read(fd, buffer, bsize)); @@ -551,8 +547,8 @@ static int tcp_peek(lua_State *L) { lua_pushstring(L, strerror(errno)); break; } - lua_pushlstring(L, buffer, bsize); - lua_pushinteger(L, bsize); + lua_pushlstring(L, buffer, len); + lua_pushinteger(L, len); break; } return 2; @@ -565,11 +561,7 @@ static int tcp_sslpeek(lua_State *L) { if (!ssl) return 0; int bsize = lua_tointeger(L, 2); - char* buffer = NULL; - if (bsize <= MBSIZE) - buffer = alloca(bsize); - else - buffer = lua_newuserdata(L, bsize); + char* buffer = lua_newuserdata(L, bsize); if (0 == lua_toboolean(L, 3)) { lua_pushinteger(L, SSL_read(ssl, buffer, bsize)); @@ -590,8 +582,8 @@ static int tcp_sslpeek(lua_State *L) { lua_pushstring(L, strerror(errno)); break; } - lua_pushlstring(L, buffer, bsize); - lua_pushinteger(L, bsize); + lua_pushlstring(L, buffer, len); + lua_pushinteger(L, len); break; } return 2; From b4332c280f4f7b5d6afbf605a24e6a535bff0c06 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 26 Dec 2021 21:53:52 +0800 Subject: [PATCH 870/956] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core.c b/src/core.c index 9b8e3039..3a4a887c 100644 --- a/src/core.c +++ b/src/core.c @@ -285,7 +285,8 @@ int core_master_run(pid_t *pids, int* pidcount) { } /* 注入子进程Pid */ lua_createtable(L, 0, 0); - for (int index = 0; index < *pidcount; index++) { + int index; + for (index = 0; index < *pidcount; index++) { lua_pushinteger(L, pids[index]); lua_seti(L, -2, index + 1); } From d1e662c15df241551c6815661b3c83a8f7920b77 Mon Sep 17 00:00:00 2001 From: Nuctori <1018570975@qq.com> Date: Sun, 26 Dec 2021 22:26:46 +0800 Subject: [PATCH 871/956] =?UTF-8?q?=E6=B7=BB=E5=8A=A0build=E6=97=B6?= =?UTF-8?q?=E5=9B=BD=E5=86=85=E8=87=AA=E5=8A=A8=E9=80=89=E6=8B=A9=E9=95=9C?= =?UTF-8?q?=E5=83=8F=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/build.sh b/build.sh index 29211319..136abc0c 100755 --- a/build.sh +++ b/build.sh @@ -16,14 +16,19 @@ current=`pwd` rm -rf build && mkdir build && cd build -# git clone https://gitee.com/CandyMi/lua -b v5.4.3 -git clone https://github.com/CandyMi/lua -b v5.4.3 - -# git clone https://gitee.com/CandyMi/libev -b v4.33 -git clone https://github.com/CandyMi/libev -b v4.33 - -# git clone https://gitee.com/CandyMi/libeio -git clone https://github.com/CandyMi/libeio +# 通过时区,自动选择镜像源 +timeArea=`date +"%Z %z" ` +if [[ $timeArea == "CST +0800" ]] +then + git clone https://gitee.com/CandyMi/lua -b v5.4.3 + git clone https://gitee.com/CandyMi/libev -b v4.33 + git clone https://gitee.com/CandyMi/libeio + +else + git clone https://github.com/CandyMi/lua -b v5.4.3 + git clone https://github.com/CandyMi/libev -b v4.33 + git clone https://github.com/CandyMi/libeio +fi echo "========== build libev ==========" && cd ${current}/build/libev && sh autogen.sh && ./configure --prefix=/usr/local --enable-shared=no --with-pic && From cd9bd50d3eebda2af6fa6c580edd2c2ea2a848b1 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 30 Dec 2021 21:37:24 +0800 Subject: [PATCH 872/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0DB=E4=B8=8Emysql=20dr?= =?UTF-8?q?iver=E7=9A=84=E9=A2=84=E5=A4=84=E7=90=86(=E7=BC=96=E8=AF=91)?= =?UTF-8?q?=E8=AF=AD=E6=B3=95=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/DB/mysql.lua | 122 +++++++++---- lualib/protocol/mysql.lua | 371 +++++++++++++++++++++++++++++++++++++- 2 files changed, 450 insertions(+), 43 deletions(-) diff --git a/lualib/DB/mysql.lua b/lualib/DB/mysql.lua index 44f3e2d1..3094f430 100644 --- a/lualib/DB/mysql.lua +++ b/lualib/DB/mysql.lua @@ -9,7 +9,6 @@ local Log = log:new({ dump = true, path = 'DB'}) local co = require "internal.Co" local co_self = co.self local co_wait = co.wait -local co_spawn = co.spawn local co_wakeup = co.wakeup local type = type @@ -95,6 +94,81 @@ local function run_query(self, query) return ret, err end +local function run_execute(self, query, ...) + local db, ret, err + while 1 do + db = pop_db(self) + if db then + ret, err = db:execute(query, ...) + if db.state then + break + end + db:close() + self.current = self.current - 1 + db, ret, err = nil, nil, nil + end + end + local co = pop_wait(self) + if co then + co_wakeup(co, db) + else + add_db(self, db) + end + return ret, err +end + +-- 事务session +local session = class("session") + +function session:ctor(opt) + self.db = opt.db + self.over = false +end + +function session:execute(sql, ...) + if self.over then + return nil, "Please use `return session:rollback()` or `return session:commit()` after the transaction process is over or Process error." + end + -- assert(self.db.state, "MySQL transaction session closed. 1") + local ret, err = self.db:execute(sql, ...) + assert(self.db.state, "MySQL transaction session closed. 1") + return ret, err +end + +function session:query(sql) + if self.over then + return nil, "Please use `return session:rollback()` or `return session:commit()` after the transaction process is over or Process error." + end + -- assert(self.db.state, "MySQL transaction session closed. 1") + local ret, err = self.db:query(sql) + assert(self.db.state, "MySQL transaction session closed. 2") + return ret, err +end + +function session:rollback() + if self.over then + return + end + assert(self.db.state, "MySQL transaction session closed. 3") + self.db:query("rollback; set autocommit=1;") + assert(self.db.state, "MySQL transaction session closed. 4") + self.over = true + self.db = nil + return { state = "rollback" } +end + +function session:commit() + if self.over then + return + end + assert(self.db.state, "MySQL transaction session closed. 5") + self.db:query("set autocommit=1;") + assert(self.db.state, "MySQL transaction session closed. 6") + self.over = true + self.db = nil + return { state = "successed" } +end + local DB = class("DB") function DB:ctor(opt) @@ -142,36 +216,8 @@ function DB:transaction(f) end db, ret, err = nil, nil, nil end - -- 每个事务都有独立的session - local session = { nil, nil, nil } - session.query = function (self, sql) - assert(self and self == session, "Must use the syntax of `session:query()`") - if self.over then - return nil, "Please use `return session:rollback()` or `return session:commit()` after the transaction process is over or Process error." - end - assert(db.state, "MySQL transaction session closed. 1") - local ret, err = db:query(sql) - assert(db.state, "MySQL transaction session closed. 2") - return ret, err - end - session.rollback = function ( self ) - assert(self and self == session, "Must use the syntax of `session:rollback()`") - assert(db.state, "MySQL transaction session closed. 3") - db:query("rollback; set autocommit=1;") - assert(db.state, "MySQL transaction session closed. 4") - self.over = true - return { state = "rollback" } - end - session.commit = function ( self ) - assert(self and self == session, "Must use the syntax of `session:commit()`") - assert(db.state, "MySQL transaction session closed. 5") - db:query("set autocommit=1;") - assert(db.state, "MySQL transaction session closed. 6") - self.over = true - return { state = "successed" } - end - local ok, info = xpcall(f, traceback, session) + local ok, info = xpcall(f, traceback, session:new{ db = db }) if not ok then -- 如果在自定义事务流程的内部发生了错误 if not db.state then @@ -179,9 +225,7 @@ function DB:transaction(f) db:close() local co = pop_wait(self) if co then - co_spawn(function ( ... ) - co_wakeup(co, pop_db(self)) - end) + co_wakeup(co, pop_db(self)) end return nil, info end @@ -203,7 +247,7 @@ function DB:transaction(f) else add_db(self, db) end - error("Must return after transaction ends`session:commit()`or`session:rollback()`.") + error("After the transaction ends, either 'return session:commit()' or 'return session:rollback()' must be required.") end local co = pop_wait(self) if co then @@ -214,10 +258,16 @@ function DB:transaction(f) return info.state == "successed" and true or false end --- 原始查询语句 +-- 查询接口 function DB:query(query) assert(self.INITIALIZATION, "DB needs to be initialized first.") - return run_query(self, assert(type(query) == 'string' and query ~= '' and query , "Invalid MySQL syntax.")) + return run_query(self, assert(type(query) == 'string' and query ~= '' and query , "Invalid MySQL query syntax.")) +end + +-- 预处理接口 +function DB:execute(query, ...) + assert(self.INITIALIZATION, "DB needs to be initialized first.") + return run_execute(self, assert(type(query) == 'string' and query ~= '' and query , "Invalid MySQL prepare syntax."), ...) end -- 字符串安全转义 diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua index ae5255fa..8afb99e2 100644 --- a/lualib/protocol/mysql.lua +++ b/lualib/protocol/mysql.lua @@ -15,12 +15,16 @@ local sub = string.sub local strgsub = string.gsub local strformat = string.format local strbyte = string.byte +local strchar = string.char local strrep = string.rep local strunpack = string.unpack local strpack = string.pack local assert = assert local tonumber = tonumber local toint = math.tointeger +local mtype = math.type + +local tpack = table.pack local insert = table.insert local concat = table.concat @@ -50,6 +54,13 @@ local CMD_QUIT = 0x01 -- 查询 local CMD_QUERY = 0x03 +-- 编译 +local COM_STMT_PREPARE = 0x16 +-- 执行 +local COM_STMT_EXECUTE = 0x17 + +local CURSOR_TYPE_NO_CURSOR = 0x00 + -- 类型转换函数 local converts = new_tab(32, 0) @@ -299,7 +310,7 @@ local function get_field (self) -- print("flags :", flags) decimals, pos = strunpack("= 0 and first <= 250 then + return first, pos + 1 + end + if first == 251 then + return nil, pos + 1 + end + if first == 252 then + pos = pos + 1 + return strunpack(" 2 then + null_fields[field_index - 2] = byte & (1 << j) ~= 0 and true or false + end + field_index = field_index + 1 + end + end + + local parser + local row = new_tab(0, ncols) + for i = 1, ncols do + local col = cols[i] + -- var_dump(col) + if not null_fields[i] then + parser = binary_parser[col.field_type] + if not parser then + error("error! field key[" .. col.field_name .."] unsupported type " .. col.field_type) + end + row[col.field_name], pos = parser(data, pos, col.is_signed) + else + row[col.field_name] = null + end end return row end @@ -322,7 +477,7 @@ local function get_eof_packet (packet) end local function get_eof (self, len) - local packet, err = read_body(self, len) + local packet = read_body(self, len) if not packet then return nil, "mysql server closed when client sended query packet." end @@ -352,7 +507,7 @@ local function get_ok(packet, length) end local function read_response (self, results) - local packet, err = read_packet(self) + local packet = read_packet(self) if not packet then self.state = nil return nil, "1. mysql server closed when client sended query packet." @@ -384,11 +539,11 @@ local function read_response (self, results) self.state = nil return nil, err end - fields[#fields+1] = field + fields[index] = field end local again = false - local len, err = read_head(self) + local len = read_head(self) if not len then self.state = nil return nil, "2. mysql server closed when client sended query packet." @@ -405,7 +560,7 @@ local function read_response (self, results) local rows = new_tab(32, 0) while 1 do - local packet, err = read_packet(self) + packet, err = read_packet(self) if not packet then self.state = nil return nil, err @@ -457,6 +612,109 @@ local function send_packet (self, request) return sock_write(self, strpack(" 0 then + for _ = 1, num do + local len = read_head(self) + if not len then + return false + end + sock_read(self, len) + end + local len = read_head(self) + if not sock_read(self, len) then + return false + end + end + return true +end + +local function read_prepare_response(self) + local packet = read_packet(self) + if not packet then + self.state = nil + return nil, "1. mysql server closed when client sended query packet." + end + -- 预编译status只有OK或ERROR + local status = strbyte(packet, 1) + if status == RESP_ERROR then + return nil, get_mysql_error_packet(packet:sub(2)) + end + if status ~= RESP_OK then + return nil, "2. Invalid mysql prepare protocol." + end + local info = {} + info.sid, info.fields, info.params, info.warnings = strunpack(" 0 then + local field_index = 1 + local null_map = "" + local null_count = (argn + 7) // 8 + -- null-bitmap 必须检查正确. + for _ = 1, null_count do + local nbyte = 0 + for offset = 0, 7 do + if field_index <= argn then + local v = args[field_index] + nbyte = nbyte | (((v == nil or type(v) == 'userdata') and 1 or 0) << offset) + end + field_index = field_index + 1 + end + null_map = null_map .. strchar(nbyte) + end + local tb_idx, vb_idx = 1, 1 + local types_buf, values_buf = new_tab(16, 0), new_tab(16, 0) + for i = 1, argn, 1 do + local v = args[i] + local f = fmap[type(v)] + if not f then + error("invalid parameter type :" .. type(v)) + end + types_buf[tb_idx], values_buf[vb_idx] = f(v) + tb_idx, vb_idx = tb_idx + 1, vb_idx + 1 + end + sql = concat{sql, null_map, '\x01', concat(types_buf), concat(values_buf)} + end + send_packet(self, sql) + return read_execute_reponse(self) +end + +local function mysql_prepare(self, stmt) + send_packet(self, strpack(" Date: Fri, 31 Dec 2021 22:02:19 +0800 Subject: [PATCH 873/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0hmac=5Fpbkdf2?= =?UTF-8?q?=E7=AE=97=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/hmac_ex.c | 42 ++++++++++++++++++++++++++++++++++++ luaclib/src/lcrypt/lcrypt.c | 11 ++++++++++ luaclib/src/lcrypt/lcrypt.h | 1 + lualib/crypt/hmac.lua | 19 ++++++++++++++++ 4 files changed, 73 insertions(+) diff --git a/luaclib/src/lcrypt/hmac_ex.c b/luaclib/src/lcrypt/hmac_ex.c index ff163fc8..073a0628 100644 --- a/luaclib/src/lcrypt/hmac_ex.c +++ b/luaclib/src/lcrypt/hmac_ex.c @@ -213,4 +213,46 @@ int lhashkey(lua_State *L) { Hash(key,(int)sz,realkey); lua_pushlstring(L, (const char *)realkey, 8); return 1; +} + +int lhmac_pbkdf2(lua_State *L) { + + EVP_MD *dig_md = (EVP_MD *)lua_touserdata(L, 1); + if (!dig_md) + return luaL_error(L, "Invalid pbkdf2 hash type."); + + int bsize = 0; + if (dig_md == EVP_md5()) + bsize = MD5_DIGEST_LENGTH; + else if (dig_md == EVP_sha1()) + bsize = SHA_DIGEST_LENGTH; + else if (dig_md == EVP_sha224()) + bsize = SHA224_DIGEST_LENGTH; + else if (dig_md == EVP_sha256()) + bsize = SHA256_DIGEST_LENGTH; + else if (dig_md == EVP_sha384()) + bsize = SHA384_DIGEST_LENGTH; + else if (dig_md == EVP_sha512()) + bsize = SHA512_DIGEST_LENGTH; + else + return luaL_error(L, "unsupported pbkdf2 hash type."); + + size_t psize; + const char* password = (const char*)luaL_checklstring(L, 2, &psize); + if (psize < 1) + return luaL_error(L, "Invalid pbkdf2 password."); + + size_t sasize; + const unsigned char* salt = (const unsigned char*)luaL_tolstring(L, 3, &sasize); + if (sasize < 1) + salt = NULL; + + lua_Integer iter = luaL_checkinteger(L, 4); + unsigned char buffer[bsize]; + + if (0 == PKCS5_PBKDF2_HMAC(password, psize, salt, sasize, iter > 0 ? iter : 1000, dig_md, bsize, buffer)) + return 0; + + lua_pushlstring(L, (const char*)buffer, bsize); + return 1; } \ No newline at end of file diff --git a/luaclib/src/lcrypt/lcrypt.c b/luaclib/src/lcrypt/lcrypt.c index 4e46c6b0..3d708864 100644 --- a/luaclib/src/lcrypt/lcrypt.c +++ b/luaclib/src/lcrypt/lcrypt.c @@ -45,6 +45,7 @@ static int lrandomkey(lua_State *L) { #define lua_set_key_INT(L, key, value) ({ lua_pushstring((L), (key)); lua_pushinteger((L), (value)); lua_rawset((L), -3); }) #define lua_set_key_STR(L, key, value) ({ lua_pushstring((L), (key)); lua_pushstring((L), (value)); lua_rawset((L), -3); }) +#define lua_set_key_PTR(L, key, value) ({ lua_pushstring((L), (key)); lua_pushlightuserdata((L), (void*)(value)); lua_rawset((L), -3); }) static int crypt_set_key_value(lua_State *L) { /* OPENSSL VERSION NUMBER */ @@ -62,6 +63,15 @@ static int crypt_set_key_value(lua_State *L) { lua_set_key_INT(L, "nid_sha256", NID_sha256); lua_set_key_INT(L, "nid_sha512", NID_sha512); + /* 增加EVP的摘要方法模型 */ + lua_set_key_PTR(L, "EVP_md5", EVP_md5()); + // lua_set_key_PTR(L, "EVP_blake256", EVP_blake2s256()); + // lua_set_key_PTR(L, "EVP_blake512", EVP_blake2b512()); + lua_set_key_PTR(L, "EVP_sha128", EVP_sha1()); + lua_set_key_PTR(L, "EVP_sha224", EVP_sha224()); + lua_set_key_PTR(L, "EVP_sha256", EVP_sha256()); + lua_set_key_PTR(L, "EVP_sha384", EVP_sha384()); + lua_set_key_PTR(L, "EVP_sha512", EVP_sha512()); return 1; } @@ -107,6 +117,7 @@ LUAMOD_API int luaopen_lcrypt(lua_State *L) { { "hmac_sha512", lhmac_sha512 }, { "hmac_hash", lhmac_hash }, { "hmac_ripemd160", lhmac_ripemd160}, + { "hmac_pbkdf2", lhmac_pbkdf2 }, { "xor_str", lxor_str }, // 公钥加密 -> 私钥解密 { "rsa_public_key_encode", lrsa_public_key_encode }, diff --git a/luaclib/src/lcrypt/lcrypt.h b/luaclib/src/lcrypt/lcrypt.h index a480fe75..cf7f00a5 100644 --- a/luaclib/src/lcrypt/lcrypt.h +++ b/luaclib/src/lcrypt/lcrypt.h @@ -62,6 +62,7 @@ int lhmac_sha256(lua_State *L); int lhmac_sha384(lua_State *L); int lhmac_sha512(lua_State *L); int lhmac_ripemd160(lua_State *L); +int lhmac_pbkdf2(lua_State *L); int lrc4(lua_State *L); diff --git a/lualib/crypt/hmac.lua b/lualib/crypt/hmac.lua index 78978c93..bf10435a 100644 --- a/lualib/crypt/hmac.lua +++ b/lualib/crypt/hmac.lua @@ -12,11 +12,30 @@ local hmac_sha256 = CRYPT.hmac_sha256 local hmac_sha384 = CRYPT.hmac_sha384 local hmac_sha512 = CRYPT.hmac_sha512 local hmac_ripemd160 = CRYPT.hmac_ripemd160 +local hmac_pbkdf2 = CRYPT.hmac_pbkdf2 local hexencode = CRYPT.hexencode + +local md_map = { + md5 = CRYPT.EVP_md5, + sha128 = CRYPT.EVP_sha128, + sha224 = CRYPT.EVP_sha224, + sha256 = CRYPT.EVP_sha256, + sha384 = CRYPT.EVP_sha384, + sha512 = CRYPT.EVP_sha512, +} + local HMAC = {} +function HMAC.hmac_pbkdf2(mdname, password, salt, iter, hex) + local hash = hmac_pbkdf2(md_map[mdname], password, salt, iter) + if hash and hex then + return hexencode(hash) + end + return hash +end + function HMAC.hmac64(key, text, hex) local hash = hmac64(key, text) if hash and hex then From e7ac2c767e2070482acf6ade778a6194979cd2e0 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 1 Jan 2022 14:18:28 +0800 Subject: [PATCH 874/956] =?UTF-8?q?=E4=BC=98=E5=8C=96httpc=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpc/init.lua | 395 ++++++++++++++++-------------------------- 1 file changed, 146 insertions(+), 249 deletions(-) diff --git a/lualib/httpc/init.lua b/lualib/httpc/init.lua index bb458ad6..ab77f9af 100644 --- a/lualib/httpc/init.lua +++ b/lualib/httpc/init.lua @@ -32,54 +32,56 @@ local __TIMEOUT__ = 15 local methods = { get = true, post = true, put = true, delete = true, xml = true, json = true, file = true} +local function http_requese(sock, opt, req, parameter) + local ok, err + if parameter and parameter.cert_path and parameter.key_path then + sock:ssl_set_privatekey(parameter.key_path) + sock:ssl_set_certificate(parameter.cert_path) + if not sock:ssl_set_verify() then + sock:close() + return nil, "SSL privatekey and certfile verify failed." + end + end + ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) + if not ok then + sock:close() + return ok, err + end + ok, err = sock_send(sock, opt.protocol, req) + if not ok then + sock:close() + return ok, err + end + local code, msg, headers = httpc_response(sock, opt.protocol) + sock:close() + return code, msg, headers +end + local function raw( parameter ) - local opt, err = splite_protocol(parameter.domain) - if not opt then - return nil, err - end - - local method = assert(type(parameter.method) == 'string' and methods[lower(parameter.method)] and upper(parameter.method), "[HTTPC ERROR]: invalide http method.") - parameter.method = method - - -- GET方法禁止传递body - if parameter.method == "GET" then - parameter.body = nil - end - - -- POST/PUT方法禁止传递args - if parameter.method == "POST" or parameter.method == "PUT" or parameter.method == "DELETE" then - parameter.args = nil - end - - opt.method = method - opt.body = parameter.body - opt.args = parameter.args - opt.headers = parameter.headers - - local REQ = build_raw_req(opt) - - local sock = sock_new():timeout(parameter.timeout or __TIMEOUT__) - if parameter.cert_path and parameter.key_path then - sock:ssl_set_privatekey(parameter.key_path) - sock:ssl_set_certificate(parameter.cert_path) - if not sock:ssl_set_verify() then - sock:close() - return nil, "SSL privatekey and certfile verify failed." - end - end - local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) - if not ok then - sock:close() - return ok, err - end - local ok, err = sock_send(sock, opt.protocol, REQ) - if not ok then - sock:close() - return ok, err - end - local code, msg, headers = httpc_response(sock, opt.protocol) - sock:close() - return code, msg, headers + local opt, err = splite_protocol(parameter.domain) + if not opt then + return nil, err + end + + local method = assert(type(parameter.method) == 'string' and methods[lower(parameter.method)] and upper(parameter.method), "[HTTPC ERROR]: invalide http method.") + parameter.method = method + + -- GET方法禁止传递body + if parameter.method == "GET" then + parameter.body = nil + end + + -- POST/PUT方法禁止传递args + if parameter.method == "POST" or parameter.method == "PUT" or parameter.method == "DELETE" then + parameter.args = nil + end + + opt.method = method + opt.body = parameter.body + opt.args = parameter.args + opt.headers = parameter.headers + + return http_requese(sock_new():timeout(parameter.timeout or __TIMEOUT__), opt, build_raw_req(opt), parameter) end ---comment HTTP[S] `GET`请求方法 @@ -91,31 +93,16 @@ end ---@return string @服务器的响应内容. ---@return table @服务器的响应头部. local function get(domain, headers, args, timeout) - local opt, err = splite_protocol(domain) - if not opt then - return nil, err - end - - opt.args = args - opt.headers = headers - opt.server = ua.get_user_agent() - - local REQ = build_get_req(opt) - - local sock = sock_new():timeout(timeout or __TIMEOUT__) - local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) - if not ok then - sock:close() - return ok, err - end - local ok, err = sock_send(sock, opt.protocol, REQ) - if not ok then - sock:close() - return ok, err - end - local code, msg, headers = httpc_response(sock, opt.protocol) - sock:close() - return code, msg, headers + local opt, err = splite_protocol(domain) + if not opt then + return nil, err + end + + opt.args = args + opt.headers = headers + opt.server = ua.get_user_agent() + + return http_requese(sock_new():timeout(timeout or __TIMEOUT__), opt, build_get_req(opt)) end ---comment HTTP[S] `POST`请求方法 @@ -127,31 +114,16 @@ end ---@return string @服务器的响应内容. ---@return table @服务器的响应头部. local function post(domain, headers, body, timeout) - local opt, err = splite_protocol(domain) - if not opt then - return nil, err - end - - opt.body = body - opt.headers = headers - opt.server = ua.get_user_agent() - - local REQ = build_post_req(opt) - - local sock = sock_new():timeout(timeout or __TIMEOUT__) - local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) - if not ok then - sock:close() - return ok, err - end - local ok, err = sock_send(sock, opt.protocol, REQ) - if not ok then - sock:close() - return ok, err - end - local code, msg, headers = httpc_response(sock, opt.protocol) - sock:close() - return code, msg, headers + local opt, err = splite_protocol(domain) + if not opt then + return nil, err + end + + opt.body = body + opt.headers = headers + opt.server = ua.get_user_agent() + + return http_requese(sock_new():timeout(timeout or __TIMEOUT__), opt, build_post_req(opt)) end ---comment HTTP[S] `DELETE`请求方法 @@ -164,31 +136,16 @@ end ---@return table @服务器的响应头部. local function delete(domain, headers, body, timeout) - local opt, err = splite_protocol(domain) - if not opt then - return nil, err - end - - opt.body = body - opt.headers = headers - opt.server = ua.get_user_agent() - - local REQ = build_delete_req(opt) - - local sock = sock_new():timeout(timeout or __TIMEOUT__) - local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) - if not ok then - sock:close() - return ok, err - end - local ok, err = sock_send(sock, opt.protocol, REQ) - if not ok then - sock:close() - return ok, err - end - local code, msg, headers = httpc_response(sock, opt.protocol) - sock:close() - return code, msg, headers + local opt, err = splite_protocol(domain) + if not opt then + return nil, err + end + + opt.body = body + opt.headers = headers + opt.server = ua.get_user_agent() + + return http_requese(sock_new():timeout(timeout or __TIMEOUT__), opt, build_delete_req(opt)) end ---comment HTTP[S] `PUT`请求方法 @@ -201,31 +158,16 @@ end ---@return table @服务器的响应头部. local function put(domain, headers, body, timeout) - local opt, err = splite_protocol(domain) - if not opt then - return nil, err - end - - opt.body = body - opt.headers = headers - opt.server = ua.get_user_agent() - - local REQ = build_put_req(opt) - - local sock = sock_new():timeout(timeout or __TIMEOUT__) - local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) - if not ok then - sock:close() - return ok, err - end - local ok, err = sock_send(sock, opt.protocol, REQ) - if not ok then - sock:close() - return ok, err - end - local code, msg, headers = httpc_response(sock, opt.protocol) - sock:close() - return code, msg, headers + local opt, err = splite_protocol(domain) + if not opt then + return nil, err + end + + opt.body = body + opt.headers = headers + opt.server = ua.get_user_agent() + + return http_requese(sock_new():timeout(timeout or __TIMEOUT__), opt, build_put_req(opt)) end ---comment HTTP[S] `POST`请求方法(使用`JSON`作为请求体) @@ -238,31 +180,16 @@ end ---@return table @服务器的响应头部. local function json(domain, headers, json, timeout) - local opt, err = splite_protocol(domain) - if not opt then - return nil, err - end - - opt.json = json - opt.headers = headers - opt.server = ua.get_user_agent() - - local REQ = build_json_req(opt) - - local sock = sock_new():timeout(timeout or __TIMEOUT__) - local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) - if not ok then - sock:close() - return ok, err - end - local ok, err = sock_send(sock, opt.protocol, REQ) - if not ok then - sock:close() - return ok, err - end - local code, msg, headers = httpc_response(sock, opt.protocol) - sock:close() - return code, msg, headers + local opt, err = splite_protocol(domain) + if not opt then + return nil, err + end + + opt.json = json + opt.headers = headers + opt.server = ua.get_user_agent() + + return http_requese(sock_new():timeout(timeout or __TIMEOUT__), opt, build_json_req(opt)) end ---comment HTTP[S] `POST`请求方法(使用`XML`作为请求体) @@ -275,31 +202,16 @@ end ---@return table @服务器的响应头部. local function xml(domain, headers, xml, timeout) - local opt, err = splite_protocol(domain) - if not opt then - return nil, err - end - - opt.xml = xml - opt.headers = headers - opt.server = ua.get_user_agent() - - local REQ = build_xml_req(opt) - - local sock = sock_new():timeout(timeout or __TIMEOUT__) - local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) - if not ok then - sock:close() - return ok, err - end - local ok, err = sock_send(sock, opt.protocol, REQ) - if not ok then - sock:close() - return ok, err - end - local code, msg, headers = httpc_response(sock, opt.protocol) - sock:close() - return code, msg, headers + local opt, err = splite_protocol(domain) + if not opt then + return nil, err + end + + opt.xml = xml + opt.headers = headers + opt.server = ua.get_user_agent() + + return http_requese(sock_new():timeout(timeout or __TIMEOUT__), opt, build_xml_req(opt)) end ---comment HTTP[S] `POST`请求方法(使用`File`作为请求体) @@ -312,67 +224,52 @@ end ---@return table @服务器的响应头部. local function file(domain, headers, files, timeout) - local opt, err = splite_protocol(domain) - if not opt then - return nil, err - end - - opt.files = files - opt.headers = headers - opt.server = ua.get_user_agent() - - local REQ = build_file_req(opt) - - local sock = sock_new():timeout(timeout or __TIMEOUT__) - local ok, err = sock_connect(sock, opt.protocol, opt.domain, opt.port) - if not ok then - sock:close() - return ok, err - end - local ok, err = sock_send(sock, opt.protocol, REQ) - if not ok then - sock:close() - return ok, err - end - local code, msg, headers = httpc_response(sock, opt.protocol) - sock:close() - return code, msg, headers + local opt, err = splite_protocol(domain) + if not opt then + return nil, err + end + + opt.files = files + opt.headers = headers + opt.server = ua.get_user_agent() + + return http_requese(sock_new():timeout(timeout or __TIMEOUT__), opt, build_file_req(opt)) end local map = { get = get, post = post, delete = delete, json = json, xml = xml, file = file, put = put } local function multi_request (list) - if type(list) ~= 'table' or #list < 1 then + if type(list) ~= 'table' or #list < 1 then return false, "[HTTPC ERROR]: Invalid request parameter." end - local s = now() - local response = {} - local array = {} - for index, req in ipairs(list) do - local fn = map[req.method and lower(req.method)] - if not fn then - response[index] = {false, '[HTTPC ERROR]: Unsupported request method.', {}, now() - s} - else - tinsert(array, function () - local code, msg, headers = fn(req.domain, req.headers, req.args or req.body or req.json or req.xml or req.files, req.timeout) - response[index] = {code, msg, headers, now() - s} - end) - end - end - cf_join(tunpack(array)) - return true, response + local s = now() + local response = {} + local array = {} + for index, req in ipairs(list) do + local fn = map[req.method and lower(req.method)] + if not fn then + response[index] = {false, '[HTTPC ERROR]: Unsupported request method.', {}, now() - s} + else + tinsert(array, function () + local code, msg, headers = fn(req.domain, req.headers, req.args or req.body or req.json or req.xml or req.files, req.timeout) + response[index] = {code, msg, headers, now() - s} + end) + end + end + cf_join(tunpack(array)) + return true, response end return { - raw = raw, - get = get, - post = post, - delete = delete, - json = json, - xml = xml, - file = file, - put = put, - multi_request = multi_request, - basic_authorization = build_basic_authorization, + raw = raw, + get = get, + post = post, + delete = delete, + json = json, + xml = xml, + file = file, + put = put, + multi_request = multi_request, + basic_authorization = build_basic_authorization, } From fcffd791e52241bb26144ee5d7907aa9213758dd Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 6 Jan 2022 21:25:54 +0800 Subject: [PATCH 875/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0cf.run=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E7=9B=B4=E6=8E=A5=E6=89=A7=E8=A1=8C=E4=B8=80=E4=B8=AA?= =?UTF-8?q?lua=E6=96=87=E4=BB=B6.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/cf/init.lua | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/lualib/cf/init.lua b/lualib/cf/init.lua index 675bd63b..8d4a03d9 100644 --- a/lualib/cf/init.lua +++ b/lualib/cf/init.lua @@ -13,9 +13,26 @@ local type = type local error = error local ipairs = ipairs local tonumber = tonumber +local loadfile = loadfile + +local loadfilex = package.searchers[2] local cf = {} +---comment 指定的`filename`文件内容为入口创建一个协程. +---@param filename string @可被`loadfile`或`require`加载的代码文件. +---@param ... any @可选传递的可变参. +function cf.run(filename, ...) + local f, errinfo = loadfile(filename, 'bt') + if not f then + f = loadfilex(filename) + if type(f) ~= 'function' then + return assert(nil, '\n' .. errinfo .. ' and ' .. f ) + end + end + return cf.fork(f, ...) +end + ---comment 创建一个由cf管理的超时器(只会触发一次) ---@param timeout number @`timeout`大于0才会创建定时器. ---@param func function @时间到期后将会调用此函数. @@ -77,11 +94,11 @@ function cf.wakeup(co, ...) end local function cb(f, ctx) - local ok, errinfo = pcall(f) - ctx.waits = ctx.waits - 1 - if ctx.waits == 0 then - co_wakeup(ctx.co) - end + local ok, errinfo = pcall(f) + ctx.waits = ctx.waits - 1 + if ctx.waits == 0 then + co_wakeup(ctx.co) + end if not ok then return error(errinfo) end @@ -91,17 +108,17 @@ end ---@param fn function @至少一个或多个任务函数 ---@return nil @始终不存在返回值. function cf.join(fn, ...) - local qlist = {fn, ...} + local qlist = {fn, ...} local ctx = { co = co_self(), waits = nil } local waits = 0 - for _, f in ipairs(qlist) do + for _, f in ipairs(qlist) do if type(f) == 'function' then co_fork(cb, f, ctx) waits = waits + 1 end - end + end ctx.waits = waits - return co_wait() + return co_wait() end return cf From e36d5d66c4c08dc4993f9aeb402cdf7bab630d89 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 8 Jan 2022 13:11:27 +0800 Subject: [PATCH 876/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=88=B7=E6=96=B0?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/Co.lua | 13 +++++++++++++ lualib/internal/TCP.lua | 16 +++++++++++++--- lualib/internal/Timer.lua | 21 ++++++++++++++------- lualib/internal/UDP.lua | 10 ++++++++++ 4 files changed, 50 insertions(+), 10 deletions(-) diff --git a/lualib/internal/Co.lua b/lualib/internal/Co.lua index 6a4ea4c2..401799a6 100644 --- a/lualib/internal/Co.lua +++ b/lualib/internal/Co.lua @@ -35,6 +35,9 @@ local co_num = 0 local co_map = new_tab(0, 128) co_map[co_self()] = {co_self(), nil, false} +local tab = debug.getregistry() +tab['__G_CO__'] = co_map + local co_wlist = new_tab(128, 0) local function co_wrapper() @@ -164,4 +167,14 @@ function Co.count() return co_num end +-- 刷新缓存 +function Co.flush() + local map = {} + for key, value in pairs(co_map) do + map[key] = value + end + co_map = map + tab['__G_CO__'] = map +end + return Co \ No newline at end of file diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index c412aacd..59a7256d 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -81,10 +81,10 @@ local function tcp_push(tcp) return insert(POOL, tcp) end -local tab = new_tab(3, 0) +local tlist = new_tab(3, 0) local function buffer_concat(buf_1, buf_2) - tab[1], tab[2] = buf_1, buf_2 - return concat(tab) + tlist[1], tlist[2] = buf_1, buf_2 + return concat(tlist) end local TCP = class("TCP") @@ -848,4 +848,14 @@ function TCP:close() G_Reference[self] = nil end +---comment 刷新 +function TCP.flush() + local G_REF = {} + for key, value in pairs(G_Reference) do + G_REF[key] = value + end + G_Reference = G_REF + tab['__G_TCP__'] = G_REF +end + return TCP \ No newline at end of file diff --git a/lualib/internal/Timer.lua b/lualib/internal/Timer.lua index 61621fac..b6019f40 100644 --- a/lualib/internal/Timer.lua +++ b/lualib/internal/Timer.lua @@ -17,16 +17,13 @@ local co_self = co.self local co_start = coroutine.resume local co_wait_ex = coroutine.yield -local tab = debug.getregistry() -if tab['__G_TIMER__'] then - return tab['__G_TIMER__'] -end - local Timer = {} -tab['__G_TIMER__'] = Timer local TMap = {} +local tab = debug.getregistry() +tab['__G_TIMER__'] = TMap + local function get_tid(offset) return (time() + offset * 1e3) * 0.1 // 1 end @@ -87,7 +84,7 @@ end) -- 初始化 co_start(Timer.TCo) -- 选更小的时间来定期检查. -ti_start(TTimer, 0.005, Timer.TCo) +ti_start(TTimer, 0.01, Timer.TCo) ---@class Timer @@ -141,4 +138,14 @@ function Timer.sleep(nsleep) co_wait() end +---comment 刷新 +function Timer.flush() + local Map = {} + for key, value in pairs(TMap) do + Map[key] = value + end + TMap = Map + tab['__G_TIMER__'] = Map +end + return Timer \ No newline at end of file diff --git a/lualib/internal/UDP.lua b/lualib/internal/UDP.lua index d440e522..1d30969d 100644 --- a/lualib/internal/UDP.lua +++ b/lualib/internal/UDP.lua @@ -92,4 +92,14 @@ function UDP:close() G_Reference[self] = nil end +---comment 刷新 +function UDP.flush() + local G_REF = {} + for key, value in pairs(G_Reference) do + G_REF[key] = value + end + G_Reference = G_REF + tab['__G_UDP__'] = G_REF +end + return UDP From 42b2de33c2245dd414363581a3933b401b0c9216 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 11 Jan 2022 23:30:16 +0800 Subject: [PATCH 877/956] =?UTF-8?q?=E5=88=B7=E6=96=B0=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=B8=8E=E5=8D=8F=E7=A8=8B=E9=98=9F=E5=88=97?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/Co.lua | 4 ++-- lualib/internal/TCP.lua | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lualib/internal/Co.lua b/lualib/internal/Co.lua index 401799a6..5165d4f2 100644 --- a/lualib/internal/Co.lua +++ b/lualib/internal/Co.lua @@ -48,7 +48,7 @@ local function co_wrapper() local start, total = 1, #co_wlist -- 使用两级`FIFO`队列交替管理协程的运行与切换, 并且每次预分配的`FIFO`队列的大小与上次执行的协程的数量相关. local co_rlist = co_wlist - co_wlist = new_tab(128, 0) + co_wlist = new_tab(32, 0) while true do for index = start, total do local obj = co_rlist[index] @@ -78,7 +78,7 @@ local function co_wrapper() total = #co_wlist end co_rlist = co_wlist - co_wlist = new_tab(128, 0) + co_wlist = new_tab(32, 0) end end) end diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index 59a7256d..cf0e9faa 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -854,6 +854,7 @@ function TCP.flush() for key, value in pairs(G_Reference) do G_REF[key] = value end + POOL = {} G_Reference = G_REF tab['__G_TCP__'] = G_REF end From 0e8d2a7cef02891765bd44c1883ee9236f4f92a4 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 12 Jan 2022 00:43:16 +0800 Subject: [PATCH 878/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/Co.lua | 9 +++++++-- lualib/internal/TCP.lua | 6 +++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lualib/internal/Co.lua b/lualib/internal/Co.lua index 5165d4f2..a7c5eb10 100644 --- a/lualib/internal/Co.lua +++ b/lualib/internal/Co.lua @@ -53,7 +53,12 @@ local function co_wrapper() for index = start, total do local obj = co_rlist[index] local co, args = obj[CO_INDEX], obj[ARGS_INDEX] - local ok, errinfo; if args then ok, errinfo = co_start(co, tunpack(args)); else ok, errinfo = co_start(co); end + local ok, errinfo + if args then + ok, errinfo = co_start(co, tunpack(args)) -- 带参数的协程 + else + ok, errinfo = co_start(co) -- `fork`的协程不需要参数 + end -- 如果协程`执行出错`或`执行完毕`, 则去掉引用销毁 if not ok or co_status(co) ~= 'suspended' then -- 如果发生异常,则应该把异常打印出来. @@ -78,7 +83,7 @@ local function co_wrapper() total = #co_wlist end co_rlist = co_wlist - co_wlist = new_tab(32, 0) + co_wlist = new_tab(total >= 128 and 128 or total, 0) end end) end diff --git a/lualib/internal/TCP.lua b/lualib/internal/TCP.lua index cf0e9faa..7e74197c 100644 --- a/lualib/internal/TCP.lua +++ b/lualib/internal/TCP.lua @@ -77,8 +77,8 @@ local function tcp_pop() return remove(POOL) or tcp_new() end -local function tcp_push(tcp) - return insert(POOL, tcp) +local function tcp_push(tio) + return insert(POOL, tio) end local tlist = new_tab(3, 0) @@ -747,7 +747,7 @@ function TCP:ssl_handshake(domain) end function TCP:count() - return #POOL + return #POOL end function TCP:close() From 17d70f329c3c0d1bb4b5b410d6e57c322c236f58 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 13 Jan 2022 00:59:45 +0800 Subject: [PATCH 879/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dmysql=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE=E9=94=99=E8=AF=AF=E4=BF=A1=E6=81=AF=E5=A4=84=E7=90=86?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/mysql.lua | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua index 8afb99e2..cc29fe63 100644 --- a/lualib/protocol/mysql.lua +++ b/lualib/protocol/mysql.lua @@ -729,19 +729,23 @@ local function mysql_login (self) return nil, "MySQL Server driver Invalid Configure." end - local len, err = read_head(self) + local len, err, packet + len, err = read_head(self) if not len then return nil, err end - local packet, err = read_body(self, len) + packet, err = read_body(self, len) if not packet then return nil, err end local protocol, version, tid, salt, salt_len, auth_plugin, pos - protocol, version, tid, pos = strunpack(" Date: Fri, 14 Jan 2022 22:47:32 +0800 Subject: [PATCH 880/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0base64=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 完善测试用例 优化base64编码、解码效率与兼容性 优化base64代码 优化HEX编码解码效率 Update hex.c 优化crypt代码 --- luaclib/src/lcrypt/b64.c | 251 +++++++++++++++++++++++---------------- luaclib/src/lcrypt/hex.c | 169 ++++++++++++++++++++------ lualib/crypt/b64.lua | 28 +++-- script/test_crypt.lua | 113 ++++++++++++++++-- 4 files changed, 406 insertions(+), 155 deletions(-) diff --git a/luaclib/src/lcrypt/b64.c b/luaclib/src/lcrypt/b64.c index c46512f9..eda69369 100644 --- a/luaclib/src/lcrypt/b64.c +++ b/luaclib/src/lcrypt/b64.c @@ -1,119 +1,170 @@ #include "lcrypt.h" -static inline int b64index(uint8_t c) { - static const int decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51}; - int decoding_size = sizeof(decoding)/sizeof(decoding[0]); - if (c<43) { - return -1; +#define BASE64_CHUNK (65535) + +#define BASE64_ENC_LENGTH(len) (((len) + 2) / 3 * 4) + +#define BASE64_DEC_LENGTH(len) ((len + 3) / 4 * 3) + +#define BASE64_URLSAFE(safe, ch, a, b, c, d) \ + if (urlsafe) { \ + if (ch == a) \ + ch = b; \ + else if (ch == c) \ + ch = d; \ } - c -= 43; - if (c>=decoding_size) - return -1; - return decoding[c]; -} +static inline void encoder(uint8_t *buffer, uint32_t idx, uint8_t code, int32_t urlsafe) { + static const char b64code[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + uint8_t ch = b64code[code]; + BASE64_URLSAFE(urlsafe, ch, '+', '-', '/', '_'); + /* check encoder */ + buffer[idx] = ch; +} int lb64encode(lua_State *L) { - static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - size_t sz = 0; - const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 1, &sz); - int encode_sz = (sz + 2)/3*4; - char tmp[SMALL_CHUNK]; - char *buffer = tmp; - if (encode_sz > SMALL_CHUNK) { - buffer = lua_newuserdata(L, encode_sz); - } - int i,j; - j=0; - for (i=0;i<(int)sz-2;i+=3) { - uint32_t v = text[i] << 16 | text[i+1] << 8 | text[i+2]; - buffer[j] = encoding[v >> 18]; - buffer[j+1] = encoding[(v >> 12) & 0x3f]; - buffer[j+2] = encoding[(v >> 6) & 0x3f]; - buffer[j+3] = encoding[(v) & 0x3f]; - j+=4; + + size_t tsize = 0; + const uint8_t* text = (const uint8_t *)luaL_checklstring(L, 1, &tsize); + if (!text || tsize == 0) + return 0; + + int32_t urlsafe = 0; + if (lua_isboolean(L, 2) && lua_toboolean(L, 2)) + urlsafe = 1; + + int32_t nopadding = 0; + if (lua_isboolean(L, 3) && lua_toboolean(L, 3)) + nopadding = 1; + + int esize = BASE64_ENC_LENGTH(tsize); + + char *buffer; + if (tsize <= BASE64_CHUNK) + buffer = alloca(esize); + else + buffer = lua_newuserdata(L, esize); + + int64_t nsize = tsize; + size_t idx, set, index = 0; + + /* normal encoder */ + for (idx = 0; idx < nsize - 2; idx += 3) { + set = (text[idx] << 16) | (text[idx + 1] << 8) | (text[idx + 2]); + encoder((uint8_t*)buffer, index++, set >> 18 & 0x3f, urlsafe); + encoder((uint8_t*)buffer, index++, set >> 12 & 0x3f, urlsafe); + encoder((uint8_t*)buffer, index++, set >> 6 & 0x3f, urlsafe); + encoder((uint8_t*)buffer, index++, set & 0x3f, urlsafe); } - int padding = sz-i; - uint32_t v; - switch(padding) { - case 1 : - v = text[i]; - buffer[j] = encoding[v >> 2]; - buffer[j+1] = encoding[(v & 3) << 4]; - buffer[j+2] = '='; - buffer[j+3] = '='; - break; - case 2 : - v = text[i] << 8 | text[i+1]; - buffer[j] = encoding[v >> 10]; - buffer[j+1] = encoding[(v >> 4) & 0x3f]; - buffer[j+2] = encoding[(v & 0xf) << 2]; - buffer[j+3] = '='; - break; + + // int padding = tsize - idx; + /* checked padding. */ + switch (tsize - idx) { + case 1: /* only 1 char */ + set = text[idx]; + encoder((uint8_t*)buffer, index++, set >> 2, urlsafe); + encoder((uint8_t*)buffer, index++, (set << 4) & 0x3f, urlsafe); + if (!nopadding) { + buffer[index++] = '='; + buffer[index++] = '='; + } + break; + case 2: /* having 2 char */ + set = text[idx] << 8 | text[idx + 1]; + encoder((uint8_t*)buffer, index++, (set >> 10) & 0x3f, urlsafe); + encoder((uint8_t*)buffer, index++, (set >> 4) & 0x3f, urlsafe); + encoder((uint8_t*)buffer, index++, (set << 2) & 0x3f, urlsafe); + if (!nopadding) + buffer[index++] = '='; + break; } - lua_pushlstring(L, buffer, encode_sz); + lua_pushlstring(L, buffer, index); return 1; } +static inline uint8_t decoder(lua_State* L, uint8_t ch, int32_t urlsafe) { + static const int8_t b64code_idx[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }; + BASE64_URLSAFE(urlsafe, ch, '-', '+', '_', '/'); + int8_t code = b64code_idx[ch]; + if (code == -1) + return luaL_error(L, "Invalid base64 decode byte. %d", ch); + return (uint8_t)code; +} + int lb64decode(lua_State *L) { - size_t sz = 0; - const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 1, &sz); - int decode_sz = (sz+3)/4*3; - char tmp[SMALL_CHUNK]; - char *buffer = tmp; - if (decode_sz > SMALL_CHUNK) { - buffer = lua_newuserdata(L, decode_sz); - } - int i,j; - int output = 0; - for (i=0;i=sz) { - return luaL_error(L, "Invalid base64 text"); + size_t tsize = 0; + const uint8_t* text = (const uint8_t *)luaL_checklstring(L, 1, &tsize); + if (!text || tsize == 0) + return 0; + + int32_t urlsafe = 0; + if (lua_isboolean(L, 2) && lua_toboolean(L, 2)) + urlsafe = 1; + + int dsize = BASE64_DEC_LENGTH(tsize); + + char *buffer; + if (tsize <= BASE64_CHUNK) + buffer = alloca(dsize); + else + buffer = lua_newuserdata(L, dsize); + + size_t index = 0, idx = 0; + uint32_t offsets, i, pos; + for (idx = 0; idx < tsize;) + { + i = 0, pos = 0; + uint8_t set[] = {0, 0, 0, 0}; + while (idx + pos < tsize && i < 4) { + uint8_t ch = text[idx + (pos++)]; + if (ch == '=') { + if (idx + pos < tsize - 2) + return luaL_error(L, "Invalid base64 decode text."); + break; } - c[j] = b64index(text[i]); - if (c[j] == -1) { - ++i; + if (ch == '\n') continue; - } - if (c[j] == -2) { - ++padding; - } - ++i; - ++j; + set[i++] = decoder(L, ch, urlsafe); } - uint32_t v; - switch (padding) { - case 0: - v = (unsigned)c[0] << 18 | c[1] << 12 | c[2] << 6 | c[3]; - buffer[output] = v >> 16; - buffer[output+1] = (v >> 8) & 0xff; - buffer[output+2] = v & 0xff; - output += 3; - break; - case 1: - if (c[3] != -2 || (c[2] & 3)!=0) { - return luaL_error(L, "Invalid base64 text"); - } - v = (unsigned)c[0] << 10 | c[1] << 4 | c[2] >> 2 ; - buffer[output] = v >> 8; - buffer[output+1] = v & 0xff; - output += 2; - break; - case 2: - if (c[3] != -2 || c[2] != -2 || (c[1] & 0xf) !=0) { - return luaL_error(L, "Invalid base64 text"); - } - v = (unsigned)c[0] << 2 | c[1] >> 4; - buffer[output] = v; - ++ output; - break; - default: - return luaL_error(L, "Invalid base64 text"); + // printf("pos = %d, set = { %d, %d, %d, %d }\n", idx, set[0], set[1], set[2], set[3]); + offsets = (set[0] << 18) | (set[1] << 12) | (set[2] << 6) | set[3]; + switch (4 - i){ + case 0: + /* decode normal character. */ + buffer[index++] = (offsets >> 16); + buffer[index++] = (offsets >> 8) & 0xFF;; + buffer[index++] = offsets & 0xFF; + break; + case 1: + /* padding 1 character. */ + buffer[index++] = (offsets >> 16); + buffer[index++] = (offsets >> 8) & 0xFF;; + break; + case 2: + /* padding 2 character. */ + buffer[index++] = offsets >> 16; + break; } + idx += pos; } - lua_pushlstring(L, buffer, output); + // printf("ret = %d\n", index); + lua_pushlstring(L, buffer, index); return 1; } \ No newline at end of file diff --git a/luaclib/src/lcrypt/hex.c b/luaclib/src/lcrypt/hex.c index 0cdbe5f9..24fc3731 100644 --- a/luaclib/src/lcrypt/hex.c +++ b/luaclib/src/lcrypt/hex.c @@ -1,46 +1,141 @@ #include "lcrypt.h" -#define HEX(v, c) { char tmp = (char) c; if (tmp >= '0' && tmp <= '9') { v = tmp-'0'; } else { v = tmp - 'a' + 10; } } +#define HEX_CHUNK (65535) -int ltohex(lua_State *L) { - static char hex[] = "0123456789abcdef"; - size_t sz = 0; - const uint8_t * text = (const uint8_t *)luaL_checklstring(L, 1, &sz); - char tmp[SMALL_CHUNK]; - char *buffer = tmp; - if (sz > SMALL_CHUNK/2) { - buffer = lua_newuserdata(L, sz * 2); - } - int i; - for (i=0;i> 4]; - buffer[i*2+1] = hex[text[i] & 0xf]; +static const uint8_t encode[256][2] = { + {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, {'0', '6'}, {'0', '7'}, + {'0', '8'}, {'0', '9'}, {'0', 'a'}, {'0', 'b'}, {'0', 'c'}, {'0', 'd'}, {'0', 'e'}, {'0', 'f'}, + {'1', '0'}, {'1', '1'}, {'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'}, + {'1', '8'}, {'1', '9'}, {'1', 'a'}, {'1', 'b'}, {'1', 'c'}, {'1', 'd'}, {'1', 'e'}, {'1', 'f'}, + {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, {'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, + {'2', '8'}, {'2', '9'}, {'2', 'a'}, {'2', 'b'}, {'2', 'c'}, {'2', 'd'}, {'2', 'e'}, {'2', 'f'}, + {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'}, {'3', '6'}, {'3', '7'}, + {'3', '8'}, {'3', '9'}, {'3', 'a'}, {'3', 'b'}, {'3', 'c'}, {'3', 'd'}, {'3', 'e'}, {'3', 'f'}, + {'4', '0'}, {'4', '1'}, {'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'}, + {'4', '8'}, {'4', '9'}, {'4', 'a'}, {'4', 'b'}, {'4', 'c'}, {'4', 'd'}, {'4', 'e'}, {'4', 'f'}, + {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, {'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, + {'5', '8'}, {'5', '9'}, {'5', 'a'}, {'5', 'b'}, {'5', 'c'}, {'5', 'd'}, {'5', 'e'}, {'5', 'f'}, + {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'}, {'6', '6'}, {'6', '7'}, + {'6', '8'}, {'6', '9'}, {'6', 'a'}, {'6', 'b'}, {'6', 'c'}, {'6', 'd'}, {'6', 'e'}, {'6', 'f'}, + {'7', '0'}, {'7', '1'}, {'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'}, + {'7', '8'}, {'7', '9'}, {'7', 'a'}, {'7', 'b'}, {'7', 'c'}, {'7', 'd'}, {'7', 'e'}, {'7', 'f'}, + {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, {'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, + {'8', '8'}, {'8', '9'}, {'8', 'a'}, {'8', 'b'}, {'8', 'c'}, {'8', 'd'}, {'8', 'e'}, {'8', 'f'}, + {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'}, {'9', '6'}, {'9', '7'}, + {'9', '8'}, {'9', '9'}, {'9', 'a'}, {'9', 'b'}, {'9', 'c'}, {'9', 'd'}, {'9', 'e'}, {'9', 'f'}, + {'a', '0'}, {'a', '1'}, {'a', '2'}, {'a', '3'}, {'a', '4'}, {'a', '5'}, {'a', '6'}, {'a', '7'}, + {'a', '8'}, {'a', '9'}, {'a', 'a'}, {'a', 'b'}, {'a', 'c'}, {'a', 'd'}, {'a', 'e'}, {'a', 'f'}, + {'b', '0'}, {'b', '1'}, {'b', '2'}, {'b', '3'}, {'b', '4'}, {'b', '5'}, {'b', '6'}, {'b', '7'}, + {'b', '8'}, {'b', '9'}, {'b', 'a'}, {'b', 'b'}, {'b', 'c'}, {'b', 'd'}, {'b', 'e'}, {'b', 'f'}, + {'c', '0'}, {'c', '1'}, {'c', '2'}, {'c', '3'}, {'c', '4'}, {'c', '5'}, {'c', '6'}, {'c', '7'}, + {'c', '8'}, {'c', '9'}, {'c', 'a'}, {'c', 'b'}, {'c', 'c'}, {'c', 'd'}, {'c', 'e'}, {'c', 'f'}, + {'d', '0'}, {'d', '1'}, {'d', '2'}, {'d', '3'}, {'d', '4'}, {'d', '5'}, {'d', '6'}, {'d', '7'}, + {'d', '8'}, {'d', '9'}, {'d', 'a'}, {'d', 'b'}, {'d', 'c'}, {'d', 'd'}, {'d', 'e'}, {'d', 'f'}, + {'e', '0'}, {'e', '1'}, {'e', '2'}, {'e', '3'}, {'e', '4'}, {'e', '5'}, {'e', '6'}, {'e', '7'}, + {'e', '8'}, {'e', '9'}, {'e', 'a'}, {'e', 'b'}, {'e', 'c'}, {'e', 'd'}, {'e', 'e'}, {'e', 'f'}, + {'f', '0'}, {'f', '1'}, {'f', '2'}, {'f', '3'}, {'f', '4'}, {'f', '5'}, {'f', '6'}, {'f', '7'}, + {'f', '8'}, {'f', '9'}, {'f', 'a'}, {'f', 'b'}, {'f', 'c'}, {'f', 'd'}, {'f', 'e'}, {'f', 'f'}, +}; + +static const char deindex[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static const uint8_t decode[16][16] = { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }, + { 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47 }, + { 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 }, + { 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79 }, + { 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95 }, + { 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111 }, + { 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127 }, + { 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143 }, + { 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159 }, + { 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175 }, + { 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191 }, + { 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207 }, + { 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223 }, + { 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239 }, + { 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 }, +}; + +int lfromhex(lua_State *L) { + size_t tsize = 0; + const uint8_t* text = (const uint8_t *)luaL_checklstring(L, 1, &tsize); + if (!text || tsize < 2 || (tsize & 0x01) == 0x01) + return luaL_error(L, "Invalid hexdecode text size %d", (int)tsize); + + size_t dsize = tsize / 2; + char *buffer; + if (tsize <= HEX_CHUNK) + buffer = alloca(dsize); + else + buffer = lua_newuserdata(L, dsize); + + size_t i, idx = 0; + int8_t hi, lo; + for (i = 0; i < tsize; i += 2) { + hi = deindex[text[i]]; lo = deindex[text[i+1]]; + if (hi == -1 || lo == -1) + return luaL_error(L, "Invalid hexdecode char pos in %d", i); + buffer[idx++] = decode[hi][lo]; } - lua_pushlstring(L, buffer, sz * 2); + lua_pushlstring(L, buffer, dsize); return 1; } -int lfromhex(lua_State *L) { - size_t sz = 0; - const char * text = luaL_checklstring(L, 1, &sz); - if (sz & 1) { - return luaL_error(L, "Invalid hex text size %d", (int)sz); - } - char tmp[SMALL_CHUNK]; - char *buffer = tmp; - if (sz > SMALL_CHUNK*2) { - buffer = lua_newuserdata(L, sz / 2); - } - int i; - for (i=0;i 16 || low > 16) { - return luaL_error(L, "Invalid hex text", text); - } - buffer[i/2] = hi<<4 | low; +int ltohex(lua_State *L) { + size_t tsize = 0; + const uint8_t* text = (const uint8_t *)luaL_checklstring(L, 1, &tsize); + if (!text || tsize == 0) + return luaL_error(L, "Invalid hexencode text size %d", (int)tsize); + + size_t esize = tsize * 2; + char *buffer; + if (tsize <= HEX_CHUNK) + buffer = alloca(esize); + else + buffer = lua_newuserdata(L, esize); + + const uint8_t *code; + size_t i, idx = 0; + for (i = 0; i < tsize; i++) { + code = encode[text[i]]; + buffer[idx++] = code[0]; + buffer[idx++] = code[1]; } - lua_pushlstring(L, buffer, i/2); + lua_pushlstring(L, buffer, esize); return 1; -} \ No newline at end of file +} diff --git a/lualib/crypt/b64.lua b/lualib/crypt/b64.lua index 89fedada..a26f8f5d 100644 --- a/lualib/crypt/b64.lua +++ b/lualib/crypt/b64.lua @@ -4,20 +4,34 @@ local base64decode = CRYPT.base64decode local B64 = {} -function B64.base64encode(text) - return base64encode(text) +---comment 普通BASE64编码 +---@param text string @等待编码的内容 +---@param nopadding boolean @是否保留填充字符(默认保留) +---@return string @返回编码后的内容 +function B64.base64encode(text, nopadding) + return base64encode(text, false, nopadding) end +---comment 普通BASE64解码 +---@param text string @等待解码的内容 +---@return string @返回解码后的内容 function B64.base64decode(text) - return base64decode(text) + return base64decode(text, false) end -function B64.base64urlencode(data) - return base64encode(data):gsub('+', '-'):gsub('/', '_') +---comment URL安全的BASE64编码 +---@param text string @等待编码的内容 +---@param nopadding boolean @是否保留填充字符(默认保留) +---@return string @返回编码后的内容 +function B64.base64urlencode(text, nopadding) + return base64encode(text, true, nopadding) end -function B64.base64urldecode(data) - return base64decode(data:gsub('-', '+'):gsub('_', '/')) +---comment URL安全的BASE64解码 +---@param text string @等待解码的内容 +---@return string @返回解码后的内容 +function B64.base64urldecode(text) + return base64decode(text, true) end -- 初始化函数 diff --git a/script/test_crypt.lua b/script/test_crypt.lua index 5f60873b..0d45a107 100644 --- a/script/test_crypt.lua +++ b/script/test_crypt.lua @@ -151,11 +151,100 @@ local function test_b64() print("----------*** 开始测试 base64 ***----------") - local rawData = "123456789" - - Log:DEBUG("测试base64:" .. crypt.base64encode(rawData), "原始数据为:" .. rawData) - - assert(rawData == crypt.base64decode(crypt.base64encode(rawData))) + local base64encode = require"crypt".base64encode + local base64decode = require"crypt".base64decode + local base64urlencode = require"crypt".base64urlencode + local base64urldecode = require"crypt".base64urldecode + + local LOG = require "logging" + + -- 1. 测试base64与base64url的编码解码 + local text = "Base64编码及解码测试" + local nb, ub = base64encode(text), base64urlencode(text) + local txt1, txt2 = base64decode(nb), base64urldecode(ub) + LOG:DEBUG(nb, ub) + LOG:DEBUG(assert(text == txt2), assert(text == txt1), assert(txt1 == txt2)) + + local v = 'Some_data_to_be_converted' + + -- 2. 测试padding是否会影响解码 + local b1 = 'U29tZV9kYXRhX3RvX2JlX2NvbnZlcnRlZA==' + local b2 = 'U29tZV9kYXRhX3RvX2JlX2NvbnZlcnRlZA=' + local b3 = 'U29tZV9kYXRhX3RvX2JlX2NvbnZlcnRlZA' + LOG:DEBUG(1, v, assert(v == base64decode(b1))) + LOG:DEBUG(2, v, assert(v == base64decode(b2))) + LOG:DEBUG(3, v, assert(v == base64decode(b3))) + + -- 3. 递推鲁棒性测试编码 + local v1, v2 + local t1 = 'A' -- 'QQ==' + local t2 = 'AB' -- 'QUI=' + local t3 = 'ABC' -- 'QUJD' + local t4 = 'ABCD' -- 'QUJDRA==' + local t5 = 'ABCDE' -- 'QUJDREU=' + local t6 = 'ABCDEF' -- 'QUJDREVG' + local t7 = 'ABCDEFG' -- 'QUJDREVGRw==' + local t8 = 'ABCDEFGH' -- 'QUJDREVGR0g=' + local t9 = 'ABCDEFGHI' -- 'QUJDREVGR0hJ' + + v1, v2 = base64decode("QQ=="), base64decode("QQ") + LOG:DEBUG(1, t1, assert(v1 == v2), assert(t1 == v1), assert(t1 == v2)) + + v1, v2 = base64decode("QUI="), base64decode("QUI") + LOG:DEBUG(2, t2, assert(v1 == v2), assert(t2 == v1), assert(t2 == v2)) + + v1, v2 = base64decode("QUJD"), base64decode("QUJD") + LOG:DEBUG(3, t3, assert(v1 == v2), assert(t3 == v1), assert(t3 == v2)) + + v1, v2 = base64decode("QUJDRA=="), base64decode("QUJDRA") + LOG:DEBUG(4, t4, assert(v1 == v2), assert(t4 == v1), assert(t4 == v2)) + + v1, v2 = base64decode("QUJDREU="), base64decode("QUJDREU") + LOG:DEBUG(5, t5, assert(v1 == v2), assert(t5 == v1), assert(t5 == v2)) + + v1, v2 = base64decode("QUJDREVG"), base64decode("QUJDREVG") + LOG:DEBUG(6, t6, assert(v1 == v2), assert(t6 == v1), assert(t6 == v2)) + + v1, v2 = base64decode("QUJDREVGRw=="), base64decode("QUJDREVGRw") + LOG:DEBUG(7, t7, assert(v1 == v2), assert(t7 == v1), assert(t7 == v2)) + + v1, v2 = base64decode("QUJDREVGR0g="), base64decode("QUJDREVGR0g") + LOG:DEBUG(8, t8, assert(v1 == v2), assert(t8 == v1), assert(t8 == v2)) + + v1, v2 = base64decode("QUJDREVGR0hJ"), base64decode("QUJDREVGR0hJ") + LOG:DEBUG(9, t9, assert(v1 == v2), assert(t9 == v1), assert(t9 == v2)) + + -- 4. 测试篡改字符 + local bdata1 = 'NWE3MGQzMTBhYWYyODUxZTFlN2QwOWY2OWFmOGE5ZjMtMmUzOGIxZWNlZTVkNDUzNjkyYTg2NDAxYTVhZjk0MzUwMDAyLUx3QTF1OGpXWC8zelM2TUt0NG9pbW1qZzVEOD1=' + local ubdata1 = 'NWE3MGQzMTBhYWYyODUxZTFlN2QwOWY2OWFmOGE5ZjMtMmUzOGIxZWNlZTVkNDUzNjkyYTg2NDAxYTVhZjk0MzUwMDAyLUx3QTF1OGpXWC8zelM2TUt0NG9pbW1qZzVEOD1' + LOG:DEBUG(base64decode(bdata1), base64decode(ubdata1)) + + local bdata2 = 'NWE3MGQzMTBhYWYyODUxZTFlN2QwOWY2OWFmOGE5ZjMtMmUzOGIxZWNlZTVkNDUzNjkyYTg2NDAxYTVhZjk0MzUwMDAyLUx3QTF1OGpXWC8zelM2TUt0NG9pbW1qZzVEOD0=' + local ubdata2 = 'NWE3MGQzMTBhYWYyODUxZTFlN2QwOWY2OWFmOGE5ZjMtMmUzOGIxZWNlZTVkNDUzNjkyYTg2NDAxYTVhZjk0MzUwMDAyLUx3QTF1OGpXWC8zelM2TUt0NG9pbW1qZzVEOD0' + LOG:DEBUG(base64decode(bdata2), base64decode(ubdata2)) + + -- 5. paddding测试 1 + local c1, c2 + local test = "我刚刚想了一下,现在是不是应该为咱们的真实数据添加一点特殊字符.!" + -- 携带paddding测试 + c1, c2 = base64encode(test), base64urlencode(test) + LOG:DEBUG(c1, c2) + LOG:DEBUG(assert(base64decode(c1) == test), assert(base64urldecode(c2) == test)) + -- 去除paddding测试 + c1, c2 = base64encode(test, true), base64urlencode(test, true) + LOG:DEBUG(c1, c2) + LOG:DEBUG(assert(base64decode(c1) == test), assert(base64urldecode(c2) == test)) + + -- 6. paddding测试 2 + test = "A." + -- 携带paddding测试 + c1, c2 = base64encode(test), base64urlencode(test) + LOG:DEBUG(c1, c2) + LOG:DEBUG(assert(base64decode(c1) == test), assert(base64urldecode(c2) == test)) + -- 去除paddding测试 + c1, c2 = base64encode(test, true), base64urlencode(test, true) + LOG:DEBUG(c1, c2) + LOG:DEBUG(assert(base64decode(c1) == test), assert(base64urldecode(c2) == test)) print("----------*** base64 测试完毕 ***----------\n") @@ -185,7 +274,7 @@ local function test_aes() local function test_aes_192_bit() - key_192 = "abcdefghabcdefghabcdefgh" + local key_192 = "abcdefghabcdefghabcdefgh" -- ECB local ecb_encryptData = crypt.aes_192_ecb_encrypt(key_192, text, iv) local ecb_rawData = crypt.aes_192_ecb_decrypt(key_192, ecb_encryptData, iv) @@ -200,7 +289,7 @@ local function test_aes() end local function test_aes_256_bit() - key_256 = "abcdefghabcdefghabcdefghabcdefgh" + local key_256 = "abcdefghabcdefghabcdefghabcdefgh" -- ECB local ecb_encryptData = crypt.aes_256_ecb_encrypt(key_256, text, iv) local ecb_rawData = crypt.aes_256_ecb_decrypt(key_256, ecb_encryptData, iv) @@ -369,10 +458,12 @@ end local function test_uuid( ... ) - print("----------*** 开始测试 uuid 生成 ***----------") + print("----------*** 开始测试 uuid/guid 生成 ***----------") Log:DEBUG("生成的UUID为: " .. crypt.uuid()) + Log:DEBUG("生成的GUID为: " .. crypt.guid()) + print("----------*** uuid 测试完成 ***----------\n") end @@ -388,7 +479,7 @@ local function main() -- test_url, - -- test_b64, + test_b64, -- test_sha, @@ -402,9 +493,9 @@ local function main() -- test_other, - test_rsa, + -- test_rsa, - -- test_uuid, + test_uuid, } From 622943b628a5294e621fc26b0a8a3812489ae89d Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Fri, 21 Jan 2022 20:15:07 +0800 Subject: [PATCH 881/956] =?UTF-8?q?=E4=BC=98=E5=8C=96timer=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/Timer.lua | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lualib/internal/Timer.lua b/lualib/internal/Timer.lua index b6019f40..d45bf66f 100644 --- a/lualib/internal/Timer.lua +++ b/lualib/internal/Timer.lua @@ -86,7 +86,15 @@ co_start(Timer.TCo) -- 选更小的时间来定期检查. ti_start(TTimer, 0.01, Timer.TCo) ----@class Timer +---@class Timer @定时器对象 +local class = require "class" + +local TIMER = class("Timer") + +-- 初始化 +function TIMER:ctor() end +-- 停止定时器 +function TIMER:stop() if self then self[1] = false end end ---comment 初始化定时器对象 ---@param timeout number @超时时间 @@ -95,14 +103,7 @@ ti_start(TTimer, 0.01, Timer.TCo) ---@param func function @回调函数 ---@return Timer @定时器对象 local function Timer_Init(timeout, again, async, func) - local ctx = set_ctx(get_tid(timeout), { true, timeout, func, again, async }) - return { - stop = function (self) - if self and ctx and ctx[1] then - ctx[1] = false - end - end - } + return set_ctx(get_tid(timeout), setmetatable({true, timeout, func, again, async}, TIMER)) end ---comment 一次性定时器 From 21124c311b80a72d979cf6f6a8e69c6c7a2df982 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Mon, 7 Mar 2022 23:49:01 +0800 Subject: [PATCH 882/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0url=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E6=96=B9=E6=B3=95.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/url/init.lua | 108 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 102 insertions(+), 6 deletions(-) diff --git a/lualib/url/init.lua b/lualib/url/init.lua index 5b2ca645..a519bea2 100644 --- a/lualib/url/init.lua +++ b/lualib/url/init.lua @@ -1,9 +1,14 @@ --- lua版实现 --- local tonumber = tonumber --- local byte = string.byte --- local char = string.char --- local fmt = string.format --- local spliter = string.gsub +local type = type +local assert = assert +local setmetatable = setmetatable + +local strsub = string.sub +local strbyte = string.byte +local strfind = string.find +local strfmt = string.format +local strmatch = string.match + +local tconcat = table.concat -- C版实现 local encode = require("crypt").urlencode @@ -27,4 +32,95 @@ function url.decode(s) return decode(s) end +local meta = {} + +function meta.__tostring(t) + return strfmt( + "Url(sheme='%s', netloc='%s', path='%s', query='%s', fragment='%s')", + t.sheme or '', t.netloc or '', t.path or '', t.query or '', t.fragment or '' + ) +end + +local function parse_other(other, t, dec) + -- find '#' or '?' + local pos = strfind(other, "#") + if pos then + -- Begin with '#' + t.fragment = strsub(other, pos + 1) + if pos > 1 then + t.query = strsub(other, 2, pos - 1) + end + else + -- got query string. + pos = strfind(other, "?") + if pos then + t.query = strsub(other, pos + 1) + end + end + if dec and t.query and t.query ~= '' then + t.query = decode(t.query) + end +end + +local function parse_noloc(str, dec) + local t, other = {}, nil + t.path, other = strmatch(str, "([^%?#]*)([%?#]?.*)") + parse_other(other, t, dec) + return setmetatable(t, meta) +end + +local function parse_nosheme(str, dec) + local t, other = {}, nil + -- got url split string. + t.path, other = strmatch(str, "([^%?#]*)([%?#]?.*)") + if strbyte(t.path) == 47 then + return parse_noloc(str) + end + if strfind(t.path, '/') then + t.netloc, t.path, other = strmatch(str, "([^/]*)([^%?#]*)([%?#]?.*)") + end + parse_other(other, t, dec) + return setmetatable(t, meta) +end + +---comment split url to Url Table(Class). +---@param str string @Url buffer. +---@param dec boolean @Url decode. +---@return table +function url.split(str, dec) + assert(type(str) == 'string', 'Invalid Url type.') + local t, other = {}, nil + -- got url split string. + t.sheme, t.netloc, t.path, other = strmatch(str, '([^:]*)[:]?//([^/]*)([/]?[^%?#]*)([%?#]?.*)') + if not t.sheme then + return parse_nosheme(str, dec) + end + parse_other(other, t, dec) + return setmetatable(t, meta) +end + +---comment Use `Url` Table(Class) to Build url `String`. +---@param tab table @Split Class. +---@return string +function url.join(tab) + assert(type(tab) == 'table', 'Invalid Url Class.') + local urls = {} + if tab.sheme then + urls[1] = (tab.sheme ~= '' and (tab.sheme .. ':') or '' ) .. '//' + end + if tab.netloc then + urls[#urls+1] = tab.netloc + end + if tab.path then + urls[#urls+1] = tab.path + end + if tab.query then + urls[#urls+1] = '?' .. tab.query + end + if tab.fragment then + urls[#urls+1] = '#' .. tab.fragment + end + return tconcat(urls) +end + return url From 72aa1d7379ad324685dfb5dbf9f5a05b70c04a29 Mon Sep 17 00:00:00 2001 From: Nuctori <1018570975@qq.com> Date: Tue, 8 Mar 2022 00:28:43 +0800 Subject: [PATCH 883/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9http=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E8=A7=A3=E6=9E=90=E6=96=B9=E5=BC=8F=EF=BC=8C=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=89=80=E6=9C=89=E7=9A=84=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/httpd/Form.lua | 10 +-- lualib/protocol/http/init.lua | 121 ++++++++++++++++++---------------- 2 files changed, 64 insertions(+), 67 deletions(-) diff --git a/lualib/httpd/Form.lua b/lualib/httpd/Form.lua index d2eaacf5..1dd2742a 100644 --- a/lualib/httpd/Form.lua +++ b/lualib/httpd/Form.lua @@ -70,21 +70,13 @@ function form.multipart(body, BOUNDARY) insert(FILES, {filename = name, file = file}) end end - if #FILES > 0 then - -- return form.FILE, FILE - return 0, FILES - end local ARGS = {} for key, value in splite(body, 'name="([^"]*)"\r\n\r\n([^%-]-)\r\n[%-]-'..BOUNDARY) do if (key and key ~= '' ) and (value and value ~= '') then insert(ARGS, {key, value}) end end - if #ARGS > 0 then - -- return form.ARGS, ARGS - return 1, ARGS - end - return + return FILES, ARGS end diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index a394e02a..4e7f51cd 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -164,69 +164,69 @@ end local function PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEADER) local content = new_tab(0, 16) - if METHOD == "GET" then - local spl_pos = find(PATH, '%?') - if spl_pos and spl_pos < #PATH then - content['args'] = form_argsencode(PATH) + local body_len = toint(HEADER['Content-Length']) or toint(HEADER['Content-length']) or toint(HEADER['content-length']) + local BODY + if body_len and body_len > 0 then + if body_len >= (max_body_size or (1024 * 1024)) then + return nil, 413 end - elseif METHOD == "POST" or METHOD == "PUT" then - local body_len = toint(HEADER['Content-Length']) or toint(HEADER['Content-length']) or toint(HEADER['content-length']) - if body_len and body_len > 0 then - if body_len >= (max_body_size or (1024 * 1024)) then - return nil, 413 - end - local buffers = new_tab(16, 0) - local bsize = body_len - local _, CRLF_END = find(buffer, RE_CRLF2) - if #buffer > CRLF_END then - bsize = bsize - (#buffer - CRLF_END) - buffers[#buffers+1] = split(buffer, CRLF_END + 1) - end - if bsize > 0 and not readall(sock, bsize, buffers) then - return nil, null - end - local BODY = concat(buffers) - if METHOD == 'PUT' then - content['body'] = BODY - return true, content - end - local FILE_ENCODE = 'multipart/form-data' - local XML_ENCODE_1 = 'text/xml' - local XML_ENCODE_2 = 'application/xml' - local JSON_ENCODE = 'application/json' - local URL_ENCODE = 'application/x-www-form-urlencoded' - local format = match(HEADER['Content-Type'] or HEADER['content-type'] or '', '(.-/[^;]*)') - if format == FILE_ENCODE then - local BOUNDARY = match(HEADER['Content-Type'] or HEADER['content-type'] or '', '^.+=[%-]*(.+)') - if BOUNDARY and BOUNDARY ~= '' then - local typ, body = form_multipart(BODY, BOUNDARY) - if typ == FILE_TYPE then - content['files'] = body - elseif typ == ARGS_TYPE then - content['args'] = {} - for _, args in ipairs(body) do - content['args'][args[1]] = args[2] - end + local buffers = new_tab(16, 0) + local bsize = body_len + local _, CRLF_END = find(buffer, RE_CRLF2) + if #buffer > CRLF_END then + bsize = bsize - (#buffer - CRLF_END) + buffers[#buffers+1] = split(buffer, CRLF_END + 1) + end + if bsize > 0 and not readall(sock, bsize, buffers) then + return nil, null + end + BODY = concat(buffers) + end + + local FILE_ENCODE = 'multipart/form-data' + local XML_ENCODE_1 = 'text/xml' + local XML_ENCODE_2 = 'application/xml' + local JSON_ENCODE = 'application/json' + local URL_ENCODE = 'application/x-www-form-urlencoded' + local format = match(HEADER['Content-Type'] or HEADER['content-type'] or '', '(.-/[^;]*)') + + if format == JSON_ENCODE then + content['json'] = true + elseif format == XML_ENCODE_1 or format == XML_ENCODE_2 then + content['xml'] = true + elseif format == FILE_ENCODE then + if format == FILE_ENCODE then + local BOUNDARY = match(HEADER['Content-Type'] or HEADER['content-type'] or '', '^.+=[%-]*(.+)') + if BOUNDARY and BOUNDARY ~= '' then + local files, formargs = form_multipart(BODY, BOUNDARY) + if files then + content['files'] = files + end + if formargs then + content['formargs'] = {} + for _, args in ipairs(formargs) do + content['formargs'][args[1]] = args[2] end end - elseif format == JSON_ENCODE then - content['json'] = true - content['body'] = BODY - elseif format == XML_ENCODE_1 or format == XML_ENCODE_2 then - content['xml'] = true - content['body'] = BODY - elseif format == URL_ENCODE then - content['args'] = form_urlencode(BODY) - else - content['body'] = BODY end end - elseif METHOD == "HEAD" or METHOD == "OPTIONS" then - return true, nil - else - -- 暂未支持其他方法 - return end + + local spl_pos = find(PATH, '%?') + local queryParams = form_argsencode(PATH) + if spl_pos and spl_pos < #PATH then + content['query'] = queryParams + end + if METHOD == "GET" or METHOD == "DELETE" then + content['args'] = queryParams + elseif METHOD == "POST" then + if format == FILE_ENCODE then + content['args'] = content['formargs'] + elseif format == URL_ENCODE then + content['args'] = form_urlencode(BODY) + end + end + content['body'] = BODY return true, content end @@ -460,7 +460,12 @@ function HTTP_PROTOCOL.DISPATCH(sock, opt, http) deCookie(HEADER["Cookie"] or HEADER["cookie"]) end if http_router.enable_rest then - content['query'] = rest_args + if content['query'] then + -- 原则上说,不应该出现 /rest/{id}?id=1 这种设计 + content['query'] = table.rmerge(content['query'], rest_args) + else + content['query'] = rest_args + end end if type(cls) == "table" then local method = cls[lower(METHOD)] From feec5cd895b261030cc48fb1166ba812d5933bb2 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 10 Mar 2022 21:23:25 +0800 Subject: [PATCH 884/956] =?UTF-8?q?utils=E6=96=B0=E5=A2=9E=E4=B8=80?= =?UTF-8?q?=E4=BA=9B=E6=96=B9=E6=B3=95.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/utils/string.lua | 11 ++++++----- lualib/utils/table.lua | 28 ++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/lualib/utils/string.lua b/lualib/utils/string.lua index a2c15b63..0e5f65b9 100644 --- a/lualib/utils/string.lua +++ b/lualib/utils/string.lua @@ -11,6 +11,7 @@ local sbyte = string.byte local sgsub = string.gsub local sgmatch = string.gmatch +local mtype = math.type local tconcat = table.concat ---comment 计算`pattern`在`text`中pos位置开始出现的总次数. @@ -19,17 +20,17 @@ local tconcat = table.concat ---@param pos integer @字符串 ---@return string @返回拼接好的字符串 function string.count (text, pattern, pos) - local count = 0 - if not pos then + if mtype(pos) ~= 'integer' or pos < 1 then pos = 1 end + local count = 0 while true do - local s, e = sfind(text, pattern, pos) - if not s or not e then + pos = sfind(text, pattern, pos) + if not pos then break end count = count + 1 - pos = e + 1 + pos = pos + 1 end return count end diff --git a/lualib/utils/table.lua b/lualib/utils/table.lua index 12be4074..03227c4d 100644 --- a/lualib/utils/table.lua +++ b/lualib/utils/table.lua @@ -342,4 +342,32 @@ function table.reduce(func, list) args[1] = result end return args[1] +end + +---comment 检查`key`是否包含在多个参数集合中. +---@param key any @指定的`key` +---@param ... any @`1`个或`N`个参数组成的集合 +---@return boolean @包含返回`true`, 不包含返回`false` +function table.on(key, ...) + if not key then + return false + end + local tab = {...} + for i = 1, #tab do + if key == tab[i] then + return true + end + end + return false +end + +---comment 将就表的内容转移到新表内, 并且可以直接替换为新表引用(可用于释放大表内存) +---@param tab table @之前的表 +---@return table @新建的表 +function table.replace(tab) + local t = {} + for key, value in pairs(tab) do + t[key] = value + end + return t end \ No newline at end of file From 1b63e8f0244f60495ab186438f7c209842bc0ef4 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Thu, 10 Mar 2022 23:28:34 +0800 Subject: [PATCH 885/956] =?UTF-8?q?=E4=BC=98=E5=8C=96Timer=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/internal/Timer.lua | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lualib/internal/Timer.lua b/lualib/internal/Timer.lua index d45bf66f..197adf53 100644 --- a/lualib/internal/Timer.lua +++ b/lualib/internal/Timer.lua @@ -5,6 +5,9 @@ local co = require "internal.Co" local ti = require "timer" local type = type +local pairs = pairs +local setmetatable = setmetatable + local ti_new = ti.new local ti_start = ti.start @@ -25,7 +28,11 @@ local tab = debug.getregistry() tab['__G_TIMER__'] = TMap local function get_tid(offset) - return (time() + offset * 1e3) * 0.1 // 1 + local ts = time() + if offset > 0 then + ts = ts + offset * 1e3 + end + return ts * 0.1 // 1 end local function get_ctx(tid) @@ -62,11 +69,10 @@ Timer.TCo = co_new(function () for idx = 1, #list do local ctx = list[idx] if ctx[run_idx] then - local cb = ctx[func_idx] if ctx[async_idx] then - cb() + ctx[func_idx]() else - co_spawn(cb) + co_spawn(ctx[func_idx]) end if ctx[again_idx] then -- 如果需要重复 set_ctx(get_tid(ctx[time_idx]), ctx) From d4577d20814c82cbcb036a94e4b59c7ec346f8c4 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 12 Mar 2022 00:37:21 +0800 Subject: [PATCH 886/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0DB=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/test_DB.lua | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/script/test_DB.lua b/script/test_DB.lua index fb5e5045..49444728 100644 --- a/script/test_DB.lua +++ b/script/test_DB.lua @@ -5,7 +5,7 @@ local DB = require "DB" local db = DB:new { host = 'localhost', port = 3306, - database = 'test', + database = 'mysql', username = 'root', password = '123456789', max = 1, @@ -46,10 +46,21 @@ end) -- 测试预编译语句 cf.fork(function ( ... ) - local rkey = db:prepare([[SELECT version() AS version]]) - local ret, err = db:execute(rkey) + local ret, err = db:execute([[SELECT version() AS version]]) if not ret then return LOG:DEBUG(err) end LOG:DEBUG(ret) end) + +-- 测试开启事务 +cf.fork(function () + local status, err = db:transaction(function (session) + LOG:WARN(session:query("show databases")) + LOG:WARN(session:execute("show tables")) + + -- return session:rollback() + return session:commit() + end) + LOG:DEBUG(status, err) +end) From 3c22610c0661a767a5c853a64320ba2a89348558 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 12 Mar 2022 23:05:04 +0800 Subject: [PATCH 887/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0stream=E5=BA=93,=20?= =?UTF-8?q?=E6=8A=BD=E8=B1=A1TCP=E7=BD=91=E7=BB=9C=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E4=BA=A4=E4=BA=92=E5=BA=95=E5=B1=82=E5=AE=9E=E7=8E=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/mssql.lua | 29 +-- lualib/protocol/mysql.lua | 29 +-- lualib/protocol/pgsql.lua | 29 +-- lualib/protocol/redis.lua | 299 ++++++++++++------------- lualib/protocol/websocket/client.lua | 24 +- lualib/protocol/websocket/protocol.lua | 53 ++--- lualib/protocol/websocket/server.lua | 121 ++++------ lualib/stream/init.lua | 184 +++++++++++++++ 8 files changed, 420 insertions(+), 348 deletions(-) create mode 100644 lualib/stream/init.lua diff --git a/lualib/protocol/mssql.lua b/lualib/protocol/mssql.lua index 2350a522..629153bf 100644 --- a/lualib/protocol/mssql.lua +++ b/lualib/protocol/mssql.lua @@ -3,6 +3,7 @@ Author: CandyMi[https://github.com/candymi] ]] +local stream = require "stream" local tcp = require "internal.TCP" local crypt = require "crypt" @@ -886,28 +887,7 @@ function mssql:ctor(opt) end function mssql:read( bytes ) - local sock = self.sock - local buffer = sock:recv(bytes) - if not buffer then - return - end - if #buffer == bytes then - return buffer - end - bytes = bytes - #buffer - local buffers = {buffer} - local sock_read = sock.recv - while 1 do - buffer = sock_read(sock, bytes) - if not buffer then - return - end - bytes = bytes - #buffer - tabinsert(buffers, buffer) - if bytes == 0 then - return tabconcat(buffers) - end - end + return self.sock:readbytes(bytes) end function mssql:write(data) @@ -931,6 +911,9 @@ function mssql:connect( ... ) return nil, "MSSQL Server driver Invalid Configure." end + -- Socket Stream Wrapper. + self.sock = stream(self.sock) + -- 发送TDS-7.0登录协议 self:write(tds_login7(self)) @@ -1046,7 +1029,7 @@ end function mssql:set_timeout(timeout) if self.sock and tonumber(timeout) then - self.sock._timeout = timeout + self.sock:timeout(timeout) end end diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua index cc29fe63..c99647fa 100644 --- a/lualib/protocol/mysql.lua +++ b/lualib/protocol/mysql.lua @@ -3,6 +3,7 @@ Author: CandyMi[https://github.com/candymi] ]] +local stream = require "stream" local tcp = require "internal.TCP" local crypt = require "crypt" local sha1 = crypt.sha1 @@ -133,28 +134,7 @@ local function sock_write (self, data) end local function sock_read (self, bytes) - local sock = self.sock - local buffer = sock:recv(bytes) - if not buffer then - return - end - if #buffer == bytes then - return buffer - end - bytes = bytes - #buffer - local buffers = {buffer} - local sock_recv = sock.recv - while 1 do - buffer = sock_recv(sock, bytes) - if not buffer then - return - end - bytes = bytes - #buffer - insert(buffers, buffer) - if bytes == 0 then - return concat(buffers) - end - end + return self.sock:readbytes(bytes) end -- mysql_native认证 @@ -729,6 +709,9 @@ local function mysql_login (self) return nil, "MySQL Server driver Invalid Configure." end + -- Socket Stream Wrapper. + self.sock = stream(self.sock) + local len, err, packet len, err = read_head(self) if not len then @@ -1012,7 +995,7 @@ end function mysql:set_timeout(timeout) if self.sock and tonumber(timeout) then - self.sock._timeout = timeout + self.sock:timeout(timeout) end end diff --git a/lualib/protocol/pgsql.lua b/lualib/protocol/pgsql.lua index 1afe5c3b..3efae938 100644 --- a/lualib/protocol/pgsql.lua +++ b/lualib/protocol/pgsql.lua @@ -3,6 +3,7 @@ Author: CandyMi[https://github.com/candymi] ]] +local stream = require "stream" local tcp = require "internal.TCP" local crypt = require "crypt" @@ -441,28 +442,7 @@ function pgsql:ctor(opt) end function pgsql:read(bytes) - local sock = self.sock - local buffer = sock:recv(bytes) - if not buffer then - return - end - if #buffer == bytes then - return buffer - end - bytes = bytes - #buffer - local buffers = {buffer} - local sock_read = sock.recv - while 1 do - buffer = sock_read(sock, bytes) - if not buffer then - return - end - bytes = bytes - #buffer - tinsert(buffers, buffer) - if bytes == 0 then - return tconcat(buffers) - end - end + return self.sock:readbytes(bytes) end function pgsql:write(data) @@ -499,6 +479,9 @@ function pgsql:connect() return nil, "PGSQL Server driver Invalid Configure." end + -- Socket Stream Wrapper. + self.sock = stream(self.sock) + -- 发送启动协议 self:write(self:startup()) @@ -575,7 +558,7 @@ end function pgsql:set_timeout(timeout) if self.sock and tonumber(timeout) then - self.sock._timeout = timeout + self.sock:timeout(timeout) end end diff --git a/lualib/protocol/redis.lua b/lualib/protocol/redis.lua index 2ed29872..34118641 100644 --- a/lualib/protocol/redis.lua +++ b/lualib/protocol/redis.lua @@ -2,8 +2,10 @@ local log = require "logging" local class = require "class" local Co = require "internal.Co" local tcp = require "internal.TCP" -local tcp_recv = tcp.recv -local tcp_readline = tcp.readline +local stream = require "stream" +local tcp_send = stream.send +local tcp_readline = stream.readline +local tcp_readbytes = stream.readbytes local new_tab = require "sys".new_tab @@ -41,118 +43,99 @@ local function read_response(sock) end local function sock_readbytes(sock, bytes) - local buffer = tcp_recv(sock, bytes) - if not buffer then - return - end - if #buffer == bytes then - return buffer - end - bytes = bytes - #buffer - local buffers = {buffer} - while 1 do - buffer = tcp_recv(sock, bytes) - if not buffer then - return - end - bytes = bytes - #buffer - insert(buffers, buffer) - if bytes == 0 then - return concat(buffers) - end - end + return tcp_readbytes(sock, bytes) end redcmd[36] = function(sock, data) -- '$' - local bytes = tonumber(data) - if bytes < 0 then - return true, nil - end - local firstline = sock_readbytes(sock, bytes + 2) - return true, sub(firstline, 1, -3) + local bytes = tonumber(data) + if bytes < 0 then + return true, nil + end + local firstline = sock_readbytes(sock, bytes + 2) + return true, sub(firstline, 1, -3) end redcmd[43] = function(sock, data) -- '+' - return true, sub(data, 1, -3) + return true, sub(data, 1, -3) end redcmd[45] = function(sock, data) -- '-' - return false, sub(data, 1, -3) + return false, sub(data, 1, -3) end redcmd[58] = function(sock, data) -- ':' - -- todo: return string later - return true, tonumber(data) + -- todo: return string later + return true, tonumber(data) end redcmd[42] = function(sock, data) -- '*' - local n = tonumber(data) - if n < 0 then - return true, nil - end - local bulk = new_tab(n, 0) - local noerr = true - for i = 1, n do - local ok, v = read_response(sock) - if not ok then - noerr = false - end - bulk[i] = v - end - return noerr, bulk + local n = tonumber(data) + if n < 0 then + return true, nil + end + local bulk = new_tab(n, 0) + local noerr = true + for i = 1, n do + local ok, v = read_response(sock) + if not ok then + noerr = false + end + bulk[i] = v + end + return noerr, bulk end -- 格式化命令为redis protocol local function CMD(...) - local tab = {...} - local lines = new_tab(#tab, 0) - lines[#lines+1] = "*"..#tab - for index = 1, #tab do - lines[#lines+1] = "$"..#tostring(tab[index]) - lines[#lines+1] = tab[index] - if index == #tab then - lines[#lines+1] = "" - end - end - return concat(lines, CRLF) + local tab = {...} + local lines = new_tab(#tab, 0) + lines[#lines+1] = "*"..#tab + for index = 1, #tab do + lines[#lines+1] = "$"..#tostring(tab[index]) + lines[#lines+1] = tab[index] + if index == #tab then + lines[#lines+1] = "" + end + end + return concat(lines, CRLF) end local function read_boolean(sock) - local ok, result = read_response(sock) - if ok then - return ok, result ~= 0 or result == "OK" - end - return ok, result + local ok, result = read_response(sock) + if ok then + return ok, result ~= 0 or result == "OK" + end + return ok, result end local function redis_login(sock, auth, db) - if type(auth) == 'string' and auth ~= '' then - sock:send(CMD("AUTH", auth)) - local ok, err = read_response(sock) - if not ok then - return nil, err - end - end - if toint(db) and toint(db) >= 0 then - sock:send(CMD("SELECT", db)) - local ok, err = read_response(sock) - if not ok then - return nil, err - end - end - sock.state = true - return true + if type(auth) == 'string' and auth ~= '' then + tcp_send(sock, CMD("AUTH", auth)) + local ok, err = read_response(sock) + if not ok then + return nil, err + end + end + if toint(db) and toint(db) >= 0 then + tcp_send(sock, CMD("SELECT", db)) + local ok, err = read_response(sock) + if not ok then + return nil, err + end + end + sock.state = true + return true end local redis = class("redis") function redis:ctor(opt) - self.sock = tcp:new() - self.host = opt.host - self.port = opt.port - self.unixdomain = opt.unixdomain - self.auth = opt.auth - self.db = opt.db + self.sock = tcp:new() + self.host = opt.host + self.port = opt.port + self.unixdomain = opt.unixdomain + self.auth = opt.auth + self.db = opt.db end function redis:isconnected() @@ -160,114 +143,116 @@ function redis:isconnected() end function redis:connect() - -- 尝试多种连接渠道 - if not self.sock:connect_ex(self.unixdomain or "") and not self.sock:connect(self.host, toint(self.port) or 6379) then - return nil, "redis network connect failed." - end - -- 登录状态检查 - local ok, err = redis_login(self.sock, self.auth, self.db) - if not ok then - return nil, "redis login error:" .. (err or 'close') - end - return true + -- 尝试多种连接渠道 + if not self.sock:connect_ex(self.unixdomain or "") and not self.sock:connect(self.host, toint(self.port) or 6379) then + return nil, "redis network connect failed." + end + -- Socket Stream Wrapper. + self.sock = stream(self.sock) + -- 登录状态检查 + local ok, err = redis_login(self.sock, self.auth, self.db) + if not ok then + return nil, "redis login error:" .. (err or 'close') + end + return true end function redis:set_timeout(timeout) self.sock._timeout = timeout - return self + return self end -- 订阅 function redis:psubscribe(pattern, func) - local sock = self.sock - sock:send(CMD("PSUBSCRIBE", pattern)) - local ok, msg = read_response(sock) - if not ok or not msg[2] then - return nil, "PSUBSCRIBE error: 订阅"..tostring(pattern).."失败." - end - co_spawn(function () - while 1 do - local ok, msg = read_response(sock) - if not ok or not msg or not self.sock then - local ok, err = pcall(func, nil) - if not ok then - Log:ERROR(err) - end - return - end - local data = {type = msg[1], source = msg[2], pattern = pattern, payload = msg[3]} - if #msg > 3 then - data = {type = msg[1], source = msg[3], pattern = pattern, payload = msg[4]} - end - local ok, err = pcall(func, data) - if not ok then - return Log:ERROR(err) - end - end - end) - return ok, msg + local sock = self.sock + tcp_send(sock, CMD("PSUBSCRIBE", pattern)) + local ok, msg = read_response(sock) + if not ok or not msg[2] then + return nil, "PSUBSCRIBE error: 订阅"..tostring(pattern).."失败." + end + co_spawn(function () + while 1 do + local ok, msg = read_response(sock) + if not ok or not msg or not self.sock then + local ok, err = pcall(func, nil) + if not ok then + Log:ERROR(err) + end + return + end + local data = {type = msg[1], source = msg[2], pattern = pattern, payload = msg[3]} + if #msg > 3 then + data = {type = msg[1], source = msg[3], pattern = pattern, payload = msg[4]} + end + local ok, err = pcall(func, data) + if not ok then + return Log:ERROR(err) + end + end + end) + return ok, msg end -- 订阅 function redis:subscribe(pattern, func) - return self:psubscribe(pattern, func) + return self:psubscribe(pattern, func) end -- 发布 function redis:publish(pattern, data) - local sock = self.sock - sock:send(CMD("PUBLISH", pattern, data)) - return read_response(sock) + local sock = self.sock + tcp_send(sock, CMD("PUBLISH", pattern, data)) + return read_response(sock) end -- 查询键是否存在 function redis:exists(key) - local sock = self.sock - sock:send(CMD("EXISTS", key)) - return read_boolean(sock) + local sock = self.sock + tcp_send(sock, CMD("EXISTS", key)) + return read_boolean(sock) end -- 查询元素是否集合成员 function redis:sismember(key, value) - local sock = self.sock - sock:send(CMD("SISMEMBER", key, value)) - return read_boolean(sock) + local sock = self.sock + tcp_send(sock, CMD("SISMEMBER", key, value)) + return read_boolean(sock) end -- 执行命令 function redis:cmd(...) - local sock = self.sock - sock:send(CMD(...)) - return read_response(sock) + local sock = self.sock + tcp_send(sock, CMD(...)) + return read_response(sock) end -- 管道命令 function redis:pipeline(opt) - local cmds = {} - if opt and #opt > 0 then - for _, cmd in ipairs(opt) do - cmds[#cmds+1] = CMD(unpack(cmd)) - end - end - local max_read_times = #cmds - if max_read_times > 0 then - local sock = self.sock - sock:send(concat(cmds)) - local rets = new_tab(max_read_times, 0) - for index = 1, max_read_times do - rets[index] = {read_response(sock)} - end - return true, rets - end - return nil + local cmds = {} + if opt and #opt > 0 then + for _, cmd in ipairs(opt) do + cmds[#cmds+1] = CMD(unpack(cmd)) + end + end + local max_read_times = #cmds + if max_read_times > 0 then + local sock = self.sock + tcp_send(sock, concat(cmds)) + local rets = new_tab(max_read_times, 0) + for index = 1, max_read_times do + rets[index] = {read_response(sock)} + end + return true, rets + end + return nil end function redis:close() - if self.sock then - self.sock.state = false - self.sock:close() - self.sock = nil - end + if self.sock then + self.sock.state = false + self.sock:close() + self.sock = nil + end end return redis diff --git a/lualib/protocol/websocket/client.lua b/lualib/protocol/websocket/client.lua index e77282bf..5672c843 100644 --- a/lualib/protocol/websocket/client.lua +++ b/lualib/protocol/websocket/client.lua @@ -4,6 +4,7 @@ local log = require "logging" local Log = log:new{ dump = true, path = "protocol-wsclient"} local tcp = require "internal.TCP" +local stream = require "stream" local cf = require "cf" local cf_fork = cf.fork @@ -191,7 +192,7 @@ function websocket:ctor (opt) self.ssl = nil self.ext = nil self.url = opt.url - self.sock = tcp:new() + self.sock = stream(tcp:new()) self.send_masked = true self.sock._timeout = toint(opt.timeout) self.max_payload_len = opt.max_payload_len or 65535 @@ -259,9 +260,7 @@ function websocket:send (data, bin) end assert(type(data) == 'string' and data ~= '', "Invalid websocket send data.") local sock = self.sock - return self:request(function () - return _send_frame(sock, true, bin and 0x02 or 0x01, data, self.max_payload_len, self.send_masked, self.ext) - end) + return _send_frame(sock, true, bin and 0x02 or 0x01, data, self.send_masked, self.ext) end -- 发送ping @@ -270,9 +269,7 @@ function websocket:ping(data) return nil, 'not connected.' end local sock = self.sock - return self:request(function () - return _send_frame(sock, true, 0x09, type(data) == 'string' and #data <= 125 and data or "", self.max_payload_len, self.send_masked, self.ext) - end) + return _send_frame(sock, true, 0x09, type(data) == 'string' and #data <= 125 and data or "", self.send_masked, self.ext) end -- 发送pong @@ -281,9 +278,7 @@ function websocket:pong(data) return nil, 'not connected.' end local sock = self.sock - return self:request(function () - return _send_frame(sock, true, 0x0A, type(data) == 'string' and #data <= 125 and data or "", self.max_payload_len, self.send_masked, self.ext) - end) + return _send_frame(sock, true, 0x0A, type(data) == 'string' and #data <= 125 and data or "", self.send_masked, self.ext) end -- 清理连接 @@ -292,14 +287,7 @@ function websocket:close () local sock = self.sock return self:request(function () if self.state then - self:request(function () - return _send_frame(sock, true, 0x08, strpack(">H", 1000), self.max_payload_len, self.send_masked, self.ext) - end) - end - if sock then - self:request(function () - return sock_close(self) - end) + return _send_frame(sock, true, 0x08, strpack(">H", 1000), self.send_masked, self.ext) end end) end diff --git a/lualib/protocol/websocket/protocol.lua b/lualib/protocol/websocket/protocol.lua index 594b0a27..e0e86078 100644 --- a/lualib/protocol/websocket/protocol.lua +++ b/lualib/protocol/websocket/protocol.lua @@ -8,6 +8,8 @@ local wsuncompress = lz.wsuncompress local new_tab = require("sys").new_tab local error = error +local assert = assert + local strpack = string.pack local strunpack = string.unpack local random = math.random @@ -24,31 +26,15 @@ local WS_TYPE = { } local function sock_recv (sock, bytes) - local buffer = sock:recv(bytes) - if not buffer then - return - end - if #buffer == bytes then - return buffer - end - bytes = bytes - #buffer - local buffers = {buffer} - local sock_read = sock.recv - while 1 do - buffer = sock_read(sock, bytes) - if not buffer then - return - end - bytes = bytes - #buffer - insert(buffers, buffer) - if bytes == 0 then - return concat(buffers) - end - end + local buf = sock:readbytes(bytes) + if not buf then + sock.closed = true + end + return buf end local function sock_send (sock, data) - return sock:send(data) + return sock:write(data) end local function wsdeflate(data) @@ -177,7 +163,7 @@ function protocol.recv_frame(sock, max_payload_len, force_masking, buffers) end -- close帧有状态码 - if opcode == "close" then + if opcode == 'close' then data = data:sub(3) end @@ -191,10 +177,9 @@ end ---@param fin boolean @结束帧标志 ---@param opcode integer @消息类型 ---@param payload string @数据载荷 ----@param max_payload_len integer @最大长度限制 ---@param masking string @数据掩码 ---@param ext string @协议扩展 -function protocol.send_frame(sock, fin, opcode, payload, max_payload_len, masking, ext) +function protocol.send_frame(sock, fin, opcode, payload, masking, ext) local payload_len = #payload @@ -206,7 +191,7 @@ function protocol.send_frame(sock, fin, opcode, payload, max_payload_len, maskin end -- 结束位标志位 + 保留位 + 消息类型 - local h1 = 0x80 | opcode + local h1 = (fin and 0x80 or 0x00) | opcode -- 如果有扩展协议则加上扩展响应头部 if (opc ~= 'close' and opc ~= 'ping' and opc ~= 'pong') and payload_len > 125 and ext == 'deflate' then h1 = h1 | 0x40 @@ -225,22 +210,26 @@ function protocol.send_frame(sock, fin, opcode, payload, max_payload_len, maskin h2, len_ext = h2 | 0x7F, strpack(">I8", payload_len) end - local buffers = new_tab(3, 0) - - insert(buffers, strpack(">BB", h1, h2)) + local idx = 1 + local buffers = new_tab(4, 0) + buffers[idx] = strpack(">BB", h1, h2) if len_ext then - buffers[#buffers+1] = len_ext + idx = idx + 1 + buffers[idx] = len_ext end -- 创建随机掩码并与数据载荷进行异或 if masking and payload_len > 0 then masking = strpack(">BBBB", random(255), random(255), random(255), random(255)) payload = xor_str(payload, masking) - insert(buffers, masking) + idx = idx + 1 + buffers[idx] = masking end - return sock_send(sock, concat(buffers)) and sock_send(sock, payload) + -- 根据实际情况需要减少发送数据次数. + buffers[idx + 1] = payload + sock_send(sock, concat(buffers)) end return protocol \ No newline at end of file diff --git a/lualib/protocol/websocket/server.lua b/lualib/protocol/websocket/server.lua index d7513a99..098c7565 100644 --- a/lualib/protocol/websocket/server.lua +++ b/lualib/protocol/websocket/server.lua @@ -1,14 +1,14 @@ +local stream = require "stream" + local cf = require "cf" local cf_fork = cf.fork local cf_sleep = cf.sleep -local new_tab = require"sys".new_tab - local wsproto = require "protocol.websocket.protocol" local _recv_frame = wsproto.recv_frame local _send_frame = wsproto.send_frame -local Log = require "logging":new { dump = true, path = 'protocol-websocket-server'} +local LOG = require "logging":new { dump = true, path = 'protocol-websocket-server'} local type = type local pcall = pcall @@ -22,10 +22,14 @@ local class = require "class" local ws = class("ws") function ws:ctor(opt) + self.ext = opt.ext self.sock = opt.sock - self.send_masked = nil + self.closed = false self.max_payload_len = 65535 - self.ext = opt.ext +end + +function ws:set_timeout(timeout) + self.sock:timeout(timeout) end -- 设置发送掩码 @@ -38,99 +42,72 @@ function ws:set_max_payload_len(max_payload_len) self.max_payload_len = max_payload_len end --- 异步消息发送 -function ws:add_to_queue (f) - if not self.queue then - self.queue = new_tab(64, 0) - self.co = cf_fork(function () - for _, func in ipairs(self.queue) do - local ok, writeable = pcall(func) - if not ok then - Log:ERROR(writeable) - end - if not ok or not writeable then - break - end - end - self.co, self.queue = nil, nil - end) +-- 发送TEXT/BINARY帧 +function ws:send(data, binary) + if self.closed or self.sock.closed then + return end - return insert(self.queue, f) + _send_frame(self.sock, true, binary and 0x02 or 0x01, data, false, self.ext) end --- 发送text/binary消息 -function ws:send (data, binary) - if self.closed then +-- 发送PING帧 +function ws:ping(data) + if self.closed or self.sock.closed then return end - assert(type(data) == 'string' and data ~= '', "websoket error: send need string data.") - self:add_to_queue(function () - return _send_frame(self.sock, true, binary and 0x02 or 0x01, data, self.max_payload_len, self.send_masked, self.ext) - end) + return _send_frame(self.sock, true, 0x09, data or '', false, self.ext) end --- 发送close帧 +-- 发送CLOSE帧 function ws:close(data) - if self.closed then + if self.closed or self.sock.closed then return end self.closed = true - self:add_to_queue(function () - return _send_frame(self.sock, true, 0x08, strpack(">H", 1000) .. (type(data) == 'string' and data or ""), self.max_payload_len, self.send_masked, self.ext) and self.sock:close() - end) + _send_frame(self.sock, true, 0x08, strpack(">H", 1000) .. (type(data) == 'string' and data or ""), false, self.ext) end --- 退出 -function ws:exit() - self:close() - self:add_to_queue(function () end) -end - -local Websocket = { __Version__ = 1.0 } +local Websocket = { __Version__ = 0.1 } --- Websocket Server 事件循环 -function Websocket.start(sock, obj, args, headers, ext) - local w = ws:new { sock = sock, ext = ext } - - local cls = obj:new { ws = w, args = args, headers = headers } +function Websocket.start(sock, cls, args, headers, ext) local on_open = assert(type(cls.on_open) == 'function' and cls.on_open, "'on_open' method is not implemented.") local on_message = assert(type(cls.on_message) == 'function' and cls.on_message, "'on_message' method is not implemented.") local on_error = assert(type(cls.on_error) == 'function' and cls.on_error, "'on_error' method is not implemented.") local on_close = assert(type(cls.on_close) == 'function' and cls.on_close, "'on_close' method is not implemented.") - sock._timeout = cls.timeout or nil - local max_payload_len = cls.max_payload_len or 65535 - w:set_max_payload_len(max_payload_len) - local ok, err = pcall(on_open, cls) + sock = stream(sock) + local w = ws { sock = sock, ext = ext } + local obj = cls{ ws = w, args = args, headers = headers } + + local timeout = obj.timeout or 0 + local max_payload_len = obj.max_payload_len or 65535 + w:set_timeout(timeout); w:set_max_payload_len(max_payload_len) + + local ok, err = pcall(on_open, obj) if not ok then - Log:ERROR(err) - return + return LOG:ERROR(err) end - -- Websocket 交互协议循环 - while 1 do - local data, typ, errinfo = _recv_frame(sock, max_payload_len, true) - if not typ or typ == 'close' or typ == 'error' then - w:exit() - if typ == 'error' then - ok, err = pcall(on_error, cls, errinfo) - if not ok then - Log:ERROR(err) - end - end - ok, err = pcall(on_close, cls, data or errinfo) + -- 开始监听 + :: CONTINUE :: + local data, typ, errinfo = _recv_frame(sock, max_payload_len, true) + if not typ or typ == 'close' or typ == 'error' then + if typ == 'error' then + ok, err = pcall(on_error, obj, errinfo) if not ok then - Log:ERROR(err) + LOG:ERROR(err) end - break - end - if typ == 'ping' then - w:add_to_queue(function () return _send_frame(sock, true, 0x0A, data or '', max_payload_len, false, ext) end) end - if typ == 'text' or typ == 'binary' then - cf_fork(on_message, cls, data, typ == 'binary') + ok, err = pcall(on_close, obj, data or errinfo) + if not ok then + LOG:ERROR(err) end + return w:close(), cf_sleep(0) + elseif typ == 'ping' then + _send_frame(sock, true, 0x0A, data or '', false, ext) + elseif typ == 'text' or typ == 'binary' then + cf_fork(on_message, obj, data, typ == 'binary') end - return cf_sleep(0) + goto CONTINUE end return Websocket \ No newline at end of file diff --git a/lualib/stream/init.lua b/lualib/stream/init.lua new file mode 100644 index 00000000..6e1ff7ed --- /dev/null +++ b/lualib/stream/init.lua @@ -0,0 +1,184 @@ +local TCP = require "internal.TCP" +local sock_read = TCP.recv +local sock_write = TCP.send +local sock_readline = TCP.readline +local sock_connect = TCP.connect +local sock_connectx = TCP.connect_ex +local sock_sslconnect = TCP.ssl_connect + +local new_tab = require "sys".new_tab + +local cf = require "cf" +local cf_fork = cf.fork + +local type = type +local error = error +local ipairs = ipairs +local getmetatable = getmetatable + +local mtype = math.type +local strfmt = string.format +local tconcat = table.concat + +local class = require "class" + +local Stream = class("Stream") + +function Stream:ctor(sock) + if getmetatable(sock) ~= TCP then + error(strfmt("[Stream Error]: Invalid Socket object in (%s:%d).", debug.getinfo(3, "S").source, debug.getinfo(3, "l").currentline)) + end + self.tcp = sock +end + +function Stream:set_fd(fd) + self.tcp:set_fd(fd) + return self +end + +function Stream:timeout(ts) + self.tcp:timeout(ts) + return self +end + +function Stream:connect(domain, port) + return sock_connect(self.tcp, domain, port) +end + +function Stream:ssl_connect(domain, port) + return sock_sslconnect(self.tcp, domain, port) +end + +function Stream:connectx(path) + return sock_connectx(self.tcp, path) +end + +---comment @同步写入(阻塞当前协程) +---@param buf string @待写入的数据 +---@return boolean @写入成功返回`true`, 写入失败返回`false` +function Stream:send(buf) + if type (buf) ~= 'string' or buf == '' then + error(strfmt("[Stream Error]: pass Invalid send buffer in (%s:%d)", debug.getinfo(2, "S").source, debug.getinfo(2, "l").currentline)) + end + return sock_write(self.tcp, buf) +end + +---comment @异步写入(不会阻塞当前协程) +---@param buf string @待写入的数据 +function Stream:write(buf) + if type (buf) ~= 'string' or buf == '' then + error(strfmt("[Stream Error]: pass Invalid write buffer in (%s:%d)", debug.getinfo(2, "S").source, debug.getinfo(2, "l").currentline)) + end + -- 异步写入队列 + if not self.wqueue then + self.wqueue = new_tab(8, 0) + cf_fork(function () + local sock = self.tcp + if sock and self.wqueue then + for _, buffer in ipairs(self.wqueue) do + if not sock_write(sock, buffer) then + break + end + end + end + self.wqueue = nil + end) + end + self.wqueue[#self.wqueue+1] = buf +end + +function Stream:recv(nbytes) + return self:read(nbytes) +end + +---comment @读取指定数量的网络数据(此函数只要缓冲区里有数据立刻返回) +---@param nbytes integer @指定的要读取的数量. +---@return string | nil @读取成功返回内容, 失败返回`nil` +function Stream:read(nbytes) + if mtype(nbytes) ~= 'integer' or nbytes < 1 then + error(strfmt("[Stream Error]: Pass invalid nbytes in (%s:%d)", debug.getinfo(2, "S").source, debug.getinfo(2, "l").currentline)) + end + return sock_read(self.tcp, nbytes) +end + +---comment @读取数据直到遇到指定分隔符, 可以选择返回的数据不包括分隔符. +---@param sp string @字符串类型的分隔符 +---@param nosp boolean @返回数据是否包括分隔符 +---@return string | nil @读取成功返回内容, 失败返回`nil` +function Stream:readline(sp, nosp) + if type(sp) ~= 'string' or sp == '' then + error(strfmt("[Stream Error]: Pass invalid readline char in (%s:%d)", debug.getinfo(2, "S").source, debug.getinfo(2, "l").currentline)) + end + return sock_readline(self.tcp, sp, nosp) +end + +---comment @读取指定数量的网络数据(读取足够字节或者连接断开才会返回). +---@param nbytes integer @指定的要读取的数量. +---@return string | nil @读取成功返回内容, 失败返回`nil` +function Stream:readbytes(nbytes) + if mtype(nbytes) ~= 'integer' or nbytes < 1 then + error(strfmt("[Stream Error]: Pass invalid nbytes in (%s:%d)", debug.getinfo(2, "S").source, debug.getinfo(2, "l").currentline)) + end + local sock, buffer, len, buffers = self.tcp, nil, nil, nil + :: CONTINUE :: + buffer, len = sock_read(sock, nbytes) + if not buffer then + return + end + -- 检查读取的字节数 + if len == nbytes then + -- 如果是一次性读取完毕直接返回 + if not buffers then + return buffer + end + -- 如果是多次读取完毕 + buffers[#buffers+1] = buffer + return tconcat(buffers) + end + if not buffers then + buffers = new_tab(3, 0) + end + -- 计算字节数并且准备继续读取 + buffers[#buffers+1] = buffer + nbytes = nbytes - len + goto CONTINUE +end + +---comment @监听网络套接字 +---@param ip string @监听指定地址 +---@param port integer @监听指定端口 +---@param cb function @连接建立成功的回调 +function Stream:listen(ip, port, cb) + self.tcp:listen(ip, port, function (fd, ...) + return cb(Stream(TCP():set_fd(fd):timeout(0)), ...) + end) +end + +---comment @监听本机套接字 +---@param path string @本机套接字所在路径 +---@param cb function @连接建立成功的回调 +function Stream:listenx(path, cb) + self.tcp:listen_ex(path, function (fd, ...) + return cb(Stream(TCP():set_fd(fd):timeout(0)), ...) + end) +end + +function Stream:run_forever() + return cf.wait() +end + +function Stream:run() + return self:run_forever() +end + +function Stream:close() + if self.tcp then + self.tcp:close() + self.tcp = nil + end + if self.wqueue then + self.wqueue = nil + end +end + +return Stream \ No newline at end of file From a5130d0f7f7473f09b50a6720de478219cedb4da Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 19 Mar 2022 00:03:43 +0800 Subject: [PATCH 888/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dmssql=E9=A9=B1?= =?UTF-8?q?=E5=8A=A8=E5=AD=97=E7=AC=A6=E4=B8=B2null=E5=80=BC=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/mssql.lua | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/lualib/protocol/mssql.lua b/lualib/protocol/mssql.lua index 629153bf..5b5c9ce9 100644 --- a/lualib/protocol/mssql.lua +++ b/lualib/protocol/mssql.lua @@ -258,17 +258,15 @@ FTYPE_TAB[TYPE_NVARCHAR] = function (packet, pos) end FTYPE_TAB[TYPE_TEXT] = function (packet, pos) - local large_type_size, collate_codepage, collate_flags, collate_charset_id, table_name_len - large_type_size, collate_codepage, collate_flags, collate_charset_id, pos = strunpack(" Date: Sat, 19 Mar 2022 03:57:21 +0800 Subject: [PATCH 889/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0LFS=E7=89=88=E6=9C=AC?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lfs/lfs.c | 1223 ++++++++++++++++++++++++----------------- luaclib/src/lfs/lfs.h | 7 +- 2 files changed, 728 insertions(+), 502 deletions(-) diff --git a/luaclib/src/lfs/lfs.c b/luaclib/src/lfs/lfs.c index 05d0b552..ff770775 100644 --- a/luaclib/src/lfs/lfs.c +++ b/luaclib/src/lfs/lfs.c @@ -1,6 +1,7 @@ /* ** LuaFileSystem -** Copyright Kepler Project 2003 - 2017 (http://keplerproject.github.io/luafilesystem) +** Copyright Kepler Project 2003 - 2020 +** (http://keplerproject.github.io/luafilesystem) ** ** File system manipulation library. ** This library offers these functions: @@ -20,17 +21,17 @@ */ #ifndef LFS_DO_NOT_USE_LARGE_FILE -#ifndef _WIN32 -#ifndef _AIX -#define _FILE_OFFSET_BITS 64 /* Linux, Solaris and HP-UX */ + #ifndef _WIN32 + #ifndef _AIX + #define _FILE_OFFSET_BITS 64 /* Linux, Solaris and HP-UX */ #else -#define _LARGE_FILES 1 /* AIX */ -#endif -#endif + #define _LARGE_FILES 1 /* AIX */ + #endif + #endif #endif #ifndef LFS_DO_NOT_USE_LARGE_FILE -#define _LARGEFILE64_SOURCE + #define _LARGEFILE64_SOURCE #endif #include @@ -41,36 +42,51 @@ #include #ifdef _WIN32 - #include - #include - #include - #include - #ifdef __BORLANDC__ - #include - #else - #include - #endif - #include - /* MAX_PATH seems to be 260. Seems kind of small. Is there a better one? */ - #define LFS_MAXPATHLEN MAX_PATH + +#include +#include +#include +#include + +#ifdef __BORLANDC__ +#include +#else +#include +#endif + +#include + +/* MAX_PATH seems to be 260. Seems kind of small. Is there a better one? */ +#define LFS_MAXPATHLEN MAX_PATH + +#else + +#include +#include +#include +#include +#include +#include /* for MAXPATHLEN */ + +#ifdef MAXPATHLEN +#define LFS_MAXPATHLEN MAXPATHLEN #else - #include - #include - #include - #include - #include - #include /* for MAXPATHLEN */ - #define LFS_MAXPATHLEN MAXPATHLEN +#include /* for _POSIX_PATH_MAX */ +#define LFS_MAXPATHLEN _POSIX_PATH_MAX +#endif + #endif -#include +#include +#include +#include #include "lfs.h" -#define LFS_VERSION "1.7.0" +#define LFS_VERSION "1.8.0" #define LFS_LIBNAME "lfs" -#if LUA_VERSION_NUM >= 503 /* Lua 5.3 */ +#if LUA_VERSION_NUM >= 503 /* Lua 5.3+ */ #ifndef luaL_optlong #define luaL_optlong luaL_optinteger @@ -79,9 +95,9 @@ #endif #if LUA_VERSION_NUM >= 502 -# define new_lib(L, l) (luaL_newlib(L, l)) +#define new_lib(L, l) (luaL_newlib(L, l)) #else -# define new_lib(L, l) (lua_newtable(L), luaL_register(L, NULL, l)) +#define new_lib(L, l) (lua_newtable(L), luaL_register(L, NULL, l)) #endif /* Define 'strerror' for systems that do not implement it */ @@ -91,58 +107,139 @@ #define DIR_METATABLE "directory metatable" typedef struct dir_data { - int closed; + int closed; #ifdef _WIN32 - intptr_t hFile; - char pattern[MAX_PATH+1]; + intptr_t hFile; + char pattern[MAX_PATH + 1]; #else - DIR *dir; + DIR *dir; #endif } dir_data; #define LOCK_METATABLE "lock metatable" #ifdef _WIN32 - #ifdef __BORLANDC__ - #define lfs_setmode(file, m) (setmode(_fileno(file), m)) - #define STAT_STRUCT struct stati64 - #else - #define lfs_setmode(file, m) (_setmode(_fileno(file), m)) - #define STAT_STRUCT struct _stati64 - #endif + +#ifdef __BORLANDC__ +#define lfs_setmode(file, m) (setmode(_fileno(file), m)) +#define STAT_STRUCT struct stati64 +#else +#define lfs_setmode(file, m) (_setmode(_fileno(file), m)) +#define STAT_STRUCT struct _stati64 +#endif + +#ifndef _S_IFLNK +#define _S_IFLNK 0x400 +#endif + +#ifndef S_ISDIR +#define S_ISDIR(mode) (mode&_S_IFDIR) +#endif +#ifndef S_ISREG +#define S_ISREG(mode) (mode&_S_IFREG) +#endif +#ifndef S_ISLNK +#define S_ISLNK(mode) (mode&_S_IFLNK) +#endif +#ifndef S_ISSOCK +#define S_ISSOCK(mode) (0) +#endif +#ifndef S_ISFIFO +#define S_ISFIFO(mode) (0) +#endif +#ifndef S_ISCHR +#define S_ISCHR(mode) (mode&_S_IFCHR) +#endif +#ifndef S_ISBLK +#define S_ISBLK(mode) (0) +#endif + #define STAT_FUNC _stati64 -#define LSTAT_FUNC STAT_FUNC +#define LSTAT_FUNC lfs_win32_lstat + #else + #define _O_TEXT 0 #define _O_BINARY 0 #define lfs_setmode(file, m) ((void)file, (void)m, 0) #define STAT_STRUCT struct stat #define STAT_FUNC stat #define LSTAT_FUNC lstat + #endif #ifdef _WIN32 - #define lfs_mkdir _mkdir +#define lfs_mkdir _mkdir #else - #define lfs_mkdir(path) (mkdir((path), \ +#define lfs_mkdir(path) (mkdir((path), \ S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH)) #endif +#ifdef _WIN32 + +int lfs_win32_pusherror(lua_State * L) +{ + int en = GetLastError(); + lua_pushnil(L); + if (en == ERROR_FILE_EXISTS || en == ERROR_SHARING_VIOLATION) + lua_pushstring(L, "File exists"); + else + lua_pushstring(L, strerror(en)); + return 2; +} + +#define TICKS_PER_SECOND 10000000 +#define EPOCH_DIFFERENCE 11644473600LL +time_t windowsToUnixTime(FILETIME ft) +{ + ULARGE_INTEGER uli; + uli.LowPart = ft.dwLowDateTime; + uli.HighPart = ft.dwHighDateTime; + return (time_t) (uli.QuadPart / TICKS_PER_SECOND - EPOCH_DIFFERENCE); +} + +int lfs_win32_lstat(const char *path, STAT_STRUCT * buffer) +{ + WIN32_FILE_ATTRIBUTE_DATA win32buffer; + if (GetFileAttributesEx(path, GetFileExInfoStandard, &win32buffer)) { + if (!(win32buffer.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + return STAT_FUNC(path, buffer); + } + buffer->st_mode = _S_IFLNK; + buffer->st_dev = 0; + buffer->st_ino = 0; + buffer->st_nlink = 0; + buffer->st_uid = 0; + buffer->st_gid = 0; + buffer->st_rdev = 0; + buffer->st_atime = windowsToUnixTime(win32buffer.ftLastAccessTime); + buffer->st_mtime = windowsToUnixTime(win32buffer.ftLastWriteTime); + buffer->st_ctime = windowsToUnixTime(win32buffer.ftCreationTime); + buffer->st_size = 0; + return 0; + } else { + return 1; + } +} + +#endif + /* ** Utility functions */ -static int pusherror(lua_State *L, const char *info) +static int pusherror(lua_State * L, const char *info) { - lua_pushnil(L); - if (info==NULL) - lua_pushstring(L, strerror(errno)); - else - lua_pushfstring(L, "%s: %s", info, strerror(errno)); - lua_pushinteger(L, errno); - return 3; + lua_pushnil(L); + if (info == NULL) + lua_pushstring(L, strerror(errno)); + else + lua_pushfstring(L, "%s: %s", info, strerror(errno)); + lua_pushinteger(L, errno); + return 3; } -static int pushresult(lua_State *L, int res, const char *info) { +static int pushresult(lua_State * L, int res, const char *info) +{ if (res == -1) { return pusherror(L, info); } else { @@ -155,17 +252,18 @@ static int pushresult(lua_State *L, int res, const char *info) { /* ** This function changes the working (current) directory */ -static int change_dir (lua_State *L) { - const char *path = luaL_checkstring(L, 1); - if (chdir(path)) { - lua_pushnil (L); - lua_pushfstring (L,"Unable to change working directory to '%s'\n%s\n", - path, chdir_error); - return 2; - } else { - lua_pushboolean (L, 1); - return 1; - } +static int change_dir(lua_State * L) +{ + const char *path = luaL_checkstring(L, 1); + if (chdir(path)) { + lua_pushnil(L); + lua_pushfstring(L, "Unable to change working directory to '%s'\n%s\n", + path, chdir_error); + return 2; + } else { + lua_pushboolean(L, 1); + return 1; + } } /* @@ -173,59 +271,62 @@ static int change_dir (lua_State *L) { ** If unable to get the current directory, it returns nil ** and a string describing the error */ -static int get_dir (lua_State *L) { +static int get_dir(lua_State * L) +{ #ifdef NO_GETCWD - lua_pushnil(L); - lua_pushstring(L, "Function 'getcwd' not provided by system"); - return 2; + lua_pushnil(L); + lua_pushstring(L, "Function 'getcwd' not provided by system"); + return 2; #else - char *path = NULL; - /* Passing (NULL, 0) is not guaranteed to work. Use a temp buffer and size instead. */ - size_t size = LFS_MAXPATHLEN; /* initial buffer size */ - int result; - while (1) { - char* path2 = realloc(path, size); - if (!path2) /* failed to allocate */ { - result = pusherror(L, "get_dir realloc() failed"); - break; - } - path = path2; - if (getcwd(path, size) != NULL) { - /* success, push the path to the Lua stack */ - lua_pushstring(L, path); - result = 1; - break; - } - if (errno != ERANGE) { /* unexpected error */ - result = pusherror(L, "get_dir getcwd() failed"); - break; - } - /* ERANGE = insufficient buffer capacity, double size and retry */ - size *= 2; + char *path = NULL; + /* Passing (NULL, 0) is not guaranteed to work. + Use a temp buffer and size instead. */ + size_t size = LFS_MAXPATHLEN; /* initial buffer size */ + int result; + while (1) { + char *path2 = realloc(path, size); + if (!path2) { /* failed to allocate */ + result = pusherror(L, "get_dir realloc() failed"); + break; + } + path = path2; + if (getcwd(path, size) != NULL) { + /* success, push the path to the Lua stack */ + lua_pushstring(L, path); + result = 1; + break; + } + if (errno != ERANGE) { /* unexpected error */ + result = pusherror(L, "get_dir getcwd() failed"); + break; } - free(path); - return result; + /* ERANGE = insufficient buffer capacity, double size and retry */ + size *= 2; + } + free(path); + return result; #endif } /* ** Check if the given element on the stack is a file and returns it. */ -static FILE *check_file (lua_State *L, int idx, const char *funcname) { +static FILE *check_file(lua_State * L, int idx, const char *funcname) +{ #if LUA_VERSION_NUM == 501 - FILE **fh = (FILE **)luaL_checkudata (L, idx, "FILE*"); - if (*fh == NULL) { - luaL_error (L, "%s: closed file", funcname); - return 0; - } else - return *fh; -#elif LUA_VERSION_NUM >= 502 - luaL_Stream *fh = (luaL_Stream *)luaL_checkudata (L, idx, "FILE*"); - if (fh->closef == 0 || fh->f == NULL) { - luaL_error (L, "%s: closed file", funcname); - return 0; - } else - return fh->f; + FILE **fh = (FILE **) luaL_checkudata(L, idx, "FILE*"); + if (*fh == NULL) { + luaL_error(L, "%s: closed file", funcname); + return 0; + } else + return *fh; +#elif LUA_VERSION_NUM >= 502 && LUA_VERSION_NUM <= 504 + luaL_Stream *fh = (luaL_Stream *) luaL_checkudata(L, idx, "FILE*"); + if (fh->closef == 0 || fh->f == NULL) { + luaL_error(L, "%s: closed file", funcname); + return 0; + } else + return fh->f; #else #error unsupported Lua version #endif @@ -235,90 +336,112 @@ static FILE *check_file (lua_State *L, int idx, const char *funcname) { /* ** */ -static int _file_lock (lua_State *L, FILE *fh, const char *mode, const long start, long len, const char *funcname) { - int code; +static int _file_lock(lua_State * L, FILE * fh, const char *mode, + const long start, long len, const char *funcname) +{ + int code; #ifdef _WIN32 - /* lkmode valid values are: - LK_LOCK Locks the specified bytes. If the bytes cannot be locked, the program immediately tries again after 1 second. If, after 10 attempts, the bytes cannot be locked, the constant returns an error. - LK_NBLCK Locks the specified bytes. If the bytes cannot be locked, the constant returns an error. - LK_NBRLCK Same as _LK_NBLCK. - LK_RLCK Same as _LK_LOCK. - LK_UNLCK Unlocks the specified bytes, which must have been previously locked. - - Regions should be locked only briefly and should be unlocked before closing a file or exiting the program. - - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt__locking.asp - */ - int lkmode; - switch (*mode) { - case 'r': lkmode = LK_NBLCK; break; - case 'w': lkmode = LK_NBLCK; break; - case 'u': lkmode = LK_UNLCK; break; - default : return luaL_error (L, "%s: invalid mode", funcname); - } - if (!len) { - fseek (fh, 0L, SEEK_END); - len = ftell (fh); - } - fseek (fh, start, SEEK_SET); + /* lkmode valid values are: + LK_LOCK Locks the specified bytes. If the bytes cannot be locked, + the program immediately tries again after 1 second. + If, after 10 attempts, the bytes cannot be locked, + the constant returns an error. + LK_NBLCK Locks the specified bytes. If the bytes cannot be locked, + the constant returns an error. + LK_NBRLCK Same as _LK_NBLCK. + LK_RLCK Same as _LK_LOCK. + LK_UNLCK Unlocks the specified bytes, which must have been + previously locked. + Regions should be locked only briefly and should be unlocked + before closing a file or exiting the program. + http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt__locking.asp + */ + int lkmode; + switch (*mode) { + case 'r': + lkmode = LK_NBLCK; + break; + case 'w': + lkmode = LK_NBLCK; + break; + case 'u': + lkmode = LK_UNLCK; + break; + default: + return luaL_error(L, "%s: invalid mode", funcname); + } + if (!len) { + fseek(fh, 0L, SEEK_END); + len = ftell(fh); + } + fseek(fh, start, SEEK_SET); #ifdef __BORLANDC__ - code = locking (fileno(fh), lkmode, len); + code = locking(fileno(fh), lkmode, len); #else - code = _locking (fileno(fh), lkmode, len); + code = _locking(fileno(fh), lkmode, len); #endif #else - struct flock f; - switch (*mode) { - case 'w': f.l_type = F_WRLCK; break; - case 'r': f.l_type = F_RDLCK; break; - case 'u': f.l_type = F_UNLCK; break; - default : return luaL_error (L, "%s: invalid mode", funcname); - } - f.l_whence = SEEK_SET; - f.l_start = (off_t)start; - f.l_len = (off_t)len; - code = fcntl (fileno(fh), F_SETLK, &f); + struct flock f; + switch (*mode) { + case 'w': + f.l_type = F_WRLCK; + break; + case 'r': + f.l_type = F_RDLCK; + break; + case 'u': + f.l_type = F_UNLCK; + break; + default: + return luaL_error(L, "%s: invalid mode", funcname); + } + f.l_whence = SEEK_SET; + f.l_start = (off_t) start; + f.l_len = (off_t) len; + code = fcntl(fileno(fh), F_SETLK, &f); #endif - return (code != -1); + return (code != -1); } #ifdef _WIN32 typedef struct lfs_Lock { HANDLE fd; } lfs_Lock; -static int lfs_lock_dir(lua_State *L) { - size_t pathl; HANDLE fd; +static int lfs_lock_dir(lua_State * L) +{ + size_t pathl; + HANDLE fd; lfs_Lock *lock; char *ln; const char *lockfile = "/lockfile.lfs"; const char *path = luaL_checklstring(L, 1, &pathl); - ln = (char*)malloc(pathl + strlen(lockfile) + 1); - if(!ln) { - lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2; - } - strcpy(ln, path); strcat(ln, lockfile); - if((fd = CreateFile(ln, GENERIC_WRITE, 0, NULL, CREATE_NEW, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL)) == INVALID_HANDLE_VALUE) { - int en = GetLastError(); - free(ln); lua_pushnil(L); - if(en == ERROR_FILE_EXISTS || en == ERROR_SHARING_VIOLATION) - lua_pushstring(L, "File exists"); - else - lua_pushstring(L, strerror(en)); - return 2; + ln = (char *) malloc(pathl + strlen(lockfile) + 1); + if (!ln) { + lua_pushnil(L); + lua_pushstring(L, strerror(errno)); + return 2; } + strcpy(ln, path); + strcat(ln, lockfile); + fd = CreateFile(ln, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL); free(ln); - lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock)); + if (fd == INVALID_HANDLE_VALUE) { + return lfs_win32_pusherror(L); + } + lock = (lfs_Lock *) lua_newuserdata(L, sizeof(lfs_Lock)); lock->fd = fd; - luaL_getmetatable (L, LOCK_METATABLE); - lua_setmetatable (L, -2); + luaL_getmetatable(L, LOCK_METATABLE); + lua_setmetatable(L, -2); return 1; } -static int lfs_unlock_dir(lua_State *L) { - lfs_Lock *lock = (lfs_Lock *)luaL_checkudata(L, 1, LOCK_METATABLE); - if(lock->fd != INVALID_HANDLE_VALUE) { + +static int lfs_unlock_dir(lua_State * L) +{ + lfs_Lock *lock = (lfs_Lock *) luaL_checkudata(L, 1, LOCK_METATABLE); + if (lock->fd != INVALID_HANDLE_VALUE) { CloseHandle(lock->fd); - lock->fd=INVALID_HANDLE_VALUE; + lock->fd = INVALID_HANDLE_VALUE; } return 0; } @@ -326,30 +449,38 @@ static int lfs_unlock_dir(lua_State *L) { typedef struct lfs_Lock { char *ln; } lfs_Lock; -static int lfs_lock_dir(lua_State *L) { +static int lfs_lock_dir(lua_State * L) +{ lfs_Lock *lock; size_t pathl; char *ln; const char *lockfile = "/lockfile.lfs"; const char *path = luaL_checklstring(L, 1, &pathl); - lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock)); - ln = (char*)malloc(pathl + strlen(lockfile) + 1); - if(!ln) { - lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2; + lock = (lfs_Lock *) lua_newuserdata(L, sizeof(lfs_Lock)); + ln = (char *) malloc(pathl + strlen(lockfile) + 1); + if (!ln) { + lua_pushnil(L); + lua_pushstring(L, strerror(errno)); + return 2; } - strcpy(ln, path); strcat(ln, lockfile); - if(symlink("lock", ln) == -1) { - free(ln); lua_pushnil(L); - lua_pushstring(L, strerror(errno)); return 2; + strcpy(ln, path); + strcat(ln, lockfile); + if (symlink("lock", ln) == -1) { + free(ln); + lua_pushnil(L); + lua_pushstring(L, strerror(errno)); + return 2; } lock->ln = ln; - luaL_getmetatable (L, LOCK_METATABLE); - lua_setmetatable (L, -2); + luaL_getmetatable(L, LOCK_METATABLE); + lua_setmetatable(L, -2); return 1; } -static int lfs_unlock_dir(lua_State *L) { - lfs_Lock *lock = (lfs_Lock *)luaL_checkudata(L, 1, LOCK_METATABLE); - if(lock->ln) { + +static int lfs_unlock_dir(lua_State * L) +{ + lfs_Lock *lock = (lfs_Lock *) luaL_checkudata(L, 1, LOCK_METATABLE); + if (lock->ln) { unlink(lock->ln); free(lock->ln); lock->ln = NULL; @@ -358,9 +489,10 @@ static int lfs_unlock_dir(lua_State *L) { } #endif -static int lfs_g_setmode (lua_State *L, FILE *f, int arg) { - static const int mode[] = {_O_BINARY, _O_TEXT}; - static const char *const modenames[] = {"binary", "text", NULL}; +static int lfs_g_setmode(lua_State * L, FILE * f, int arg) +{ + static const int mode[] = { _O_BINARY, _O_TEXT }; + static const char *const modenames[] = { "binary", "text", NULL }; int op = luaL_checkoption(L, arg, NULL, modenames); int res = lfs_setmode(f, mode[op]); if (res != -1) { @@ -379,7 +511,8 @@ static int lfs_g_setmode (lua_State *L, FILE *f, int arg) { } } -static int lfs_f_setmode(lua_State *L) { +static int lfs_f_setmode(lua_State * L) +{ return lfs_g_setmode(L, check_file(L, 1, "setmode"), 2); } @@ -390,19 +523,20 @@ static int lfs_f_setmode(lua_State *L) { ** @param #3 Number with start position (optional). ** @param #4 Number with length (optional). */ -static int file_lock (lua_State *L) { - FILE *fh = check_file (L, 1, "lock"); - const char *mode = luaL_checkstring (L, 2); - const long start = (long) luaL_optinteger (L, 3, 0); - long len = (long) luaL_optinteger (L, 4, 0); - if (_file_lock (L, fh, mode, start, len, "lock")) { - lua_pushboolean (L, 1); - return 1; - } else { - lua_pushnil (L); - lua_pushfstring (L, "%s", strerror(errno)); - return 2; - } +static int file_lock(lua_State * L) +{ + FILE *fh = check_file(L, 1, "lock"); + const char *mode = luaL_checkstring(L, 2); + const long start = (long) luaL_optinteger(L, 3, 0); + long len = (long) luaL_optinteger(L, 4, 0); + if (_file_lock(L, fh, mode, start, len, "lock")) { + lua_pushboolean(L, 1); + return 1; + } else { + lua_pushnil(L); + lua_pushfstring(L, "%s", strerror(errno)); + return 2; + } } @@ -412,18 +546,19 @@ static int file_lock (lua_State *L) { ** @param #2 Number with start position (optional). ** @param #3 Number with length (optional). */ -static int file_unlock (lua_State *L) { - FILE *fh = check_file (L, 1, "unlock"); - const long start = (long) luaL_optinteger (L, 2, 0); - long len = (long) luaL_optinteger (L, 3, 0); - if (_file_lock (L, fh, "u", start, len, "unlock")) { - lua_pushboolean (L, 1); - return 1; - } else { - lua_pushnil (L); - lua_pushfstring (L, "%s", strerror(errno)); - return 2; - } +static int file_unlock(lua_State * L) +{ + FILE *fh = check_file(L, 1, "unlock"); + const long start = (long) luaL_optinteger(L, 2, 0); + long len = (long) luaL_optinteger(L, 3, 0); + if (_file_lock(L, fh, "u", start, len, "unlock")) { + lua_pushboolean(L, 1); + return 1; + } else { + lua_pushnil(L); + lua_pushfstring(L, "%s", strerror(errno)); + return 2; + } } @@ -433,20 +568,40 @@ static int file_unlock (lua_State *L) { ** @param #2 Name of link. ** @param #3 True if link is symbolic (optional). */ -static int make_link (lua_State *L) { -#ifndef _WIN32 +static int make_link(lua_State * L) +{ const char *oldpath = luaL_checkstring(L, 1); const char *newpath = luaL_checkstring(L, 2); - int res = (lua_toboolean(L,3) ? symlink : link)(oldpath, newpath); - if (res == -1) { - return pusherror(L, NULL); +#ifndef _WIN32 + return pushresult(L, + (lua_toboolean(L, 3) ? symlink : link) (oldpath, + newpath), + NULL); +#else + int symbolic = lua_toboolean(L, 3); + STAT_STRUCT oldpathinfo; + int is_dir = 0; + if (STAT_FUNC(oldpath, &oldpathinfo) == 0) { + is_dir = S_ISDIR(oldpathinfo.st_mode) != 0; + } + if (!symbolic && is_dir) { + lua_pushnil(L); + lua_pushstring(L, + "hard links to directories are not supported on Windows"); + return 2; + } + + int result = symbolic ? CreateSymbolicLink(newpath, oldpath, is_dir) + : CreateHardLink(newpath, oldpath, NULL); + + if (result) { + return pushresult(L, result, NULL); } else { - lua_pushinteger(L, 0); - return 1; + lua_pushnil(L); + lua_pushstring(L, symbolic ? "make_link CreateSymbolicLink() failed" + : "make_link CreateHardLink() failed"); + return 2; } -#else - errno = ENOSYS; /* = "Function not implemented" */ - return pushresult(L, -1, "make_link is not supported on Windows"); #endif } @@ -455,7 +610,8 @@ static int make_link (lua_State *L) { ** Creates a directory. ** @param #1 Directory path. */ -static int make_dir (lua_State *L) { +static int make_dir(lua_State * L) +{ const char *path = luaL_checkstring(L, 1); return pushresult(L, lfs_mkdir(path), NULL); } @@ -465,7 +621,8 @@ static int make_dir (lua_State *L) { ** Removes a directory. ** @param #1 Directory path. */ -static int remove_dir (lua_State *L) { +static int remove_dir(lua_State * L) +{ const char *path = luaL_checkstring(L, 1); return pushresult(L, rmdir(path), NULL); } @@ -474,46 +631,47 @@ static int remove_dir (lua_State *L) { /* ** Directory iterator */ -static int dir_iter (lua_State *L) { +static int dir_iter(lua_State * L) +{ #ifdef _WIN32 - struct _finddata_t c_file; + struct _finddata_t c_file; #else - struct dirent *entry; + struct dirent *entry; #endif - dir_data *d = (dir_data *)luaL_checkudata (L, 1, DIR_METATABLE); - luaL_argcheck (L, d->closed == 0, 1, "closed directory"); + dir_data *d = (dir_data *) luaL_checkudata(L, 1, DIR_METATABLE); + luaL_argcheck(L, d->closed == 0, 1, "closed directory"); #ifdef _WIN32 - if (d->hFile == 0L) { /* first entry */ - if ((d->hFile = _findfirst (d->pattern, &c_file)) == -1L) { - lua_pushnil (L); - lua_pushstring (L, strerror (errno)); - d->closed = 1; - return 2; - } else { - lua_pushstring (L, c_file.name); - return 1; - } - } else { /* next entry */ - if (_findnext (d->hFile, &c_file) == -1L) { - /* no more entries => close directory */ - _findclose (d->hFile); - d->closed = 1; - return 0; - } else { - lua_pushstring (L, c_file.name); - return 1; - } - } + if (d->hFile == 0L) { /* first entry */ + if ((d->hFile = _findfirst(d->pattern, &c_file)) == -1L) { + lua_pushnil(L); + lua_pushstring(L, strerror(errno)); + d->closed = 1; + return 2; + } else { + lua_pushstring(L, c_file.name); + return 1; + } + } else { /* next entry */ + if (_findnext(d->hFile, &c_file) == -1L) { + /* no more entries => close directory */ + _findclose(d->hFile); + d->closed = 1; + return 0; + } else { + lua_pushstring(L, c_file.name); + return 1; + } + } #else - if ((entry = readdir (d->dir)) != NULL) { - lua_pushstring (L, entry->d_name); - return 1; - } else { - /* no more entries => close directory */ - closedir (d->dir); - d->closed = 1; - return 0; - } + if ((entry = readdir(d->dir)) != NULL) { + lua_pushstring(L, entry->d_name); + return 1; + } else { + /* no more entries => close directory */ + closedir(d->dir); + d->closed = 1; + return 0; + } #endif } @@ -521,135 +679,129 @@ static int dir_iter (lua_State *L) { /* ** Closes directory iterators */ -static int dir_close (lua_State *L) { - dir_data *d = (dir_data *)lua_touserdata (L, 1); +static int dir_close(lua_State * L) +{ + dir_data *d = (dir_data *) lua_touserdata(L, 1); #ifdef _WIN32 - if (!d->closed && d->hFile) { - _findclose (d->hFile); - } + if (!d->closed && d->hFile) { + _findclose(d->hFile); + } #else - if (!d->closed && d->dir) { - closedir (d->dir); - } + if (!d->closed && d->dir) { + closedir(d->dir); + } #endif - d->closed = 1; - return 0; + d->closed = 1; + return 0; } /* ** Factory of directory iterators */ -static int dir_iter_factory (lua_State *L) { - const char *path = luaL_checkstring (L, 1); - dir_data *d; - lua_pushcfunction (L, dir_iter); - d = (dir_data *) lua_newuserdata (L, sizeof(dir_data)); - luaL_getmetatable (L, DIR_METATABLE); - lua_setmetatable (L, -2); - d->closed = 0; +static int dir_iter_factory(lua_State * L) +{ + const char *path = luaL_checkstring(L, 1); + dir_data *d; + lua_pushcfunction(L, dir_iter); + d = (dir_data *) lua_newuserdata(L, sizeof(dir_data)); + luaL_getmetatable(L, DIR_METATABLE); + lua_setmetatable(L, -2); + d->closed = 0; #ifdef _WIN32 - d->hFile = 0L; - if (strlen(path) > MAX_PATH-2) - luaL_error (L, "path too long: %s", path); - else - sprintf (d->pattern, "%s/*", path); + d->hFile = 0L; + if (strlen(path) > MAX_PATH - 2) + luaL_error(L, "path too long: %s", path); + else + sprintf(d->pattern, "%s/*", path); #else - d->dir = opendir (path); - if (d->dir == NULL) - luaL_error (L, "cannot open %s: %s", path, strerror (errno)); + d->dir = opendir(path); + if (d->dir == NULL) + luaL_error(L, "cannot open %s: %s", path, strerror(errno)); +#endif +#if LUA_VERSION_NUM >= 504 + lua_pushnil(L); + lua_pushvalue(L, -2); + return 4; +#else + return 2; #endif - return 2; } /* ** Creates directory metatable. */ -static int dir_create_meta (lua_State *L) { - luaL_newmetatable (L, DIR_METATABLE); - - /* Method table */ - lua_newtable(L); - lua_pushcfunction (L, dir_iter); - lua_setfield(L, -2, "next"); - lua_pushcfunction (L, dir_close); - lua_setfield(L, -2, "close"); - - /* Metamethods */ - lua_setfield(L, -2, "__index"); - lua_pushcfunction (L, dir_close); - lua_setfield (L, -2, "__gc"); - return 1; +static int dir_create_meta(lua_State * L) +{ + luaL_newmetatable(L, DIR_METATABLE); + + /* Method table */ + lua_newtable(L); + lua_pushcfunction(L, dir_iter); + lua_setfield(L, -2, "next"); + lua_pushcfunction(L, dir_close); + lua_setfield(L, -2, "close"); + + /* Metamethods */ + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, dir_close); + lua_setfield(L, -2, "__gc"); + +#if LUA_VERSION_NUM >= 504 + lua_pushcfunction(L, dir_close); + lua_setfield(L, -2, "__close"); +#endif + return 1; } /* ** Creates lock metatable. */ -static int lock_create_meta (lua_State *L) { - luaL_newmetatable (L, LOCK_METATABLE); - - /* Method table */ - lua_newtable(L); - lua_pushcfunction(L, lfs_unlock_dir); - lua_setfield(L, -2, "free"); - - /* Metamethods */ - lua_setfield(L, -2, "__index"); - lua_pushcfunction(L, lfs_unlock_dir); - lua_setfield(L, -2, "__gc"); - return 1; +static int lock_create_meta(lua_State * L) +{ + luaL_newmetatable(L, LOCK_METATABLE); + + /* Method table */ + lua_newtable(L); + lua_pushcfunction(L, lfs_unlock_dir); + lua_setfield(L, -2, "free"); + + /* Metamethods */ + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, lfs_unlock_dir); + lua_setfield(L, -2, "__gc"); + return 1; } -#ifdef _WIN32 - #ifndef S_ISDIR - #define S_ISDIR(mode) (mode&_S_IFDIR) - #endif - #ifndef S_ISREG - #define S_ISREG(mode) (mode&_S_IFREG) - #endif - #ifndef S_ISLNK - #define S_ISLNK(mode) (0) - #endif - #ifndef S_ISSOCK - #define S_ISSOCK(mode) (0) - #endif - #ifndef S_ISFIFO - #define S_ISFIFO(mode) (0) - #endif - #ifndef S_ISCHR - #define S_ISCHR(mode) (mode&_S_IFCHR) - #endif - #ifndef S_ISBLK - #define S_ISBLK(mode) (0) - #endif -#endif /* ** Convert the inode protection mode to a string. */ #ifdef _WIN32 -static const char *mode2string (unsigned short mode) { +static const char *mode2string(unsigned short mode) +{ #else -static const char *mode2string (mode_t mode) { +static const char *mode2string(mode_t mode) +{ #endif - if ( S_ISREG(mode) ) + if (S_ISREG(mode)) return "file"; - else if ( S_ISDIR(mode) ) + else if (S_ISDIR(mode)) return "directory"; - else if ( S_ISLNK(mode) ) - return "link"; - else if ( S_ISSOCK(mode) ) + else if (S_ISLNK(mode)) + return "link"; + else if (S_ISSOCK(mode)) return "socket"; - else if ( S_ISFIFO(mode) ) - return "named pipe"; - else if ( S_ISCHR(mode) ) - return "char device"; - else if ( S_ISBLK(mode) ) - return "block device"; + else if (S_ISFIFO(mode)) + return "named pipe"; + else if (S_ISCHR(mode)) + return "char device"; + else if (S_ISBLK(mode)) + return "block device"; else - return "other"; + return "other"; } @@ -659,11 +811,12 @@ static const char *mode2string (mode_t mode) { ** @param #2 Access time in seconds, current time is used if missing. ** @param #3 Modification time in seconds, access time is used if missing. */ -static int file_utime (lua_State *L) { +static int file_utime(lua_State * L) +{ const char *file = luaL_checkstring(L, 1); struct utimbuf utb, *buf; - if (lua_gettop (L) == 1) /* set to current date/time */ + if (lua_gettop(L) == 1) /* set to current date/time */ buf = NULL; else { utb.actime = (time_t) luaL_optnumber(L, 2, 0); @@ -676,173 +829,225 @@ static int file_utime (lua_State *L) { /* inode protection mode */ -static void push_st_mode (lua_State *L, STAT_STRUCT *info) { - lua_pushstring (L, mode2string (info->st_mode)); +static void push_st_mode(lua_State * L, STAT_STRUCT * info) +{ + lua_pushstring(L, mode2string(info->st_mode)); } + /* device inode resides on */ -static void push_st_dev (lua_State *L, STAT_STRUCT *info) { - lua_pushinteger (L, (lua_Integer) info->st_dev); +static void push_st_dev(lua_State * L, STAT_STRUCT * info) +{ + lua_pushinteger(L, (lua_Integer) info->st_dev); } + /* inode's number */ -static void push_st_ino (lua_State *L, STAT_STRUCT *info) { - lua_pushinteger (L, (lua_Integer) info->st_ino); +static void push_st_ino(lua_State * L, STAT_STRUCT * info) +{ + lua_pushinteger(L, (lua_Integer) info->st_ino); } + /* number of hard links to the file */ -static void push_st_nlink (lua_State *L, STAT_STRUCT *info) { - lua_pushinteger (L, (lua_Integer)info->st_nlink); +static void push_st_nlink(lua_State * L, STAT_STRUCT * info) +{ + lua_pushinteger(L, (lua_Integer) info->st_nlink); } + /* user-id of owner */ -static void push_st_uid (lua_State *L, STAT_STRUCT *info) { - lua_pushinteger (L, (lua_Integer)info->st_uid); +static void push_st_uid(lua_State * L, STAT_STRUCT * info) +{ + lua_pushinteger(L, (lua_Integer) info->st_uid); } + /* group-id of owner */ -static void push_st_gid (lua_State *L, STAT_STRUCT *info) { - lua_pushinteger (L, (lua_Integer)info->st_gid); +static void push_st_gid(lua_State * L, STAT_STRUCT * info) +{ + lua_pushinteger(L, (lua_Integer) info->st_gid); } + /* device type, for special file inode */ -static void push_st_rdev (lua_State *L, STAT_STRUCT *info) { - lua_pushinteger (L, (lua_Integer) info->st_rdev); +static void push_st_rdev(lua_State * L, STAT_STRUCT * info) +{ + lua_pushinteger(L, (lua_Integer) info->st_rdev); } + /* time of last access */ -static void push_st_atime (lua_State *L, STAT_STRUCT *info) { - lua_pushinteger (L, (lua_Integer) info->st_atime); +static void push_st_atime(lua_State * L, STAT_STRUCT * info) +{ + lua_pushinteger(L, (lua_Integer) info->st_atime); } + /* time of last data modification */ -static void push_st_mtime (lua_State *L, STAT_STRUCT *info) { - lua_pushinteger (L, (lua_Integer) info->st_mtime); +static void push_st_mtime(lua_State * L, STAT_STRUCT * info) +{ + lua_pushinteger(L, (lua_Integer) info->st_mtime); } + /* time of last file status change */ -static void push_st_ctime (lua_State *L, STAT_STRUCT *info) { - lua_pushinteger (L, (lua_Integer) info->st_ctime); +static void push_st_ctime(lua_State * L, STAT_STRUCT * info) +{ + lua_pushinteger(L, (lua_Integer) info->st_ctime); } + /* file size, in bytes */ -static void push_st_size (lua_State *L, STAT_STRUCT *info) { - lua_pushinteger (L, (lua_Integer)info->st_size); +static void push_st_size(lua_State * L, STAT_STRUCT * info) +{ + lua_pushinteger(L, (lua_Integer) info->st_size); } + #ifndef _WIN32 /* blocks allocated for file */ -static void push_st_blocks (lua_State *L, STAT_STRUCT *info) { - lua_pushinteger (L, (lua_Integer)info->st_blocks); +static void push_st_blocks(lua_State * L, STAT_STRUCT * info) +{ + lua_pushinteger(L, (lua_Integer) info->st_blocks); } + /* optimal file system I/O blocksize */ -static void push_st_blksize (lua_State *L, STAT_STRUCT *info) { - lua_pushinteger (L, (lua_Integer)info->st_blksize); +static void push_st_blksize(lua_State * L, STAT_STRUCT * info) +{ + lua_pushinteger(L, (lua_Integer) info->st_blksize); } #endif /* -** Convert the inode protection mode to a permission list. -*/ + ** Convert the inode protection mode to a permission list. + */ #ifdef _WIN32 -static const char *perm2string (unsigned short mode) { +static const char *perm2string(unsigned short mode) +{ static char perms[10] = "---------"; int i; - for (i=0;i<9;i++) perms[i]='-'; - if (mode & _S_IREAD) - { perms[0] = 'r'; perms[3] = 'r'; perms[6] = 'r'; } - if (mode & _S_IWRITE) - { perms[1] = 'w'; perms[4] = 'w'; perms[7] = 'w'; } - if (mode & _S_IEXEC) - { perms[2] = 'x'; perms[5] = 'x'; perms[8] = 'x'; } + for (i = 0; i < 9; i++) + perms[i] = '-'; + if (mode & _S_IREAD) { + perms[0] = 'r'; + perms[3] = 'r'; + perms[6] = 'r'; + } + if (mode & _S_IWRITE) { + perms[1] = 'w'; + perms[4] = 'w'; + perms[7] = 'w'; + } + if (mode & _S_IEXEC) { + perms[2] = 'x'; + perms[5] = 'x'; + perms[8] = 'x'; + } return perms; } #else -static const char *perm2string (mode_t mode) { +static const char *perm2string(mode_t mode) +{ static char perms[10] = "---------"; int i; - for (i=0;i<9;i++) perms[i]='-'; - if (mode & S_IRUSR) perms[0] = 'r'; - if (mode & S_IWUSR) perms[1] = 'w'; - if (mode & S_IXUSR) perms[2] = 'x'; - if (mode & S_IRGRP) perms[3] = 'r'; - if (mode & S_IWGRP) perms[4] = 'w'; - if (mode & S_IXGRP) perms[5] = 'x'; - if (mode & S_IROTH) perms[6] = 'r'; - if (mode & S_IWOTH) perms[7] = 'w'; - if (mode & S_IXOTH) perms[8] = 'x'; + for (i = 0; i < 9; i++) + perms[i] = '-'; + if (mode & S_IRUSR) + perms[0] = 'r'; + if (mode & S_IWUSR) + perms[1] = 'w'; + if (mode & S_IXUSR) + perms[2] = 'x'; + if (mode & S_IRGRP) + perms[3] = 'r'; + if (mode & S_IWGRP) + perms[4] = 'w'; + if (mode & S_IXGRP) + perms[5] = 'x'; + if (mode & S_IROTH) + perms[6] = 'r'; + if (mode & S_IWOTH) + perms[7] = 'w'; + if (mode & S_IXOTH) + perms[8] = 'x'; return perms; } #endif /* permssions string */ -static void push_st_perm (lua_State *L, STAT_STRUCT *info) { - lua_pushstring (L, perm2string (info->st_mode)); +static void push_st_perm(lua_State * L, STAT_STRUCT * info) +{ + lua_pushstring(L, perm2string(info->st_mode)); } -typedef void (*_push_function) (lua_State *L, STAT_STRUCT *info); +typedef void (*_push_function)(lua_State * L, STAT_STRUCT * info); struct _stat_members { - const char *name; - _push_function push; + const char *name; + _push_function push; }; struct _stat_members members[] = { - { "mode", push_st_mode }, - { "dev", push_st_dev }, - { "ino", push_st_ino }, - { "nlink", push_st_nlink }, - { "uid", push_st_uid }, - { "gid", push_st_gid }, - { "rdev", push_st_rdev }, - { "access", push_st_atime }, - { "modification", push_st_mtime }, - { "change", push_st_ctime }, - { "size", push_st_size }, - { "permissions", push_st_perm }, + { "mode", push_st_mode }, + { "dev", push_st_dev }, + { "ino", push_st_ino }, + { "nlink", push_st_nlink }, + { "uid", push_st_uid }, + { "gid", push_st_gid }, + { "rdev", push_st_rdev }, + { "access", push_st_atime }, + { "modification", push_st_mtime }, + { "change", push_st_ctime }, + { "size", push_st_size }, + { "permissions", push_st_perm }, #ifndef _WIN32 - { "blocks", push_st_blocks }, - { "blksize", push_st_blksize }, + { "blocks", push_st_blocks }, + { "blksize", push_st_blksize }, #endif - { NULL, NULL } + { NULL, NULL } }; /* ** Get file or symbolic link information */ -static int _file_info_ (lua_State *L, int (*st)(const char*, STAT_STRUCT*)) { - STAT_STRUCT info; - const char *file = luaL_checkstring (L, 1); - int i; - - if (st(file, &info)) { - lua_pushnil(L); - lua_pushfstring(L, "cannot obtain information from file '%s': %s", file, strerror(errno)); - lua_pushinteger(L, errno); - return 3; - } - if (lua_isstring (L, 2)) { - const char *member = lua_tostring (L, 2); - for (i = 0; members[i].name; i++) { - if (strcmp(members[i].name, member) == 0) { - /* push member value and return */ - members[i].push (L, &info); - return 1; - } - } - /* member not found */ - return luaL_error(L, "invalid attribute name '%s'", member); - } - /* creates a table if none is given, removes extra arguments */ - lua_settop(L, 2); - if (!lua_istable (L, 2)) { - lua_newtable (L); - } - /* stores all members in table on top of the stack */ - for (i = 0; members[i].name; i++) { - lua_pushstring (L, members[i].name); - members[i].push (L, &info); - lua_rawset (L, -3); - } +static int _file_info_(lua_State * L, + int (*st)(const char *, STAT_STRUCT *)) +{ + STAT_STRUCT info; + const char *file = luaL_checkstring(L, 1); + int i; + + if (st(file, &info)) { + lua_pushnil(L); + lua_pushfstring(L, "cannot obtain information from file '%s': %s", + file, strerror(errno)); + lua_pushinteger(L, errno); + return 3; + } + if (lua_isstring(L, 2)) { + const char *member = lua_tostring(L, 2); + for (i = 0; members[i].name; i++) { + if (strcmp(members[i].name, member) == 0) { + /* push member value and return */ + members[i].push(L, &info); return 1; + } + } + /* member not found */ + return luaL_error(L, "invalid attribute name '%s'", member); + } + /* creates a table if none is given, removes extra arguments */ + lua_settop(L, 2); + if (!lua_istable(L, 2)) { + lua_newtable(L); + } + /* stores all members in table on top of the stack */ + for (i = 0; members[i].name; i++) { + lua_pushstring(L, members[i].name); + members[i].push(L, &info); + lua_rawset(L, -3); + } + return 1; } /* ** Get file information using stat. */ -static int file_info (lua_State *L) { - return _file_info_ (L, STAT_FUNC); +static int file_info(lua_State * L) +{ + return _file_info_(L, STAT_FUNC); } @@ -852,55 +1057,75 @@ static int file_info (lua_State *L) { ** Returns 1 if successful (with the target on top of the stack), ** 0 on failure (with stack unchanged, and errno set). */ -static int push_link_target(lua_State *L) { +static int push_link_target(lua_State * L) +{ + const char *file = luaL_checkstring(L, 1); +#ifdef _WIN32 + HANDLE h = CreateFile(file, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) { + return lfs_win32_pusherror(L); + } +#endif + char *target = NULL; + int tsize, size = 256; /* size = initial buffer capacity */ + int ok = 0; + while (!ok) { + char *target2 = realloc(target, size); + if (!target2) { /* failed to allocate */ + break; + } + target = target2; #ifdef _WIN32 - errno = ENOSYS; - return 0; + tsize = GetFinalPathNameByHandle(h, target, size, FILE_NAME_OPENED); #else - const char *file = luaL_checkstring(L, 1); - char *target = NULL; - int tsize, size = 256; /* size = initial buffer capacity */ - while (1) { - char* target2 = realloc(target, size); - if (!target2) { /* failed to allocate */ - free(target); - return 0; - } - target = target2; - tsize = readlink(file, target, size); - if (tsize < 0) { /* a readlink() error occurred */ - free(target); - return 0; - } - if (tsize < size) - break; - /* possibly truncated readlink() result, double size and retry */ - size *= 2; - } - target[tsize] = '\0'; - lua_pushlstring(L, target, tsize); - free(target); - return 1; + tsize = readlink(file, target, size); #endif + if (tsize < 0) { /* a readlink() error occurred */ + break; + } + if (tsize < size) { +#ifdef _WIN32 + if (tsize > 4 && strncmp(target, "\\\\?\\", 4) == 0) { + memmove_s(target, tsize - 3, target + 4, tsize - 3); + tsize -= 4; + } +#endif + ok = 1; + break; + } + /* possibly truncated readlink() result, double size and retry */ + size *= 2; + } + if (ok) { + target[tsize] = '\0'; + lua_pushlstring(L, target, tsize); + } +#ifdef _WIN32 + CloseHandle(h); +#endif + free(target); + return ok; } /* ** Get symbolic link information using lstat. */ static int link_info (lua_State *L) { - int ret; - if (lua_isstring (L, 2) && (strcmp(lua_tostring(L, 2), "target") == 0)) { - int ok = push_link_target(L); - return ok ? 1 : pusherror(L, "could not obtain link target"); - } - ret = _file_info_ (L, LSTAT_FUNC); - if (ret == 1 && lua_type(L, -1) == LUA_TTABLE) { - int ok = push_link_target(L); - if (ok) { - lua_setfield(L, -2, "target"); - } - } - return ret; + int ret; + if (lua_isstring(L, 2) && (strcmp(lua_tostring(L, 2), "target") == 0)) { + int ok = push_link_target(L); + return ok ? 1 : pusherror(L, "could not obtain link target"); + } + ret = _file_info_(L, LSTAT_FUNC); + if (ret == 1 && lua_type(L, -1) == LUA_TTABLE) { + int ok = push_link_target(L); + if (ok) { + lua_setfield(L, -2, "target"); + } + } + return ret; } @@ -908,7 +1133,7 @@ static int link_info (lua_State *L) { ** Assumes the table is on top of the stack. */ static void set_info (lua_State *L) { - lua_pushliteral(L, "Copyright (C) 2003-2017 Kepler Project"); + lua_pushliteral(L, "Copyright (C) 2003-2020 Kepler Project"); lua_setfield(L, -2, "_COPYRIGHT"); lua_pushliteral(L, "LuaFileSystem is a Lua library developed to complement the set of functions related to file systems offered by the standard Lua distribution"); lua_setfield(L, -2, "_DESCRIPTION"); diff --git a/luaclib/src/lfs/lfs.h b/luaclib/src/lfs/lfs.h index 45875640..fe8f0a45 100644 --- a/luaclib/src/lfs/lfs.h +++ b/luaclib/src/lfs/lfs.h @@ -1,6 +1,7 @@ /* ** LuaFileSystem -** Copyright Kepler Project 2003 - 2017 (http://keplerproject.github.io/luafilesystem) +** Copyright Kepler Project 2003 - 2020 +** (http://keplerproject.github.io/luafilesystem) */ /* Define 'chdir' for systems that do not implement it */ @@ -27,8 +28,8 @@ extern "C" { #endif -LFS_EXPORT int luaopen_lfs (lua_State *L); +LFS_EXPORT int luaopen_lfs(lua_State * L); #ifdef __cplusplus } -#endif +#endif \ No newline at end of file From c7e934ba4bfc635134aedf8420581493628bee57 Mon Sep 17 00:00:00 2001 From: Nuctori <1018570975@qq.com> Date: Fri, 25 Mar 2022 01:30:22 +0800 Subject: [PATCH 890/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=B7=A8=E5=9F=9F?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E5=A4=B1=E6=95=88=E9=97=AE=E9=A2=98,?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 78 ++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index 4e7f51cd..8f551444 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -162,9 +162,16 @@ local function cros_append(header, timeout) insert(header, 'Access-Control-Max-Age: ' .. (timeout or 86400)) end +local AllowMethod = { + GET = true, + POST = true, + DELETE = true, + PUT = true, + PATCH = true, +} + local function PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEADER) - local content = new_tab(0, 16) - local body_len = toint(HEADER['Content-Length']) or toint(HEADER['Content-length']) or toint(HEADER['content-length']) + local body_len = toint(HEADER['Content-Length'] or HEADER['content-length']) local BODY if body_len and body_len > 0 then if body_len >= (max_body_size or (1024 * 1024)) then @@ -190,44 +197,49 @@ local function PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEA local URL_ENCODE = 'application/x-www-form-urlencoded' local format = match(HEADER['Content-Type'] or HEADER['content-type'] or '', '(.-/[^;]*)') - if format == JSON_ENCODE then - content['json'] = true - elseif format == XML_ENCODE_1 or format == XML_ENCODE_2 then - content['xml'] = true - elseif format == FILE_ENCODE then - if format == FILE_ENCODE then - local BOUNDARY = match(HEADER['Content-Type'] or HEADER['content-type'] or '', '^.+=[%-]*(.+)') - if BOUNDARY and BOUNDARY ~= '' then - local files, formargs = form_multipart(BODY, BOUNDARY) - if files then - content['files'] = files - end - if formargs then - content['formargs'] = {} - for _, args in ipairs(formargs) do - content['formargs'][args[1]] = args[2] + if AllowMethod[string.upper(METHOD)] then + local content = new_tab(0, 16) + if format == JSON_ENCODE then + content['json'] = true + elseif format == XML_ENCODE_1 or format == XML_ENCODE_2 then + content['xml'] = true + elseif format == FILE_ENCODE then + if format == FILE_ENCODE then + local BOUNDARY = match(HEADER['Content-Type'] or HEADER['content-type'] or '', '^.+=[%-]*(.+)') + if BOUNDARY and BOUNDARY ~= '' then + local files, formargs = form_multipart(BODY, BOUNDARY) + if files then + content['files'] = files + end + if formargs then + content['formargs'] = {} + for _, args in ipairs(formargs) do + content['formargs'][args[1]] = args[2] + end end end end end - end - local spl_pos = find(PATH, '%?') - local queryParams = form_argsencode(PATH) - if spl_pos and spl_pos < #PATH then - content['query'] = queryParams - end - if METHOD == "GET" or METHOD == "DELETE" then - content['args'] = queryParams - elseif METHOD == "POST" then - if format == FILE_ENCODE then - content['args'] = content['formargs'] - elseif format == URL_ENCODE then - content['args'] = form_urlencode(BODY) + local spl_pos = find(PATH, '%?') + local queryParams = form_argsencode(PATH) + if spl_pos and spl_pos < #PATH then + content['query'] = queryParams end + if METHOD == "GET" or METHOD == "DELETE" then + content['args'] = queryParams + elseif METHOD == "POST" then + if format == FILE_ENCODE then + content['args'] = content['formargs'] + elseif format == URL_ENCODE then + content['args'] = form_urlencode(BODY) + end + end + content['body'] = BODY + return true, content + else + return true end - content['body'] = BODY - return true, content end local function X_Forwarded_FORMAT(ip_list) From 6427a8b6b3b789a904f447ff7e7136996424e13f Mon Sep 17 00:00:00 2001 From: Nuctori <1018570975@qq.com> Date: Sun, 10 Apr 2022 16:42:48 +0800 Subject: [PATCH 891/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/protocol/http/init.lua | 72 +++++++++++++++++------------------ 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/lualib/protocol/http/init.lua b/lualib/protocol/http/init.lua index 8f551444..617313dd 100644 --- a/lualib/protocol/http/init.lua +++ b/lualib/protocol/http/init.lua @@ -49,6 +49,7 @@ local fmt = string.format local toint = math.tointeger local find = string.find local split = string.sub +local upper = string.upper local DATE = os.date local time = os.time @@ -196,50 +197,49 @@ local function PASER_METHOD(http, sock, max_body_size, buffer, METHOD, PATH, HEA local JSON_ENCODE = 'application/json' local URL_ENCODE = 'application/x-www-form-urlencoded' local format = match(HEADER['Content-Type'] or HEADER['content-type'] or '', '(.-/[^;]*)') + if not AllowMethod[upper(METHOD)] then + return true + end - if AllowMethod[string.upper(METHOD)] then - local content = new_tab(0, 16) - if format == JSON_ENCODE then - content['json'] = true - elseif format == XML_ENCODE_1 or format == XML_ENCODE_2 then - content['xml'] = true - elseif format == FILE_ENCODE then - if format == FILE_ENCODE then - local BOUNDARY = match(HEADER['Content-Type'] or HEADER['content-type'] or '', '^.+=[%-]*(.+)') - if BOUNDARY and BOUNDARY ~= '' then - local files, formargs = form_multipart(BODY, BOUNDARY) - if files then - content['files'] = files - end - if formargs then - content['formargs'] = {} - for _, args in ipairs(formargs) do - content['formargs'][args[1]] = args[2] - end + local content = new_tab(0, 16) + if format == JSON_ENCODE then + content['json'] = true + elseif format == XML_ENCODE_1 or format == XML_ENCODE_2 then + content['xml'] = true + elseif format == FILE_ENCODE then + if format == FILE_ENCODE then + local BOUNDARY = match(HEADER['Content-Type'] or HEADER['content-type'] or '', '^.+=[%-]*(.+)') + if BOUNDARY and BOUNDARY ~= '' then + local files, formargs = form_multipart(BODY, BOUNDARY) + if files then + content['files'] = files + end + if formargs then + content['formargs'] = {} + for _, args in ipairs(formargs) do + content['formargs'][args[1]] = args[2] end end end end + end - local spl_pos = find(PATH, '%?') - local queryParams = form_argsencode(PATH) - if spl_pos and spl_pos < #PATH then - content['query'] = queryParams - end - if METHOD == "GET" or METHOD == "DELETE" then - content['args'] = queryParams - elseif METHOD == "POST" then - if format == FILE_ENCODE then - content['args'] = content['formargs'] - elseif format == URL_ENCODE then - content['args'] = form_urlencode(BODY) - end + local spl_pos = find(PATH, '%?') + local queryParams = form_argsencode(PATH) + if spl_pos and spl_pos < #PATH then + content['query'] = queryParams + end + if METHOD == "GET" or METHOD == "DELETE" then + content['args'] = queryParams + elseif METHOD == "POST" then + if format == FILE_ENCODE then + content['args'] = content['formargs'] + elseif format == URL_ENCODE then + content['args'] = form_urlencode(BODY) end - content['body'] = BODY - return true, content - else - return true end + content['body'] = BODY + return true, content end local function X_Forwarded_FORMAT(ip_list) From f9b2a833f06d87e524e29066a31e4e3b7f2d0fe9 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Sat, 28 May 2022 02:01:00 +0800 Subject: [PATCH 892/956] =?UTF-8?q?=E4=B8=BAhexencode=E4=B8=8Ehexdecode?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=89=B9=E6=80=A7=E6=94=AF=E6=8C=81.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/hex.c | 122 +++++++++++++-------------------------- 1 file changed, 40 insertions(+), 82 deletions(-) diff --git a/luaclib/src/lcrypt/hex.c b/luaclib/src/lcrypt/hex.c index 24fc3731..d6c755cb 100644 --- a/luaclib/src/lcrypt/hex.c +++ b/luaclib/src/lcrypt/hex.c @@ -1,41 +1,8 @@ #include "lcrypt.h" -#define HEX_CHUNK (65535) +static char lencode[] = "0123456789abcdef"; -static const uint8_t encode[256][2] = { - {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, {'0', '6'}, {'0', '7'}, - {'0', '8'}, {'0', '9'}, {'0', 'a'}, {'0', 'b'}, {'0', 'c'}, {'0', 'd'}, {'0', 'e'}, {'0', 'f'}, - {'1', '0'}, {'1', '1'}, {'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'}, - {'1', '8'}, {'1', '9'}, {'1', 'a'}, {'1', 'b'}, {'1', 'c'}, {'1', 'd'}, {'1', 'e'}, {'1', 'f'}, - {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, {'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, - {'2', '8'}, {'2', '9'}, {'2', 'a'}, {'2', 'b'}, {'2', 'c'}, {'2', 'd'}, {'2', 'e'}, {'2', 'f'}, - {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'}, {'3', '6'}, {'3', '7'}, - {'3', '8'}, {'3', '9'}, {'3', 'a'}, {'3', 'b'}, {'3', 'c'}, {'3', 'd'}, {'3', 'e'}, {'3', 'f'}, - {'4', '0'}, {'4', '1'}, {'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'}, - {'4', '8'}, {'4', '9'}, {'4', 'a'}, {'4', 'b'}, {'4', 'c'}, {'4', 'd'}, {'4', 'e'}, {'4', 'f'}, - {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, {'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, - {'5', '8'}, {'5', '9'}, {'5', 'a'}, {'5', 'b'}, {'5', 'c'}, {'5', 'd'}, {'5', 'e'}, {'5', 'f'}, - {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'}, {'6', '6'}, {'6', '7'}, - {'6', '8'}, {'6', '9'}, {'6', 'a'}, {'6', 'b'}, {'6', 'c'}, {'6', 'd'}, {'6', 'e'}, {'6', 'f'}, - {'7', '0'}, {'7', '1'}, {'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'}, - {'7', '8'}, {'7', '9'}, {'7', 'a'}, {'7', 'b'}, {'7', 'c'}, {'7', 'd'}, {'7', 'e'}, {'7', 'f'}, - {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, {'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, - {'8', '8'}, {'8', '9'}, {'8', 'a'}, {'8', 'b'}, {'8', 'c'}, {'8', 'd'}, {'8', 'e'}, {'8', 'f'}, - {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'}, {'9', '6'}, {'9', '7'}, - {'9', '8'}, {'9', '9'}, {'9', 'a'}, {'9', 'b'}, {'9', 'c'}, {'9', 'd'}, {'9', 'e'}, {'9', 'f'}, - {'a', '0'}, {'a', '1'}, {'a', '2'}, {'a', '3'}, {'a', '4'}, {'a', '5'}, {'a', '6'}, {'a', '7'}, - {'a', '8'}, {'a', '9'}, {'a', 'a'}, {'a', 'b'}, {'a', 'c'}, {'a', 'd'}, {'a', 'e'}, {'a', 'f'}, - {'b', '0'}, {'b', '1'}, {'b', '2'}, {'b', '3'}, {'b', '4'}, {'b', '5'}, {'b', '6'}, {'b', '7'}, - {'b', '8'}, {'b', '9'}, {'b', 'a'}, {'b', 'b'}, {'b', 'c'}, {'b', 'd'}, {'b', 'e'}, {'b', 'f'}, - {'c', '0'}, {'c', '1'}, {'c', '2'}, {'c', '3'}, {'c', '4'}, {'c', '5'}, {'c', '6'}, {'c', '7'}, - {'c', '8'}, {'c', '9'}, {'c', 'a'}, {'c', 'b'}, {'c', 'c'}, {'c', 'd'}, {'c', 'e'}, {'c', 'f'}, - {'d', '0'}, {'d', '1'}, {'d', '2'}, {'d', '3'}, {'d', '4'}, {'d', '5'}, {'d', '6'}, {'d', '7'}, - {'d', '8'}, {'d', '9'}, {'d', 'a'}, {'d', 'b'}, {'d', 'c'}, {'d', 'd'}, {'d', 'e'}, {'d', 'f'}, - {'e', '0'}, {'e', '1'}, {'e', '2'}, {'e', '3'}, {'e', '4'}, {'e', '5'}, {'e', '6'}, {'e', '7'}, - {'e', '8'}, {'e', '9'}, {'e', 'a'}, {'e', 'b'}, {'e', 'c'}, {'e', 'd'}, {'e', 'e'}, {'e', 'f'}, - {'f', '0'}, {'f', '1'}, {'f', '2'}, {'f', '3'}, {'f', '4'}, {'f', '5'}, {'f', '6'}, {'f', '7'}, - {'f', '8'}, {'f', '9'}, {'f', 'a'}, {'f', 'b'}, {'f', 'c'}, {'f', 'd'}, {'f', 'e'}, {'f', 'f'}, -}; +static char hencode[] = "0123456789ABCDEF"; static const char deindex[256] = { -1, -1, -1, -1, -1, -1, -1, -1, @@ -72,47 +39,31 @@ static const char deindex[256] = { -1, -1, -1, -1, -1, -1, -1, -1, }; -static const uint8_t decode[16][16] = { - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, - { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }, - { 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47 }, - { 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 }, - { 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79 }, - { 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95 }, - { 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111 }, - { 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127 }, - { 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143 }, - { 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159 }, - { 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175 }, - { 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191 }, - { 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207 }, - { 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223 }, - { 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239 }, - { 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 }, -}; - int lfromhex(lua_State *L) { size_t tsize = 0; const uint8_t* text = (const uint8_t *)luaL_checklstring(L, 1, &tsize); - if (!text || tsize < 2 || (tsize & 0x01) == 0x01) + if (!text || tsize < 2) return luaL_error(L, "Invalid hexdecode text size %d", (int)tsize); - size_t dsize = tsize / 2; - char *buffer; - if (tsize <= HEX_CHUNK) - buffer = alloca(dsize); - else - buffer = lua_newuserdata(L, dsize); + luaL_Buffer B; + luaL_buffinit(L, &B); - size_t i, idx = 0; - int8_t hi, lo; - for (i = 0; i < tsize; i += 2) { - hi = deindex[text[i]]; lo = deindex[text[i+1]]; - if (hi == -1 || lo == -1) - return luaL_error(L, "Invalid hexdecode char pos in %d", i); - buffer[idx++] = decode[hi][lo]; + size_t idx = 0; int8_t hi; int8_t lo; + + while (idx < tsize){ + while(isspace((uint8_t)text[idx])) + idx++; + if (idx >= tsize) + break; + /* 解码计算 */ + hi = text[idx++]; hi = deindex[hi]; + lo = text[idx++]; lo = deindex[lo]; + if (lo == -1 || hi == -1) + return luaL_error(L, "Invalid hexdecode char pos in %d and %d", idx - 2, idx - 1); + /* 还原数据 */ + luaL_addchar(&B, hi << 4 | lo); } - lua_pushlstring(L, buffer, dsize); + luaL_pushresult(&B); return 1; } @@ -122,20 +73,27 @@ int ltohex(lua_State *L) { if (!text || tsize == 0) return luaL_error(L, "Invalid hexencode text size %d", (int)tsize); - size_t esize = tsize * 2; - char *buffer; - if (tsize <= HEX_CHUNK) - buffer = alloca(esize); - else - buffer = lua_newuserdata(L, esize); + /* 默认使用小写编码 */ + const char *etable = lencode; + if (lua_isboolean(L, 2) && lua_toboolean(L, 2)) + etable = hencode; + + /* 编码之间加上空格 */ + size_t n = 2; + if (lua_isboolean(L, 3) && lua_toboolean(L, 3)) + n = 3; + + luaL_Buffer B; + luaL_buffinit(L, &B); - const uint8_t *code; - size_t i, idx = 0; - for (i = 0; i < tsize; i++) { - code = encode[text[i]]; - buffer[idx++] = code[0]; - buffer[idx++] = code[1]; + uint8_t code; + for (size_t i = 0; i < tsize; i++) { + code = text[i]; + luaL_addchar(&B, etable[code >> 4]); + luaL_addchar(&B, etable[code & 0xF]); + if (n == 3) + luaL_addchar(&B, ' '); } - lua_pushlstring(L, buffer, esize); + luaL_pushresult(&B); return 1; } From 0746db339b8d6b5d6ea994e0f678b221b14395d7 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 28 May 2022 02:02:39 +0800 Subject: [PATCH 893/956] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=A4=B4=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/hex.c | 1 + 1 file changed, 1 insertion(+) diff --git a/luaclib/src/lcrypt/hex.c b/luaclib/src/lcrypt/hex.c index d6c755cb..d8e9abaf 100644 --- a/luaclib/src/lcrypt/hex.c +++ b/luaclib/src/lcrypt/hex.c @@ -1,4 +1,5 @@ #include "lcrypt.h" +#include static char lencode[] = "0123456789abcdef"; From 8c6505a7fb334880e14793ec3b21b57cfc4cb180 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 30 May 2022 10:59:45 +0800 Subject: [PATCH 894/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=BC=96=E7=A0=81?= =?UTF-8?q?=E3=80=81=E8=A7=A3=E7=A0=81=E7=9A=84=E6=9D=A1=E4=BB=B6=E5=88=A4?= =?UTF-8?q?=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/hex.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/luaclib/src/lcrypt/hex.c b/luaclib/src/lcrypt/hex.c index d8e9abaf..db4f4dfe 100644 --- a/luaclib/src/lcrypt/hex.c +++ b/luaclib/src/lcrypt/hex.c @@ -52,15 +52,17 @@ int lfromhex(lua_State *L) { size_t idx = 0; int8_t hi; int8_t lo; while (idx < tsize){ + /* 跳过空格 */ while(isspace((uint8_t)text[idx])) idx++; if (idx >= tsize) break; + if (idx + 1 == tsize) + return luaL_error(L, "Invalid hexdecode ending."); /* 解码计算 */ - hi = text[idx++]; hi = deindex[hi]; - lo = text[idx++]; lo = deindex[lo]; - if (lo == -1 || hi == -1) - return luaL_error(L, "Invalid hexdecode char pos in %d and %d", idx - 2, idx - 1); + hi = deindex[text[idx++]]; lo = deindex[text[idx++]]; + if (hi == -1 || lo == -1) + return luaL_error(L, "Invalid hexdecode char pos between %d and %d", idx - 2, idx - 1); /* 还原数据 */ luaL_addchar(&B, hi << 4 | lo); } @@ -88,11 +90,13 @@ int ltohex(lua_State *L) { luaL_buffinit(L, &B); uint8_t code; - for (size_t i = 0; i < tsize; i++) { - code = text[i]; + size_t i = 0; + while (i < tsize) + { + code = text[i++]; luaL_addchar(&B, etable[code >> 4]); luaL_addchar(&B, etable[code & 0xF]); - if (n == 3) + if (n == 3 && i < tsize) /* 编码结尾不添加空格 */ luaL_addchar(&B, ' '); } luaL_pushresult(&B); From 96439f1c4feed562829713c83b193cbfe2b9fb17 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 1 Jun 2022 11:31:24 +0800 Subject: [PATCH 895/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0-w=20auto=E5=9C=A8?= =?UTF-8?q?=E5=8D=95=E6=A0=B8=E8=AE=BE=E5=A4=87=E4=B8=8A=E7=9A=84=E8=A1=8C?= =?UTF-8?q?=E4=B8=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core_start.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core_start.c b/src/core_start.c index 1d581ce2..c06ff87d 100644 --- a/src/core_start.c +++ b/src/core_start.c @@ -95,7 +95,7 @@ static inline void cfadmin_specify_nprocess(const char* w) { nprocess = 1; /* 自动检查可用核心数量 */ if (!strcmp(w, "auto")) - nprocess = sysconf(_SC_NPROCESSORS_ONLN) < 1 ? 1 : sysconf(_SC_NPROCESSORS_ONLN); + nprocess = sysconf(_SC_NPROCESSORS_ONLN) < 2 ? 2 : sysconf(_SC_NPROCESSORS_ONLN); } /* 后台运行 */ @@ -290,8 +290,8 @@ int main(int argc, char const *argv[]) { cfadmin_init_args(argc, argv); #if defined(__MSYS__) || defined(__CYGWIN__) - /* Windows下不可使用多进程 */ - nprocess = 1; + /* Windows下不可使用多进程 */ + nprocess = 1; #else int n = -1; if (getenv("cfadmin_nprocess")) From 54584d007a5c4fe11544f06a406fe111f66d050b Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 15 Jul 2022 21:55:00 +0800 Subject: [PATCH 896/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dtable.reduce=E7=9A=84?= =?UTF-8?q?=E9=94=99=E8=AF=AF.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/utils/table.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lualib/utils/table.lua b/lualib/utils/table.lua index 03227c4d..faf3ceab 100644 --- a/lualib/utils/table.lua +++ b/lualib/utils/table.lua @@ -172,7 +172,7 @@ function table.format (tab, fmt, sep, sort) list[#list+1] = {k, v} end -- 根据key进行升序排列 - tsort(list, sorts[tonumber(sort) or 1] or sort[1]) + tsort(list, sorts[tonumber(sort) or 1] or sorts[1]) -- 开始合并数据 for idx, item in ipairs(list) do list[idx] = sformat(fmt or "%s=%s", item[1], item[2]) @@ -330,14 +330,14 @@ function table.reduce(func, list) if len == 1 then return list[1] end - return error("can't passed empty array.") + error("can't passed empty array.") end local args = {list[1], nil} for i = 2, len do args[2] = list[i] local result = func(args[1], args[2]) if type(result) ~= 'number' then - return + error("return invalid result.") end args[1] = result end @@ -370,4 +370,4 @@ function table.replace(tab) t[key] = value end return t -end \ No newline at end of file +end From 3544e6b5b30d0abcfaba8764dae045bdce4203ed Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 19 Jul 2022 23:50:08 +0800 Subject: [PATCH 897/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=BC=93=E5=86=B2?= =?UTF-8?q?=E5=8C=BA=E5=88=B7=E6=96=B0=E7=AD=96=E7=95=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/logging/init.lua | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lualib/logging/init.lua b/lualib/logging/init.lua index 63bd0a38..8acf8bd0 100644 --- a/lualib/logging/init.lua +++ b/lualib/logging/init.lua @@ -2,9 +2,6 @@ local cf = require "cf" local cf_at = cf.at -local aio = require "aio" -local aio_fflush = aio.fflush - local class = require "class" local sys = require "sys" @@ -37,7 +34,7 @@ if ASYNC and io_type(io.output()) == 'file' then local output = io.output() output:setvbuf("full", ASYNC_BUFFER_SIZE) local at = cf_at(0.5, function () - aio_fflush(output) + output:flush() end) end @@ -188,7 +185,7 @@ local function async_write(self, log) end if self.file then self.counter = 0 - aio_fflush(self.file) + self.file:flush() end end) end From 170303814116ffeb7bb1827993e11832ed092099 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 19 Jul 2022 23:50:19 +0800 Subject: [PATCH 898/956] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core.c b/src/core.c index 3a4a887c..5e417091 100644 --- a/src/core.c +++ b/src/core.c @@ -230,7 +230,7 @@ int core_worker_run(const char entry[]) { /* 根据进程运行模式选择不同的启动方式 */ status = getenv("cfadmin_isWorker") ? - luaL_loadbufferx(L, worker_boot, strlen(worker_boot), "=[worker.lua]", "t") : luaL_loadfile(L, entry); + luaL_loadbufferx(L, worker_boot, strlen(worker_boot), "=[worker.lua]", "t") : luaL_loadfile(L, entry); if (status > 1){ LOG("ERROR", lua_tostring(L, -1)); lua_close(L); From c4ebf262b7a0d638c12a48528d66154707c0acfc Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 20 Jul 2022 21:41:41 +0800 Subject: [PATCH 899/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Daio=E5=9C=A8gc?= =?UTF-8?q?=E8=A1=8C=E4=B8=BA=E5=AF=BC=E8=87=B4bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/aio/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lualib/aio/init.lua b/lualib/aio/init.lua index c7598b35..4d77cebb 100644 --- a/lualib/aio/init.lua +++ b/lualib/aio/init.lua @@ -200,7 +200,7 @@ function File:close( ... ) local fd = self.fd self.fd = nil self.status = "closed" - return aio._close(fd) + tcp_close(fd) end -- 打开文件,并返回File(始终以rw模式打开, 没有则会创建) From 1e7c2cb4f8c154c2792c45254aa16aaaa52ee997 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 25 Jul 2022 00:13:09 +0800 Subject: [PATCH 900/956] =?UTF-8?q?=E4=BC=98=E5=8C=96RSA=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E7=9A=84=E5=8A=A0=E8=BD=BD=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/rsa.c | 60 +++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/luaclib/src/lcrypt/rsa.c b/luaclib/src/lcrypt/rsa.c index bf306142..bbccd086 100644 --- a/luaclib/src/lcrypt/rsa.c +++ b/luaclib/src/lcrypt/rsa.c @@ -1,14 +1,4 @@ -#include "lcrypt.h" -// #include - -static inline RSA* READ_PEM_PUB_KEY(FILE *f) { - RSA* rsa = NULL; - return ((rsa = PEM_read_RSA_PUBKEY(f, NULL, NULL, NULL))) ? rsa : PEM_read_RSAPublicKey(f, NULL, NULL, NULL); -} - -static inline RSA* READ_PEM_PRI_KEY(FILE *f) { - return PEM_read_RSAPrivateKey(f, NULL, NULL, NULL); -} +#include "lcrypto.h" static inline RSA* new_public_key(lua_State *L) { size_t p_size = 0; @@ -16,15 +6,30 @@ static inline RSA* new_public_key(lua_State *L) { if (!p_path || p_size <= 0) return NULL; + RSA* key; + BIO* IO = BIO_new(BIO_s_mem()); BIO_write(IO, (const char *)p_path, p_size); + key = PEM_read_bio_RSAPublicKey(IO, NULL, NULL, NULL); + if (!key) { + BIO_write(IO, (const char *)p_path, p_size); /* 重新写入 */ + key = PEM_read_bio_RSA_PUBKEY(IO, NULL, NULL, NULL); + } + BIO_free(IO); + if (key){ + // RSA_print_fp(stdout, key, 0); + return key; + } FILE* f = fopen((const char *)p_path, "rb"); if (!f) return NULL; - RSA* p_key = READ_PEM_PUB_KEY(f); - // RSA_print_fp(f, p_key, 0); - // fflush(stdout); + key = PEM_read_RSAPublicKey(f, NULL, NULL, NULL); + if (!key){ + fseek(f, SEEK_SET, 0); /* 需要将指针重置为开头 */ + key = PEM_read_RSA_PUBKEY(f, NULL, NULL, NULL); + } + // RSA_print_fp(stdout, key, 0); fclose(f); - return p_key; + return key; } static inline RSA* new_private_key(lua_State *L) { @@ -33,15 +38,22 @@ static inline RSA* new_private_key(lua_State *L) { if (!p_path || p_size <= 0) return NULL; + RSA* key; + BIO* IO = BIO_new(BIO_s_mem()); BIO_write(IO, (const char *)p_path, p_size); + key = PEM_read_bio_RSAPrivateKey(IO, NULL, NULL, NULL); + BIO_free(IO); + if (key){ + // RSA_print_fp(stdout, key, 0); + return key; + } FILE* f = fopen((const char *)p_path, "rb"); if (!f) return NULL; - RSA* p_key = READ_PEM_PRI_KEY(f); - // RSA_print_fp(f, p_key, 0); - // fflush(stdout); + key = PEM_read_RSAPrivateKey(f, NULL, NULL, NULL); + // RSA_print_fp(stdout, key, 0); fclose(f); - return p_key; + return key; } static inline const uint8_t* get_text(lua_State *L, size_t *size) { @@ -85,7 +97,6 @@ int lrsa_public_key_encode(lua_State *L){ luaL_pushresultsize(&b, RSA_size(key)); RSA_free(key); return 1; - } int lrsa_private_key_decode(lua_State *L) { @@ -102,7 +113,7 @@ int lrsa_private_key_decode(lua_State *L) { luaL_Buffer b; unsigned char* result = (unsigned char*)luaL_buffinitsize(L, &b, RSA_size(key)); - size_t len = RSA_private_decrypt(text_size, text, result, key, get_mode(L)); + ssize_t len = RSA_private_decrypt(text_size, text, result, key, get_mode(L)); if (0 > len) { RSA_free(key); luaL_pushresultsize(&b, 0); @@ -112,7 +123,6 @@ int lrsa_private_key_decode(lua_State *L) { luaL_pushresultsize(&b, len); RSA_free(key); return 1; - } int lrsa_private_key_encode(lua_State *L){ @@ -153,7 +163,7 @@ int lrsa_public_key_decode(lua_State *L){ luaL_Buffer b; unsigned char* result = (unsigned char*)luaL_buffinitsize(L, &b, RSA_size(key)); - size_t len = RSA_public_decrypt(text_size, text, result, key, get_mode(L)); + ssize_t len = RSA_public_decrypt(text_size, text, result, key, get_mode(L)); if (0 > len) { RSA_free(key); luaL_pushresultsize(&b, 0); @@ -245,7 +255,7 @@ int lrsa_verify(lua_State *L) { RSA* rsa = new_public_key(L); if (!rsa) - return luaL_error(L, "Can't find valide private rsa."); + return luaL_error(L, "Can't find valide public rsa."); size_t ssize = 0; const uint8_t *sign = (const uint8_t*)luaL_checklstring(L, 3, &ssize); @@ -263,4 +273,4 @@ int lrsa_verify(lua_State *L) { RSA_free(rsa); return 1; -} \ No newline at end of file +} From e0ba6bbb27b6fad4e087ce94bff2a61d41f9e15d Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Mon, 25 Jul 2022 00:14:09 +0800 Subject: [PATCH 901/956] Update rsa.c --- luaclib/src/lcrypt/rsa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/luaclib/src/lcrypt/rsa.c b/luaclib/src/lcrypt/rsa.c index bbccd086..0225ec22 100644 --- a/luaclib/src/lcrypt/rsa.c +++ b/luaclib/src/lcrypt/rsa.c @@ -1,4 +1,4 @@ -#include "lcrypto.h" +#include "lcrypt.h" static inline RSA* new_public_key(lua_State *L) { size_t p_size = 0; From 4cc659e6724f73220749d82d397aebc45b172a93 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Tue, 23 Aug 2022 22:04:52 +0800 Subject: [PATCH 902/956] =?UTF-8?q?=E5=AE=8C=E5=96=84aes=5Fgcm=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lcrypt/aes.c | 123 +++++++++++++++++++++++++++++++++++++-- lualib/crypt/aes.lua | 24 ++++---- 2 files changed, 129 insertions(+), 18 deletions(-) diff --git a/luaclib/src/lcrypt/aes.c b/luaclib/src/lcrypt/aes.c index 0078bdbc..0098c5db 100644 --- a/luaclib/src/lcrypt/aes.c +++ b/luaclib/src/lcrypt/aes.c @@ -205,11 +205,6 @@ int laes_ctr_encrypt(lua_State *L) { return AES_ENCRYPT(L, AES_CTR_MODE); } -int laes_gcm_encrypt(lua_State *L) { - - return AES_ENCRYPT(L, AES_GCM_MODE); -} - /* 解密封装 */ int laes_ecb_decrypt(lua_State *L) { @@ -237,7 +232,123 @@ int laes_ctr_decrypt(lua_State *L) { return AES_DECRYPT(L, AES_CTR_MODE); } +/* AES GCM */ +int laes_gcm_encrypt(lua_State *L) { + + lua_Integer bit = 0; size_t text_sz = 0; + + uint8_t* key = NULL; uint8_t* text = NULL; uint8_t* iv = NULL; + + lua_getargs(L, &bit, &text, &text_sz, &iv, &key); uint8_t *aad = (uint8_t *)luaL_checkstring(L, 5); + + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + if (!ctx) + return luaL_error(L, "allocate EVP failed."); + + if (1 != EVP_EncryptInit_ex(ctx, aes_get_cipher(AES_GCM_MODE, bit), NULL, key, iv)){ + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + lua_pushnil(L); + lua_pushstring(L, "aes_encrypt_init failed."); + return 2; + } + + int out_size = text_sz + EVP_MAX_BLOCK_LENGTH; + uint8_t *ciphertext = lua_newuserdata(L, out_size); + + int len; int total = 0; + + /* 添加`aad` */ + if(1 != EVP_EncryptUpdate(ctx, NULL, &len, aad, strlen((char*)aad))) { + EVP_CIPHER_CTX_free(ctx); + lua_pushnil(L); + lua_pushstring(L, "aes_encrypt_update update aad failed."); + return 2; + } + // printf("len = %d", len); + if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, text, text_sz)) { + EVP_CIPHER_CTX_free(ctx); + lua_pushnil(L); + lua_pushstring(L, "aes_encrypt_update update cipher failed."); + return 2; + } + total += len; + + if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) { + EVP_CIPHER_CTX_free(ctx); + lua_pushnil(L); + lua_pushstring(L, "aes_encrypt_final update aad failed."); + return 2; + } + total += len; + + if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, ciphertext + total)) { + EVP_CIPHER_CTX_free(ctx); + lua_pushnil(L); + lua_pushstring(L, "aes_encrypt_final get tag failed."); + return 2; + } + total += 16; + + lua_pushlstring(L, (const char*)ciphertext, total); + EVP_CIPHER_CTX_free(ctx); + return 1; +} + int laes_gcm_decrypt(lua_State *L) { + lua_Integer bit = 0; size_t text_sz = 0; + + uint8_t* key = NULL; uint8_t* text = NULL; uint8_t* iv = NULL; + + lua_getargs(L, &bit, &text, &text_sz, &iv, &key); uint8_t *aad = (uint8_t *)luaL_checkstring(L, 5); + + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + if (!ctx) + return luaL_error(L, "allocate EVP failed."); - return AES_DECRYPT(L, AES_GCM_MODE); + if (1 != EVP_DecryptInit_ex(ctx, aes_get_cipher(AES_GCM_MODE, bit), NULL, key, iv)){ + EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); + lua_pushnil(L); + lua_pushstring(L, "aes_decrypt_init failed."); + return 2; + } + + uint8_t *ciphertext = lua_newuserdata(L, text_sz); + int len; int total; + + /* 添加`aad` */ + if(1 != EVP_DecryptUpdate(ctx, NULL, &len, aad, strlen((char*)aad))) { + EVP_CIPHER_CTX_free(ctx); + lua_pushnil(L); + lua_pushstring(L, "aes_decrypt_update update aad failed."); + return 2; + } + + /* 添加加密文本 */ + if(!EVP_DecryptUpdate(ctx, ciphertext, &len, text, text_sz - 16)) { + EVP_CIPHER_CTX_free(ctx); + lua_pushnil(L); + lua_pushstring(L, "aes_decrypt_update update aad failed."); + return 2; + } + total = len; + + if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, text + text_sz - 16)) { + EVP_CIPHER_CTX_free(ctx); + lua_pushnil(L); + lua_pushstring(L, "aes_decrypt_final set tag failed."); + return 2; + } + + if (EVP_DecryptFinal_ex(ctx, ciphertext + len, &len) <= 0) { + EVP_CIPHER_CTX_free(ctx); + lua_pushnil(L); + lua_pushstring(L, "aes_decrypt_final flush data failed."); + return 2; + } + + lua_pushlstring(L, (const char*)ciphertext, total + len); + EVP_CIPHER_CTX_free(ctx); + return 1; } \ No newline at end of file diff --git a/lualib/crypt/aes.lua b/lualib/crypt/aes.lua index 64ec94cd..c573d634 100644 --- a/lualib/crypt/aes.lua +++ b/lualib/crypt/aes.lua @@ -62,8 +62,8 @@ function AES.aes_128_ctr_encrypt(key, text, iv, hex) return hash end -function AES.aes_128_gcm_encrypt(key, text, iv, hex) - local hash = aes_gcm_encrypt(16, key, text, iv) +function AES.aes_128_gcm_encrypt(key, text, iv, aad, hex) + local hash = aes_gcm_encrypt(16, key, text, iv, aad) if hash and hex then return hexencode(hash) end @@ -110,8 +110,8 @@ function AES.aes_192_ctr_encrypt(key, text, iv, hex) return hash end -function AES.aes_192_gcm_encrypt(key, text, iv, hex) - local hash = aes_gcm_encrypt(24, key, text, iv) +function AES.aes_192_gcm_encrypt(key, text, iv, aad, hex) + local hash = aes_gcm_encrypt(24, key, text, iv, aad) if hash and hex then return hexencode(hash) end @@ -158,8 +158,8 @@ function AES.aes_256_ctr_encrypt(key, text, iv, hex) return hash end -function AES.aes_256_gcm_encrypt(key, text, iv, hex) - local hash = aes_gcm_encrypt(32, key, text, iv) +function AES.aes_256_gcm_encrypt(key, text, iv, aad, hex) + local hash = aes_gcm_encrypt(32, key, text, iv, aad) if hash and hex then return hexencode(hash) end @@ -202,11 +202,11 @@ function AES.aes_128_ctr_decrypt(key, cipher, iv, hex) return aes_ctr_decrypt(16, key, cipher, iv) end -function AES.aes_128_gcm_decrypt(key, cipher, iv, hex) +function AES.aes_128_gcm_decrypt(key, cipher, iv, aad, hex) if hex then cipher = hexdecode(cipher) end - return aes_gcm_decrypt(16, key, cipher, iv) + return aes_gcm_decrypt(16, key, cipher, iv, aad) end function AES.aes_192_cbc_decrypt(key, cipher, iv, hex) @@ -244,11 +244,11 @@ function AES.aes_192_ctr_decrypt(key, cipher, iv, hex) return aes_ctr_decrypt(24, key, cipher, iv) end -function AES.aes_192_gcm_decrypt(key, cipher, iv, hex) +function AES.aes_192_gcm_decrypt(key, cipher, iv, aad, hex) if hex then cipher = hexdecode(cipher) end - return aes_gcm_decrypt(24, key, cipher, iv) + return aes_gcm_decrypt(24, key, cipher, iv, aad) end function AES.aes_256_cbc_decrypt(key, cipher, iv, hex) @@ -286,11 +286,11 @@ function AES.aes_256_ctr_decrypt(key, cipher, iv, hex) return aes_ctr_decrypt(32, key, cipher, iv) end -function AES.aes_256_gcm_decrypt(key, cipher, iv, hex) +function AES.aes_256_gcm_decrypt(key, cipher, iv, aad, hex) if hex then cipher = hexdecode(cipher) end - return aes_gcm_decrypt(32, key, cipher, iv) + return aes_gcm_decrypt(32, key, cipher, iv, aad) end -- 初始化函数 return function (t) From f11a7248385f8b02558b0082a73e276c80f3cea6 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 31 Aug 2022 11:10:25 +0800 Subject: [PATCH 903/956] Update mysql.lua MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复mysql协议在特殊情况下出错的问题. --- lualib/protocol/mysql.lua | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lualib/protocol/mysql.lua b/lualib/protocol/mysql.lua index c99647fa..0027c032 100644 --- a/lualib/protocol/mysql.lua +++ b/lualib/protocol/mysql.lua @@ -545,13 +545,17 @@ local function read_response (self, results) self.state = nil return nil, err end - if strbyte(packet, 1) == RESP_EOF and #packet < 9 then + local b = strbyte(packet, 1) + if b == RESP_EOF and #packet < 9 then local sever = get_eof_packet(packet) if sever.status_flags & SERVER_MORE_RESULTS == SERVER_MORE_RESULTS then again = true end break end + if b == RESP_ERROR then + return nil, get_mysql_error_packet(packet:sub(2)) + end rows[#rows+1] = get_rows(packet, #fields) end @@ -682,13 +686,17 @@ local function read_execute_reponse(self) self.state = nil return nil, err end - if strbyte(packet, 1) == RESP_EOF and #packet < 9 then + local b = strbyte(packet, 1) + if b == RESP_EOF and #packet < 9 then get_eof_packet(packet) -- if sever.status_flags & SERVER_MORE_RESULTS == SERVER_MORE_RESULTS then -- again = true -- end break end + if b == RESP_ERROR then + return nil, get_mysql_error_packet(packet:sub(2)) + end rows[#rows+1] = get_bin_rows(packet, fields) end -- var_dump(rows) @@ -1012,4 +1020,4 @@ function mysql:close() return sock:close() end -return mysql \ No newline at end of file +return mysql From 03bfdb9d4b9b02410f2f4f221b88bec77e6b60d5 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 4 Sep 2022 15:18:58 +0800 Subject: [PATCH 904/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0lz=E7=9A=84=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lz/lzlib.c | 300 +++++++++++++++-------------------------- 1 file changed, 112 insertions(+), 188 deletions(-) diff --git a/luaclib/src/lz/lzlib.c b/luaclib/src/lz/lzlib.c index d4ba6b20..b76509f3 100644 --- a/luaclib/src/lz/lzlib.c +++ b/luaclib/src/lz/lzlib.c @@ -1,241 +1,165 @@ +/* +** LICENSE: BSD +** Author: CandyMi[https://github.com/candymi] +*/ #define LUA_LIB #include #include -#ifndef MAX_WBITS -# define MAX_WBITS 15 /* 32K LZ77 window */ -#endif - -// 压缩模式 -enum zmode { - Z_COMPRESS1_MODE = 0, - Z_COMPRESS2_MODE = 1, - Z_GZCOMPRESS_MODE = 2, -}; - -// 压缩窗口大小 -enum zwsize { - Z_COMPRESS1_WSIZE = +MAX_WBITS, - Z_COMPRESS2_WSIZE = -MAX_WBITS, - Z_GZCOMPRESS_WSIZE = MAX_WBITS + 16, -}; +/* 分配内存 */ +void* stream_zalloc(void* opaque, unsigned items, unsigned nsize) +{ + (void)opaque; + return xmalloc(((size_t)items) * ((size_t)nsize)); +} -static int zwindow[] = { Z_COMPRESS1_WSIZE, Z_COMPRESS2_WSIZE, Z_GZCOMPRESS_WSIZE }; +/* 释放内存 */ +void stream_free(void* opaque, void* ptr) { + (void)opaque; + xfree(ptr); +} -// 初始化 -static inline void stream_init(z_stream *z) { +void stream_init(z_stream *z) { memset(z, 0x0, sizeof(z_stream)); - z->zalloc = Z_NULL; - z->zfree = Z_NULL; - z->opaque = Z_NULL; + z->zalloc = stream_zalloc; + z->zfree = stream_free; } -// 压缩 -static inline int stream_deflate(lua_State* L, int mode, const uint8_t* in, size_t in_size) { - - z_stream z; - stream_init(&z); +/* 压缩 */ +static inline int stream_deflate(lua_State* L, z_stream *z, int Z_MYMODE, const uint8_t* in, size_t in_size) { + if (!z) + return luaL_error(L, "[ZLIB ERROR]: `stream_deflate` got invalid `z_stream`."); - size_t out_size; - uint8_t *out; + if (Z_OK != deflateInit2(z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, Z_MYMODE, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY)) + return luaL_error(L, "[ZLIB ERROR]: `stream_deflate` init failed."); - if (Z_OK != deflateInit2(&z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, zwindow[mode], MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY)) - return luaL_error(L, "[ZLIB ERROR]: deflateInit init failed."); + /* 输入 */ + z->next_in = (uint8_t *)in; z->avail_in = in_size; - out_size = deflateBound(&z, in_size); - out = lua_newuserdata(L, out_size); - - // 输入 - z.next_in = (uint8_t *)in; - z.avail_in = in_size; + /* 输出 */ + size_t out_size = deflateBound(z, in_size) + MAX_WBITS; + uint8_t *out = lua_newuserdata(L, out_size); + z->next_out = out; z->avail_out = out_size; - z.next_out = out; - z.avail_out = out_size; + /* 压缩数据 */ + deflate(z, Z_SYNC_FLUSH); + deflateEnd(z); - int ret = deflate(&z, Z_FINISH); - // 压缩 - if (ret != Z_STREAM_END) { - deflateEnd(&z); - return luaL_error(L, "[ZLIB ERROR]: deflate error(%d).", ret); - } - // 清理 - if (deflateEnd(&z) != Z_OK){ - return luaL_error(L, "[ZLIB ERROR]: deflateEnd error(%d).", ret); - } - // 结束 - lua_pushlstring(L, (const char*)out, z.total_out); + /* 结束 */ + lua_pushlstring(L, (const char*)out, z->total_out - 4); lua_pushinteger(L, in_size); return 2; } -// 解缩 -static inline int stream_inflate(lua_State* L, int windsize, const uint8_t* in, size_t in_size) { - z_stream z; - stream_init(&z); - - z.next_in = (uint8_t *)in; - z.avail_in = in_size; - - if (Z_OK != inflateInit2(&z, windsize)) - return luaL_error(L, "[ZLIB ERROR]: inflateInit init failed."); - - luaL_Buffer B; - luaL_buffinit(L, &B); - - int bszie = 65535; - uint8_t buffer[bszie]; - - uint64_t offset = 0; - - for (;;) { - z.next_out = buffer; - z.avail_out = bszie; - // 始终用最小的内存来解压数据. - int ret = inflate(&z, Z_NO_FLUSH); - // 如果已经到数据流的尾部. - if (ret == Z_STREAM_END){ - luaL_addlstring(&B, (const char*)buffer, z.total_out - offset); - offset = z.total_out; - break; - } - // 如果数据出现其他异常情况 - if (ret != Z_OK){ - inflateEnd(&z); +/* 解压 */ +static inline int stream_inflate(lua_State* L, z_stream *z, int Z_MYWIND, const uint8_t* in, size_t in_size) { + if (!z) + return luaL_error(L, "[ZLIB ERROR]: `stream_inflate` got Invalid `z_stream`."); + + if (Z_OK != inflateInit2(z, Z_MYWIND)) + return luaL_error(L, "[ZLIB ERROR]: `stream_inflate` init failed."); + + /* 输入 */ + z->next_in = (uint8_t *)in; z->avail_in = in_size; + + /* 输出 */ + luaL_Buffer B; luaL_buffinit(L, &B); + size_t bsize = 4096; uint8_t *buffer = alloca(bsize); + + size_t offset = 0; /* z->total_out - offset 真正的输出缓冲区的长度 */ + while (1) + { + z->next_out = buffer; z->avail_out = bsize; + int ret = inflate(z, Z_SYNC_FLUSH); + // printf("解压 : [ret] = %d, isize = [%ld], osize = [%ld]\n", ret, z->total_in, z->total_out); + if (ret != Z_OK) { + inflateEnd(z); lua_pushboolean(L, 0); lua_pushfstring(L, "[ZLIB ERROR]: Invalid inflate buffer. %d", ret); return 2; } - // printf("inline : [ret] = %d, isize = [%ld], osize = [%ld]\n", ret, z.total_in, z.total_out); - // 每次迭代都需要把缓冲区的数据添加到内部. - luaL_addlstring(&B, (const char*)buffer, z.total_out - offset); - offset = z.total_out; - } - - int ret = inflateEnd(&z); - if (ret != Z_OK){ - lua_pushboolean(L, 0); - lua_pushfstring(L, "[ZLIB ERROR]: Invalid inflateEnd buffer. %d", ret); - return 2; + luaL_addlstring(&B, (char *)buffer, z->total_out - offset); + // printf("buf[%s]\n", buffer); + if (z->total_in == in_size) + break; + offset = z->total_out; } - // printf("over: [ret] = %d, isize = [%ld], osize = [%ld]\n", ret, z.total_in, z.total_out); + /* 结束 */ + inflateEnd(z); luaL_pushresult(&B); return 1; } -static int lcompress(lua_State *L) { - size_t in_size = 0; - const uint8_t* in = (const uint8_t*)luaL_checklstring(L, 1, &in_size); - if (in_size <= 0) - return luaL_error(L, "[ZLIB ERROR]: Invalid in buffer."); - - return stream_deflate(L, Z_COMPRESS1_MODE, (const uint8_t*)in, in_size); +/* RFC 1952 */ +int lgzip_compress(lua_State* L) { + z_stream z; stream_init(&z); size_t bsize; + uint8_t *buffer = (uint8_t *)luaL_checklstring(L, 1, &bsize); + return stream_deflate(L, &z, MAX_WBITS + 16, buffer, bsize); } -static int luncompress(lua_State *L) { - size_t in_size = 0; - const uint8_t* in = (const uint8_t*)luaL_checklstring(L, 1, &in_size); - if (in_size <= 0) - return luaL_error(L, "[ZLIB ERROR]: Invalid in buffer."); - - return stream_inflate(L, Z_COMPRESS1_WSIZE, (const uint8_t*)in, in_size); +int lgzip_uncompress(lua_State* L) { + z_stream z; stream_init(&z); size_t bsize; + uint8_t *buffer = (uint8_t *)luaL_checklstring(L, 1, &bsize); + return stream_inflate(L, &z, MAX_WBITS + 16, buffer, bsize); } -static int lcompress2(lua_State *L) { - size_t in_size = 0; - const uint8_t* in = (const uint8_t*)luaL_checklstring(L, 1, &in_size); - if (in_size <= 0) - return luaL_error(L, "[ZLIB ERROR]: Invalid in buffer."); - - return stream_deflate(L, Z_COMPRESS2_MODE, (const uint8_t*)in, in_size); +/* RFC 1951 */ +int ldeflate_compress(lua_State* L) { + z_stream z; stream_init(&z); size_t bsize; + uint8_t *buffer = (uint8_t *)luaL_checklstring(L, 1, &bsize); + return stream_deflate(L, &z, -MAX_WBITS, buffer, bsize); } -static int luncompress2(lua_State *L) { - size_t in_size = 0; - const uint8_t* in = (const uint8_t*)luaL_checklstring(L, 1, &in_size); - if (in_size <= 0) - return luaL_error(L, "[ZLIB ERROR]: Invalid in buffer."); - - return stream_inflate(L, Z_COMPRESS2_WSIZE, (const uint8_t*)in, in_size); +int ldeflate_uncompress(lua_State* L) { + z_stream z; stream_init(&z); size_t bsize; + uint8_t *buffer = (uint8_t *)luaL_checklstring(L, 1, &bsize); + return stream_inflate(L, &z, -MAX_WBITS, buffer, bsize); } -static int lgzip_compress(lua_State *L) { - size_t in_size = 0; - const uint8_t* in = (const uint8_t*)luaL_checklstring(L, 1, &in_size); - if (in_size <= 0) - return luaL_error(L, "[ZLIB ERROR]: Invalid in buffer."); - - return stream_deflate(L, Z_GZCOMPRESS_MODE, (const uint8_t*)in, in_size); +/* RFC 1950 */ +int lzlib_compress(lua_State* L) { + z_stream z; stream_init(&z); size_t bsize; + uint8_t *buffer = (uint8_t *)luaL_checklstring(L, 1, &bsize); + return stream_deflate(L, &z, MAX_WBITS, buffer, bsize); } -static int lgzip_uncompress(lua_State *L) { - size_t in_size = 0; - const uint8_t* in = (const uint8_t*)luaL_checklstring(L, 1, &in_size); - if (in_size <= 0) - return luaL_error(L, "[ZLIB ERROR]: Invalid in buffer."); - - return stream_inflate(L, Z_GZCOMPRESS_WSIZE, (const uint8_t*)in, in_size); +int lzlib_uncompress(lua_State* L) { + z_stream z; stream_init(&z); size_t bsize; + uint8_t *buffer = (uint8_t *)luaL_checklstring(L, 1, &bsize); + return stream_inflate(L, &z, MAX_WBITS, buffer, bsize); } -static int lws_compress(lua_State *L) { - size_t in_size = 0; - const uint8_t* in = (const uint8_t*)luaL_checklstring(L, 1, &in_size); - if (in_size <= 0) - return luaL_error(L, "[ZLIB ERROR]: Invalid in buffer."); - - z_stream z; - stream_init(&z); - - if (Z_OK != deflateInit2(&z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, Z_COMPRESS2_WSIZE, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY)) - return luaL_error(L, "[ZLIB ERROR]: deflateInit init failed."); - - size_t out_size = deflateBound(&z, in_size); - uint8_t *out = lua_newuserdata(L, out_size); - - // 输入 - z.next_in = (uint8_t *)in; - z.avail_in = in_size; - - z.next_out = out; - z.avail_out = out_size; - - deflate(&z, Z_SYNC_FLUSH); - deflateEnd(&z); - - // 结束 - lua_pushlstring(L, (const char*)out, z.total_out - 4); - lua_pushinteger(L, in_size); - return 2; +/* RFC 7692 */ +int lws_compress(lua_State* L) { + // z_stream z; stream_init(&z); size_t bsize; + // uint8_t *buffer = (uint8_t *)luaL_checklstring(L, 1, &bsize); + // return stream_deflate(L, &z, -MAX_WBITS, buffer, bsize); + return ldeflate_compress(L); } -static int lws_uncompress(lua_State *L) { - size_t in_size = 0; - const uint8_t* in = (const uint8_t*)luaL_checklstring(L, 1, &in_size); - if (in_size <= 0) - return luaL_error(L, "[ZLIB ERROR]: Invalid in buffer."); - - char *buf = (char *)in; - buf[0] = buf[0] + 1; - int ret = stream_inflate(L, Z_COMPRESS2_WSIZE, (const uint8_t*)in, in_size); - buf[0] = buf[0] - 1; - return ret; +int lws_uncompress(lua_State* L) { + // z_stream z; stream_init(&z); size_t bsize; + // uint8_t *buffer = (uint8_t *)luaL_checklstring(L, 1, &bsize); + // return stream_inflate(L, &z, -MAX_WBITS, buffer, bsize); + return ldeflate_uncompress(L); } -LUAMOD_API int luaopen_lz(lua_State *L){ +LUAMOD_API int luaopen_lz(lua_State *L) { luaL_checkversion(L); luaL_Reg zlib_libs[] = { /* LZ77压缩/解压方法 */ - {"compress", lcompress}, - {"uncompress", luncompress}, + {"compress", ldeflate_compress}, + {"uncompress", ldeflate_uncompress}, /* 原生压缩方法 */ - {"compress2", lcompress2}, - {"uncompress2", luncompress2}, + {"compress2", lzlib_compress}, + {"uncompress2", lzlib_uncompress}, /* gzip压缩/解压方法 */ - {"gzcompress", lgzip_compress}, - {"gzuncompress", lgzip_uncompress}, + {"gzcompress", lgzip_compress}, + {"gzuncompress", lgzip_uncompress}, /* Websocket压缩/解压方法 */ - {"wscompress", lws_compress}, - {"wsuncompress", lws_uncompress}, + {"wscompress", lws_compress}, + {"wsuncompress", lws_uncompress}, {NULL, NULL} }; luaL_newlib(L, zlib_libs); From 5b0cfc233e84d1b2d7188eb82bd8ad5820f92854 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 7 Sep 2022 09:56:14 +0800 Subject: [PATCH 905/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=A3=80=E6=9F=A5'gz?= =?UTF-8?q?ip'=E7=8A=B6=E6=80=81=E9=94=99=E8=AF=AF=E9=97=AE=E9=A2=98.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lz/lzlib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/luaclib/src/lz/lzlib.c b/luaclib/src/lz/lzlib.c index b76509f3..edca0639 100644 --- a/luaclib/src/lz/lzlib.c +++ b/luaclib/src/lz/lzlib.c @@ -73,7 +73,7 @@ static inline int stream_inflate(lua_State* L, z_stream *z, int Z_MYWIND, const z->next_out = buffer; z->avail_out = bsize; int ret = inflate(z, Z_SYNC_FLUSH); // printf("解压 : [ret] = %d, isize = [%ld], osize = [%ld]\n", ret, z->total_in, z->total_out); - if (ret != Z_OK) { + if (ret != Z_OK && ret != Z_STREAM_END) { inflateEnd(z); lua_pushboolean(L, 0); lua_pushfstring(L, "[ZLIB ERROR]: Invalid inflate buffer. %d", ret); From 8d2902d179189a1a6186dd59934da821c884051e Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 15 Sep 2022 12:41:06 +0800 Subject: [PATCH 906/956] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 68e0216b..be419904 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,8 @@ ### List of contributors * [suzaichuan](https://github.com/suzaichuan) - `Test` and `Problem Feedback`. + + * [Nuctori](https://github.com/Nuctori) - Feature Contribution. ### Users From 29d92f20531bc8e282d3703cb37ea61cb92671c2 Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Thu, 15 Sep 2022 12:44:17 +0800 Subject: [PATCH 907/956] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=B4=A1=E7=8C=AE?= =?UTF-8?q?=E6=84=9F=E8=B0=A2=E5=90=8D=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README_zh-cn.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README_zh-cn.md b/README_zh-cn.md index c730cedb..02809ab7 100644 --- a/README_zh-cn.md +++ b/README_zh-cn.md @@ -76,6 +76,8 @@ * [suzaichuan](https://github.com/suzaichuan) - `积极测试`与`问题反馈`. + * [Nuctori](https://github.com/Nuctori) - `功能性代码`的贡献. + ### 用户列表 |项目名称|项目图片| From eae0444a2e967e049a3e1ae93c064e8bb4ea620c Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 21 Sep 2022 18:42:32 +0800 Subject: [PATCH 908/956] Update README.md --- README.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index be419904..a91f997c 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@

                  -### Advantage +# Advantage - [x] Asynchronous I/O - The `Network I/O` and `File I/O` have been transformed, and the internal operations are now fully asynchronous. @@ -40,24 +40,26 @@ - [x] Enforcement and standardization -The framework forces you to write code, and you can also pass it on to your close friends. -### Website +# Website * [Home](https://cfadmin.cn/)(Only Chinese.) * [Wiki](https://doc.cfadmin.cn/)(Only Chinese.) -### Build And Run +# Build And Run

                  -### Preview +# Preview

                  +
                  More picture +

                  @@ -70,25 +72,27 @@

                  -### Feedback +
                  + +# Feedback * [ISSUES](https://github.com/CandyMi/cfadmin/issues) * [QQ Group](https://shang.qq.com/wpa/qunwpa?idkey=5cc977ebaf4eb17391b2c6b03eb0ee36e3d3c1871bc95ba3c96ffc426a9dc907) -### List of contributors +# List of contributors * [suzaichuan](https://github.com/suzaichuan) - `Test` and `Problem Feedback`. * [Nuctori](https://github.com/Nuctori) - Feature Contribution. -### Users +# Users |Project Name|Project Logo| |:-:|:-:| |商票易|![](https://raw.githubusercontent.com/wiki/CandyMi/cfadmin/images/company-2.png) |爆款捕手|![](https://raw.githubusercontent.com/wiki/CandyMi/cfadmin/images/company-1.png)| -### License +# License [BSD LICENSE](https://github.com/CandyMi/cfadmin/blob/master/LICENSE) From 5aaa3d865c500be0bc31b567a9eb35403ccc97ca Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Wed, 21 Sep 2022 18:45:30 +0800 Subject: [PATCH 909/956] =?UTF-8?q?=E6=9B=B4=E6=96=B0README.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README_zh-cn.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/README_zh-cn.md b/README_zh-cn.md index 02809ab7..e87be12d 100644 --- a/README_zh-cn.md +++ b/README_zh-cn.md @@ -24,7 +24,7 @@

                  -### 优势 +# 特性优势 - [x] 完善的异步I/O - 框架已经对网络、文件两种IO进行改造, 使内部的I/O全异步化成为可能. @@ -42,18 +42,20 @@ - [x] 强制化与规范化 - 框架强制要求您编写代码, 同样您也可以将其传授给挚友. -### 网站 +# 学习网站 * [社区首页](https://cfadmin.cn) * [学习文档](https://cfadmin.cn) -### 预览 +# 使用预览

                  +
                  更多图片 +

                  @@ -65,26 +67,28 @@

                  + +
                  -### 反馈 +# 反馈方式 * [我要提问](https://github.com/CandyMi/cfadmin/issues) * [我要加群](https://shang.qq.com/wpa/qunwpa?idkey=5cc977ebaf4eb17391b2c6b03eb0ee36e3d3c1871bc95ba3c96ffc426a9dc907) -### 贡献者名单 +# 贡献名单 * [suzaichuan](https://github.com/suzaichuan) - `积极测试`与`问题反馈`. * [Nuctori](https://github.com/Nuctori) - `功能性代码`的贡献. -### 用户列表 +# 用户列表 |项目名称|项目图片| |:-:|:-:| |商票易|![](https://raw.githubusercontent.com/wiki/CandyMi/cfadmin/images/company-2.png)| |爆款捕手|![](https://raw.githubusercontent.com/wiki/CandyMi/cfadmin/images/company-1.png)| -### 授权协议 +# 授权协议 [BSD LICENSE](https://github.com/CandyMi/cfadmin/blob/master/LICENSE) From fb3b3704a60e2694fd53a380f9c11901b25931aa Mon Sep 17 00:00:00 2001 From: CandyMi <869646063@qq.com> Date: Fri, 23 Sep 2022 17:41:33 +0800 Subject: [PATCH 910/956] =?UTF-8?q?=E4=BF=AE=E6=94=B9tls=E9=80=82=E5=BA=94?= =?UTF-8?q?openssl-3.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/ltcp.c | 49 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/luaclib/src/ltcp.c b/luaclib/src/ltcp.c index 7055ca8c..71bcd779 100644 --- a/luaclib/src/ltcp.c +++ b/luaclib/src/ltcp.c @@ -956,14 +956,26 @@ static int ssl_set_certificate(lua_State *L) { if (!path || size < 1) return luaL_error(L, "Invalid cert path"); - // 为SSL与SSL_CTX设置证书, 如果两种加载都失败则返回失败. - if (1 != SSL_use_certificate_file(ssl, path, SSL_FILETYPE_PEM)) - return luaL_error(L, "ssl ssl_set_certificate loading cert failed."); + X509 *cert; + /* 为`SSL`设置证书, 所有加载策略都失败就返回失败. */ + BIO* IO = BIO_new(BIO_s_mem()); BIO_write(IO, path, size); + cert = PEM_read_bio_X509(IO, NULL, NULL, NULL); + BIO_free(IO); + + /* 内存证书 */ + if (cert && 1 == SSL_use_certificate(ssl, cert)) + return 0; - if (1 != SSL_CTX_use_certificate_file(ctx, path, SSL_FILETYPE_PEM)) - return luaL_error(L, "ssl_ctx ssl_set_certificate loading cert failed."); + FILE* fp = fopen(path, "rb"); + cert = PEM_read_X509(fp, NULL, NULL, NULL); + if (fp) + fclose(fp); - return 1; + /* 文件证书 */ + if (cert && 1 == SSL_use_certificate(ssl, cert)) + return 0; + + return luaL_error(L, "[ssl error]: read cert failed."); } // 加载私钥 @@ -981,14 +993,26 @@ static int ssl_set_privatekey(lua_State *L) { if (!path || size < 1) return luaL_error(L, "Invalid cert path"); - // 为SSL与SSL_CTX设置私钥, 如果两种加载都失败则返回失败. - if (1 != SSL_use_PrivateKey_file(ssl, path, SSL_FILETYPE_PEM) && 1 != SSL_use_RSAPrivateKey_file(ssl, path, SSL_FILETYPE_PEM)) - return luaL_error(L, "ssl ssl_set_privatekey loading private_key or rsa_private_key failed."); + EVP_PKEY *key; + /* 为`SSL`设置私钥, 所有加载策略都失败就返回失败. */ + BIO* IO = BIO_new(BIO_s_mem()); BIO_write(IO, path, size); + key = PEM_read_bio_PrivateKey(IO, NULL, NULL, NULL); + BIO_free(IO); - if (1 != SSL_CTX_use_PrivateKey_file(ctx, path, SSL_FILETYPE_PEM) && 1 != SSL_CTX_use_RSAPrivateKey_file(ctx, path, SSL_FILETYPE_PEM)) - return luaL_error(L, "ssl_ctx ssl_set_privatekey loading private key or rsa_private_key failed."); + /* 内存私钥 */ + if (key && 1 == SSL_use_PrivateKey(ssl, key)) + return 0; - return 1; + FILE* fp = fopen(path, "rb"); + key = PEM_read_PrivateKey(fp, NULL, NULL, NULL); + if (fp) + fclose(fp); + + /* 文件私钥 */ + if (key && 1 == SSL_use_PrivateKey(ssl, key)) + return 0; + + return luaL_error(L, "[ssl error]: read private key failed."); } // 如果私钥有安装密钥, 则再这里设置 @@ -1006,7 +1030,6 @@ static int ssl_set_userdata_key(lua_State *L) { if (!password || size < 1) return 1; - // SSL_set_default_passwd_cb_userdata(ssl, (void*)password); SSL_CTX_set_default_passwd_cb_userdata(ctx, (void*)password); return 1; } From c64531662b100a6c13153c849e4a5ca37ff57439 Mon Sep 17 00:00:00 2001 From: Nuctori <1018570975@qq.com> Date: Sat, 29 Oct 2022 23:53:57 +0800 Subject: [PATCH 911/956] fix(Cookie): fix Set-Cookie concat error bug --- lualib/httpd/Cookie.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lualib/httpd/Cookie.lua b/lualib/httpd/Cookie.lua index ff0362df..65c6c516 100644 --- a/lualib/httpd/Cookie.lua +++ b/lualib/httpd/Cookie.lua @@ -118,8 +118,8 @@ function Cookie.serialization () for _, cookie in ipairs(cs) do local t = {} t[#t+1] = concat({cookie['name'], '=', encode_value(cookie['value'])}) - t[#t+1] = cookie['max-age'] and concat({'max-age', '=', cookie['max-age']}) - t[#t+1] = cookie['expires'] and concat({'expires', '=', os_date("%a, %d %b %Y %X GMT", cookie['expires'])}) + t[#t+1] = cookie['max-age'] and concat({'max-age', '=', cookie['max-age']}) or nil + t[#t+1] = cookie['expires'] and concat({'expires', '=', os_date("%a, %d %b %Y %X GMT", cookie['expires'])}) or nil t[#t+1] = concat({'path', '=', cookie['path']}) t[#t+1] = cookie['httponly'] if cookie['httponly'] then From 91e44c4b8595930b2fd55c8231dfb85d9e2ce403 Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sat, 3 Dec 2022 22:44:27 +0800 Subject: [PATCH 912/956] =?UTF-8?q?=E5=A2=9E=E5=8A=A0zlib=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E4=BD=BF=E7=94=A8miniz=E5=AE=9E=E7=8E=B0=E4=B8=8E?= =?UTF-8?q?=E5=8F=AF=E6=8C=87=E5=AE=9A=E7=9A=84=E4=BF=AE=E6=94=B9=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lz/Makefile | 10 +- luaclib/src/lz/lzlib.c | 118 +- luaclib/src/lz/miniz.c | 7735 +++++++++++++++++++++++++++++++++++++++ luaclib/src/lz/miniz.h | 1346 +++++++ 4 files changed, 9183 insertions(+), 26 deletions(-) create mode 100644 luaclib/src/lz/miniz.c create mode 100644 luaclib/src/lz/miniz.h diff --git a/luaclib/src/lz/Makefile b/luaclib/src/lz/Makefile index 3a8a4e3c..3b1b54b1 100644 --- a/luaclib/src/lz/Makefile +++ b/luaclib/src/lz/Makefile @@ -13,8 +13,14 @@ INCLUDES += -I../../../src -I/usr/local/include LIBS = -L../ -L../../ -L../../../ -L/usr/local/lib CFLAGS = -O3 -Wall -shared -fPIC -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib -DLL = -lcore -lz + +# 是用内置库 +MICRO = +DLL = -lcore +# 使用`zlib` +#MICRO = -DUSE_ZLIB=1 +#DLL = -lcore -lz build: - @$(CC) -o lz.so lzlib.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) + @$(CC) -o lz.so lzlib.c miniz.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) $(MICRO) @mv *.so ../../ diff --git a/luaclib/src/lz/lzlib.c b/luaclib/src/lz/lzlib.c index edca0639..774c744d 100644 --- a/luaclib/src/lz/lzlib.c +++ b/luaclib/src/lz/lzlib.c @@ -5,10 +5,23 @@ #define LUA_LIB #include -#include + +#if defined(USE_ZLIB) + #include +#else + #define MINIZ_NO_MALLOC + #define MZ_FREE xrio_free + #define MZ_MALLOC xrio_malloc + #define MZ_REALLOC xrio_realloc + #include "miniz.h" +#endif /* 分配内存 */ +#if defined(USE_ZLIB) void* stream_zalloc(void* opaque, unsigned items, unsigned nsize) +#else +void* stream_zalloc(void* opaque, size_t items, size_t nsize) +#endif { (void)opaque; return xmalloc(((size_t)items) * ((size_t)nsize)); @@ -26,6 +39,43 @@ void stream_init(z_stream *z) { z->zfree = stream_free; } +#if !defined(USE_ZLIB) +/* | 1 | 1 | 1 | 1 | 4 | 1 | 1 | +** ID1 + ID2 + CM + FLG + MTIME + XFL + OS +** |2 + len| null + terminal | 2 byte | +** FEXTRA + FNAME + FCOMMENT + FHCRC +*/ +static inline size_t gzip_check(const uint8_t *buffer, size_t bsize) { + if (bsize <= 10 || memcmp(buffer, "\x1f\x8b\x08", 3)) + return 0; + + int flag = buffer[3]; + size_t len = 10; + buffer += len; + // FEXTRA - 4 byte. + if (flag & 0x04) { + buffer += 2; + len += 4 + (buffer[0] | buffer[1] << 8); + } + // FNAME + if (flag & 0x08) { + while (*buffer++) + len++; + len += 1; /* NULL */ + } + // FCOMMENT + if (flag & 0x10) { + while (*buffer++) + len++; + len += 1; /* NULL */ + } + // FHCRC - 2 byte. + if (flag & 0x02) + len += 2; + return len; +} +#endif + /* 压缩 */ static inline int stream_deflate(lua_State* L, z_stream *z, int Z_MYMODE, const uint8_t* in, size_t in_size) { if (!z) @@ -81,8 +131,13 @@ static inline int stream_inflate(lua_State* L, z_stream *z, int Z_MYWIND, const } luaL_addlstring(&B, (char *)buffer, z->total_out - offset); // printf("buf[%s]\n", buffer); +#if defined(USE_ZLIB) if (z->total_in == in_size) break; +#else + if (z->total_out - offset < bsize) + break; +#endif offset = z->total_out; } /* 结束 */ @@ -91,60 +146,75 @@ static inline int stream_inflate(lua_State* L, z_stream *z, int Z_MYWIND, const return 1; } +/* RFC 7692 */ +int lws_compress(lua_State* L) { + z_stream z; stream_init(&z); size_t bsize; + const uint8_t *buffer = (const uint8_t *)luaL_checklstring(L, 1, &bsize); + return stream_deflate(L, &z, -MAX_WBITS, buffer, bsize); +} + +int lws_uncompress(lua_State* L) { + z_stream z; stream_init(&z); size_t bsize; + const uint8_t *buffer = (const uint8_t *)luaL_checklstring(L, 1, &bsize); + return stream_inflate(L, &z, -MAX_WBITS, buffer, bsize); +} + /* RFC 1952 */ int lgzip_compress(lua_State* L) { z_stream z; stream_init(&z); size_t bsize; - uint8_t *buffer = (uint8_t *)luaL_checklstring(L, 1, &bsize); - return stream_deflate(L, &z, MAX_WBITS + 16, buffer, bsize); + const uint8_t *buffer = (const uint8_t *)luaL_checklstring(L, 1, &bsize); +#if defined(USE_ZLIB) + stream_deflate(L, &z, MAX_WBITS + 16, buffer, bsize); +#else + stream_deflate(L, &z, -MAX_WBITS, buffer, bsize); + lua_pushlstring(L, /* GZIP Header */"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x13", 10); + lua_pushvalue(L, -3); lua_concat(L, 2); lua_pushvalue(L, -2); +#endif + return 2; } int lgzip_uncompress(lua_State* L) { z_stream z; stream_init(&z); size_t bsize; - uint8_t *buffer = (uint8_t *)luaL_checklstring(L, 1, &bsize); + const uint8_t *buffer = (const uint8_t *)luaL_checklstring(L, 1, &bsize); +#if defined(USE_ZLIB) return stream_inflate(L, &z, MAX_WBITS + 16, buffer, bsize); +#else + size_t hlen = gzip_check(buffer, bsize); + if (!hlen || hlen >= bsize) { + lua_pushboolean(L, 0); + lua_pushliteral(L, "[ZLIB ERROR]: Invalid gzip header."); + return 2; + } + return stream_inflate(L, &z, -MAX_WBITS, buffer + hlen, bsize - hlen); +#endif } /* RFC 1951 */ int ldeflate_compress(lua_State* L) { z_stream z; stream_init(&z); size_t bsize; - uint8_t *buffer = (uint8_t *)luaL_checklstring(L, 1, &bsize); + const uint8_t *buffer = (const uint8_t *)luaL_checklstring(L, 1, &bsize); return stream_deflate(L, &z, -MAX_WBITS, buffer, bsize); } int ldeflate_uncompress(lua_State* L) { z_stream z; stream_init(&z); size_t bsize; - uint8_t *buffer = (uint8_t *)luaL_checklstring(L, 1, &bsize); + const uint8_t *buffer = (const uint8_t *)luaL_checklstring(L, 1, &bsize); return stream_inflate(L, &z, -MAX_WBITS, buffer, bsize); } /* RFC 1950 */ int lzlib_compress(lua_State* L) { z_stream z; stream_init(&z); size_t bsize; - uint8_t *buffer = (uint8_t *)luaL_checklstring(L, 1, &bsize); + const uint8_t *buffer = (const uint8_t *)luaL_checklstring(L, 1, &bsize); return stream_deflate(L, &z, MAX_WBITS, buffer, bsize); } int lzlib_uncompress(lua_State* L) { z_stream z; stream_init(&z); size_t bsize; - uint8_t *buffer = (uint8_t *)luaL_checklstring(L, 1, &bsize); + const uint8_t *buffer = (const uint8_t *)luaL_checklstring(L, 1, &bsize); return stream_inflate(L, &z, MAX_WBITS, buffer, bsize); } -/* RFC 7692 */ -int lws_compress(lua_State* L) { - // z_stream z; stream_init(&z); size_t bsize; - // uint8_t *buffer = (uint8_t *)luaL_checklstring(L, 1, &bsize); - // return stream_deflate(L, &z, -MAX_WBITS, buffer, bsize); - return ldeflate_compress(L); -} - -int lws_uncompress(lua_State* L) { - // z_stream z; stream_init(&z); size_t bsize; - // uint8_t *buffer = (uint8_t *)luaL_checklstring(L, 1, &bsize); - // return stream_inflate(L, &z, -MAX_WBITS, buffer, bsize); - return ldeflate_uncompress(L); -} - LUAMOD_API int luaopen_lz(lua_State *L) { luaL_checkversion(L); luaL_Reg zlib_libs[] = { @@ -164,4 +234,4 @@ LUAMOD_API int luaopen_lz(lua_State *L) { }; luaL_newlib(L, zlib_libs); return 1; -} +} \ No newline at end of file diff --git a/luaclib/src/lz/miniz.c b/luaclib/src/lz/miniz.c new file mode 100644 index 00000000..cbd8b7b2 --- /dev/null +++ b/luaclib/src/lz/miniz.c @@ -0,0 +1,7735 @@ +#include "miniz.h" +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + + +typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1]; +typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1]; +typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1]; + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- zlib-style API's */ + +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) +{ + mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); + size_t block_len = buf_len % 5552; + if (!ptr) + return MZ_ADLER32_INIT; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; + } + for (; i < block_len; ++i) + s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; + } + return (s2 << 16) + s1; +} + +/* Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ */ +#if 0 + mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) + { + static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; + mz_uint32 crcu32 = (mz_uint32)crc; + if (!ptr) + return MZ_CRC32_INIT; + crcu32 = ~crcu32; + while (buf_len--) + { + mz_uint8 b = *ptr++; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; + } + return ~crcu32; + } +#elif defined(USE_EXTERNAL_MZCRC) +/* If USE_EXTERNAL_CRC is defined, an external module will export the + * mz_crc32() symbol for us to use, e.g. an SSE-accelerated version. + * Depending on the impl, it may be necessary to ~ the input/output crc values. + */ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len); +#else +/* Faster, but larger CPU cache footprint. + */ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) +{ + static const mz_uint32 s_crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, + 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, + 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, + 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, + 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, + 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, + 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, + 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, + 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, + 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, + 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, + 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, + 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, + 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, + 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, + 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, + 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, + 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, + 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, + 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, + 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, + 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, + 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, + 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, + 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, + 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, + 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, + 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF; + const mz_uint8 *pByte_buf = (const mz_uint8 *)ptr; + + while (buf_len >= 4) + { + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF]; + pByte_buf += 4; + buf_len -= 4; + } + + while (buf_len) + { + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; + ++pByte_buf; + --buf_len; + } + + return ~crc32; +} +#endif + +void mz_free(void *p) +{ + MZ_FREE(p); +} + +MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size) +{ + (void)opaque, (void)items, (void)size; + return MZ_MALLOC(items * size); +} +MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address) +{ + (void)opaque, (void)address; + MZ_FREE(address); +} +MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size) +{ + (void)opaque, (void)address, (void)items, (void)size; + return MZ_REALLOC(address, items * size); +} + +const char *mz_version(void) +{ + return MZ_VERSION; +} + +#ifndef MINIZ_NO_ZLIB_APIS + +int mz_deflateInit(mz_streamp pStream, int level) +{ + return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); +} + +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) +{ + tdefl_compressor *pComp; + mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); + + if (!pStream) + return MZ_STREAM_ERROR; + if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = MZ_ADLER32_INIT; + pStream->msg = NULL; + pStream->reserved = 0; + pStream->total_in = 0; + pStream->total_out = 0; + if (!pStream->zalloc) + pStream->zalloc = miniz_def_alloc_func; + if (!pStream->zfree) + pStream->zfree = miniz_def_free_func; + + pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pComp; + + if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) + { + mz_deflateEnd(pStream); + return MZ_PARAM_ERROR; + } + + return MZ_OK; +} + +int mz_deflateReset(mz_streamp pStream) +{ + if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) + return MZ_STREAM_ERROR; + pStream->total_in = pStream->total_out = 0; + tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, ((tdefl_compressor *)pStream->state)->m_flags); + return MZ_OK; +} + +int mz_deflate(mz_streamp pStream, int flush) +{ + size_t in_bytes, out_bytes; + mz_ulong orig_total_in, orig_total_out; + int mz_status = MZ_OK; + + if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) + return MZ_STREAM_ERROR; + if (!pStream->avail_out) + return MZ_BUF_ERROR; + + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + + if (((tdefl_compressor *)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) + return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; + + orig_total_in = pStream->total_in; + orig_total_out = pStream->total_out; + for (;;) + { + tdefl_status defl_status; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + + defl_status = tdefl_compress((tdefl_compressor *)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state); + + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (defl_status < 0) + { + mz_status = MZ_STREAM_ERROR; + break; + } + else if (defl_status == TDEFL_STATUS_DONE) + { + mz_status = MZ_STREAM_END; + break; + } + else if (!pStream->avail_out) + break; + else if ((!pStream->avail_in) && (flush != MZ_FINISH)) + { + if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) + break; + return MZ_BUF_ERROR; /* Can't make forward progress without some input. + */ + } + } + return mz_status; +} + +int mz_deflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) +{ + (void)pStream; + /* This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) */ + return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); +} + +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) +{ + int status; + mz_stream stream; + memset(&stream, 0, sizeof(stream)); + + /* In case mz_ulong is 64-bits (argh I hate longs). */ + if ((source_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_deflateInit(&stream, level); + if (status != MZ_OK) + return status; + + status = mz_deflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) + { + mz_deflateEnd(&stream); + return (status == MZ_OK) ? MZ_BUF_ERROR : status; + } + + *pDest_len = stream.total_out; + return mz_deflateEnd(&stream); +} + +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); +} + +mz_ulong mz_compressBound(mz_ulong source_len) +{ + return mz_deflateBound(NULL, source_len); +} + +typedef struct +{ + tinfl_decompressor m_decomp; + mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; + int m_window_bits; + mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; + tinfl_status m_last_status; +} inflate_state; + +int mz_inflateInit2(mz_streamp pStream, int window_bits) +{ + inflate_state *pDecomp; + if (!pStream) + return MZ_STREAM_ERROR; + if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + if (!pStream->zalloc) + pStream->zalloc = miniz_def_alloc_func; + if (!pStream->zfree) + pStream->zfree = miniz_def_free_func; + + pDecomp = (inflate_state *)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); + if (!pDecomp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pDecomp; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + pDecomp->m_window_bits = window_bits; + + return MZ_OK; +} + +int mz_inflateInit(mz_streamp pStream) +{ + return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); +} + +int mz_inflateReset(mz_streamp pStream) +{ + inflate_state *pDecomp; + if (!pStream) + return MZ_STREAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + + pDecomp = (inflate_state *)pStream->state; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + /* pDecomp->m_window_bits = window_bits */; + + return MZ_OK; +} + +int mz_inflate(mz_streamp pStream, int flush) +{ + inflate_state *pState; + mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; + size_t in_bytes, out_bytes, orig_avail_in; + tinfl_status status; + + if ((!pStream) || (!pStream->state)) + return MZ_STREAM_ERROR; + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + + pState = (inflate_state *)pStream->state; + if (pState->m_window_bits > 0) + decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; + orig_avail_in = pStream->avail_in; + + first_call = pState->m_first_call; + pState->m_first_call = 0; + if (pState->m_last_status < 0) + return MZ_DATA_ERROR; + + if (pState->m_has_flushed && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + pState->m_has_flushed |= (flush == MZ_FINISH); + + if ((flush == MZ_FINISH) && (first_call)) + { + /* MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. */ + decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); + pState->m_last_status = status; + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (status < 0) + return MZ_DATA_ERROR; + else if (status != TINFL_STATUS_DONE) + { + pState->m_last_status = TINFL_STATUS_FAILED; + return MZ_BUF_ERROR; + } + return MZ_STREAM_END; + } + /* flush != MZ_FINISH then we must assume there's more input. */ + if (flush != MZ_FINISH) + decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; + + if (pState->m_dict_avail) + { + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; + } + + for (;;) + { + in_bytes = pStream->avail_in; + out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; + + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); + pState->m_last_status = status; + + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + + pState->m_dict_avail = (mz_uint)out_bytes; + + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + + if (status < 0) + return MZ_DATA_ERROR; /* Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). */ + else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) + return MZ_BUF_ERROR; /* Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. */ + else if (flush == MZ_FINISH) + { + /* The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. */ + if (status == TINFL_STATUS_DONE) + return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; + /* status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. */ + else if (!pStream->avail_out) + return MZ_BUF_ERROR; + } + else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) + break; + } + + return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; +} + +int mz_inflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} +int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len) +{ + mz_stream stream; + int status; + memset(&stream, 0, sizeof(stream)); + + /* In case mz_ulong is 64-bits (argh I hate longs). */ + if ((*pSource_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)*pSource_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_inflateInit(&stream); + if (status != MZ_OK) + return status; + + status = mz_inflate(&stream, MZ_FINISH); + *pSource_len = *pSource_len - stream.avail_in; + if (status != MZ_STREAM_END) + { + mz_inflateEnd(&stream); + return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; + } + *pDest_len = stream.total_out; + + return mz_inflateEnd(&stream); +} + +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + return mz_uncompress2(pDest, pDest_len, pSource, &source_len); +} + +const char *mz_error(int err) +{ + static struct + { + int m_err; + const char *m_pDesc; + } s_error_descs[] = + { + { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } + }; + mz_uint i; + for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) + if (s_error_descs[i].m_err == err) + return s_error_descs[i].m_pDesc; + return NULL; +} + +#endif /*MINIZ_NO_ZLIB_APIS */ + +#ifdef __cplusplus +} +#endif + +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to +*/ +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- Low-level Compression (independent from all decompression API's) */ + +/* Purposely making these tables static for faster init and thread safety. */ +static const mz_uint16 s_tdefl_len_sym[256] = + { + 257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, 268, 269, 269, 269, 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, 272, 272, + 273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, 275, 275, 275, 275, 275, 275, 275, 275, 276, 276, 276, 276, 276, 276, 276, 276, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, + 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 285 + }; + +static const mz_uint8 s_tdefl_len_extra[256] = + { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0 + }; + +static const mz_uint8 s_tdefl_small_dist_sym[512] = + { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17 + }; + +static const mz_uint8 s_tdefl_small_dist_extra[512] = + { + 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7 + }; + +static const mz_uint8 s_tdefl_large_dist_sym[128] = + { + 0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 + }; + +static const mz_uint8 s_tdefl_large_dist_extra[128] = + { + 0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13 + }; + +/* Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. */ +typedef struct +{ + mz_uint16 m_key, m_sym_index; +} tdefl_sym_freq; +static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *pSyms0, tdefl_sym_freq *pSyms1) +{ + mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; + tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1; + MZ_CLEAR_OBJ(hist); + for (i = 0; i < num_syms; i++) + { + mz_uint freq = pSyms0[i].m_key; + hist[freq & 0xFF]++; + hist[256 + ((freq >> 8) & 0xFF)]++; + } + while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) + total_passes--; + for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) + { + const mz_uint32 *pHist = &hist[pass << 8]; + mz_uint offsets[256], cur_ofs = 0; + for (i = 0; i < 256; i++) + { + offsets[i] = cur_ofs; + cur_ofs += pHist[i]; + } + for (i = 0; i < num_syms; i++) + pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; + { + tdefl_sym_freq *t = pCur_syms; + pCur_syms = pNew_syms; + pNew_syms = t; + } + } + return pCur_syms; +} + +/* tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. */ +static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) +{ + int root, leaf, next, avbl, used, dpth; + if (n == 0) + return; + else if (n == 1) + { + A[0].m_key = 1; + return; + } + A[0].m_key += A[1].m_key; + root = 0; + leaf = 2; + for (next = 1; next < n - 1; next++) + { + if (leaf >= n || A[root].m_key < A[leaf].m_key) + { + A[next].m_key = A[root].m_key; + A[root++].m_key = (mz_uint16)next; + } + else + A[next].m_key = A[leaf++].m_key; + if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key)) + { + A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); + A[root++].m_key = (mz_uint16)next; + } + else + A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key); + } + A[n - 2].m_key = 0; + for (next = n - 3; next >= 0; next--) + A[next].m_key = A[A[next].m_key].m_key + 1; + avbl = 1; + used = dpth = 0; + root = n - 2; + next = n - 1; + while (avbl > 0) + { + while (root >= 0 && (int)A[root].m_key == dpth) + { + used++; + root--; + } + while (avbl > used) + { + A[next--].m_key = (mz_uint16)(dpth); + avbl--; + } + avbl = 2 * used; + dpth++; + used = 0; + } +} + +/* Limits canonical Huffman code table's max code size. */ +enum +{ + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 +}; +static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) +{ + int i; + mz_uint32 total = 0; + if (code_list_len <= 1) + return; + for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) + pNum_codes[max_code_size] += pNum_codes[i]; + for (i = max_code_size; i > 0; i--) + total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); + while (total != (1UL << max_code_size)) + { + pNum_codes[max_code_size]--; + for (i = max_code_size - 1; i > 0; i--) + if (pNum_codes[i]) + { + pNum_codes[i]--; + pNum_codes[i + 1] += 2; + break; + } + total--; + } +} + +static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) +{ + int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; + mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; + MZ_CLEAR_OBJ(num_codes); + if (static_table) + { + for (i = 0; i < table_len; i++) + num_codes[d->m_huff_code_sizes[table_num][i]]++; + } + else + { + tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; + int num_used_syms = 0; + const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; + for (i = 0; i < table_len; i++) + if (pSym_count[i]) + { + syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; + syms0[num_used_syms++].m_sym_index = (mz_uint16)i; + } + + pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); + tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); + + for (i = 0; i < num_used_syms; i++) + num_codes[pSyms[i].m_key]++; + + tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); + + MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); + MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); + for (i = 1, j = num_used_syms; i <= code_size_limit; i++) + for (l = num_codes[i]; l > 0; l--) + d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); + } + + next_code[1] = 0; + for (j = 0, i = 2; i <= code_size_limit; i++) + next_code[i] = j = ((j + num_codes[i - 1]) << 1); + + for (i = 0; i < table_len; i++) + { + mz_uint rev_code = 0, code, code_size; + if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) + continue; + code = next_code[code_size]++; + for (l = code_size; l > 0; l--, code >>= 1) + rev_code = (rev_code << 1) | (code & 1); + d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; + } +} + +#define TDEFL_PUT_BITS(b, l) \ + do \ + { \ + mz_uint bits = b; \ + mz_uint len = l; \ + MZ_ASSERT(bits <= ((1U << len) - 1U)); \ + d->m_bit_buffer |= (bits << d->m_bits_in); \ + d->m_bits_in += len; \ + while (d->m_bits_in >= 8) \ + { \ + if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ + *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ + d->m_bit_buffer >>= 8; \ + d->m_bits_in -= 8; \ + } \ + } \ + MZ_MACRO_END + +#define TDEFL_RLE_PREV_CODE_SIZE() \ + { \ + if (rle_repeat_count) \ + { \ + if (rle_repeat_count < 3) \ + { \ + d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ + while (rle_repeat_count--) \ + packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ + } \ + else \ + { \ + d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 16; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ + } \ + rle_repeat_count = 0; \ + } \ + } + +#define TDEFL_RLE_ZERO_CODE_SIZE() \ + { \ + if (rle_z_count) \ + { \ + if (rle_z_count < 3) \ + { \ + d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); \ + while (rle_z_count--) \ + packed_code_sizes[num_packed_code_sizes++] = 0; \ + } \ + else if (rle_z_count <= 10) \ + { \ + d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 17; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ + } \ + else \ + { \ + d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 18; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ + } \ + rle_z_count = 0; \ + } \ + } + +static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + +static void tdefl_start_dynamic_block(tdefl_compressor *d) +{ + int num_lit_codes, num_dist_codes, num_bit_lengths; + mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; + mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; + + d->m_huff_count[0][256] = 1; + + tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); + tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); + + for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) + if (d->m_huff_code_sizes[0][num_lit_codes - 1]) + break; + for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) + if (d->m_huff_code_sizes[1][num_dist_codes - 1]) + break; + + memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); + memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); + total_code_sizes_to_pack = num_lit_codes + num_dist_codes; + num_packed_code_sizes = 0; + rle_z_count = 0; + rle_repeat_count = 0; + + memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); + for (i = 0; i < total_code_sizes_to_pack; i++) + { + mz_uint8 code_size = code_sizes_to_pack[i]; + if (!code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + if (++rle_z_count == 138) + { + TDEFL_RLE_ZERO_CODE_SIZE(); + } + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + if (code_size != prev_code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); + packed_code_sizes[num_packed_code_sizes++] = code_size; + } + else if (++rle_repeat_count == 6) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } + } + prev_code_size = code_size; + } + if (rle_repeat_count) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + } + + tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); + + TDEFL_PUT_BITS(2, 2); + + TDEFL_PUT_BITS(num_lit_codes - 257, 5); + TDEFL_PUT_BITS(num_dist_codes - 1, 5); + + for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) + if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) + break; + num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); + TDEFL_PUT_BITS(num_bit_lengths - 4, 4); + for (i = 0; (int)i < num_bit_lengths; i++) + TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); + + for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes;) + { + mz_uint code = packed_code_sizes[packed_code_sizes_index++]; + MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); + TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); + if (code >= 16) + TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); + } +} + +static void tdefl_start_static_block(tdefl_compressor *d) +{ + mz_uint i; + mz_uint8 *p = &d->m_huff_code_sizes[0][0]; + + for (i = 0; i <= 143; ++i) + *p++ = 8; + for (; i <= 255; ++i) + *p++ = 9; + for (; i <= 279; ++i) + *p++ = 7; + for (; i <= 287; ++i) + *p++ = 8; + + memset(d->m_huff_code_sizes[1], 5, 32); + + tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); + tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); + + TDEFL_PUT_BITS(1, 2); +} + +static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + mz_uint8 *pOutput_buf = d->m_pOutput_buf; + mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; + mz_uint64 bit_buffer = d->m_bit_buffer; + mz_uint bits_in = d->m_bits_in; + +#define TDEFL_PUT_BITS_FAST(b, l) \ + { \ + bit_buffer |= (((mz_uint64)(b)) << bits_in); \ + bits_in += (l); \ + } + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + + if (flags & 1) + { + mz_uint s0, s1, n0, n1, sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + /* This sequence coaxes MSVC into using cmov's vs. jmp's. */ + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + n0 = s_tdefl_small_dist_extra[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[match_dist >> 8]; + n1 = s_tdefl_large_dist_extra[match_dist >> 8]; + sym = (match_dist < 512) ? s0 : s1; + num_extra_bits = (match_dist < 512) ? n0 : n1; + + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + } + + if (pOutput_buf >= d->m_pOutput_buf_end) + return MZ_FALSE; + + *(mz_uint64 *)pOutput_buf = bit_buffer; + pOutput_buf += (bits_in >> 3); + bit_buffer >>= (bits_in & ~7); + bits_in &= 7; + } + +#undef TDEFL_PUT_BITS_FAST + + d->m_pOutput_buf = pOutput_buf; + d->m_bits_in = 0; + d->m_bit_buffer = 0; + + while (bits_in) + { + mz_uint32 n = MZ_MIN(bits_in, 16); + TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); + bit_buffer >>= n; + bits_in -= n; + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#else +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + if (flags & 1) + { + mz_uint sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + if (match_dist < 512) + { + sym = s_tdefl_small_dist_sym[match_dist]; + num_extra_bits = s_tdefl_small_dist_extra[match_dist]; + } + else + { + sym = s_tdefl_large_dist_sym[match_dist >> 8]; + num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; + } + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS */ + +static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) +{ + if (static_block) + tdefl_start_static_block(d); + else + tdefl_start_dynamic_block(d); + return tdefl_compress_lz_codes(d); +} + +static int tdefl_flush_block(tdefl_compressor *d, int flush) +{ + mz_uint saved_bit_buf, saved_bits_in; + mz_uint8 *pSaved_output_buf; + mz_bool comp_block_succeeded = MZ_FALSE; + int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; + mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; + + d->m_pOutput_buf = pOutput_buf_start; + d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; + + MZ_ASSERT(!d->m_output_flush_remaining); + d->m_output_flush_ofs = 0; + d->m_output_flush_remaining = 0; + + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); + d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); + + if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) + { + TDEFL_PUT_BITS(0x78, 8); + TDEFL_PUT_BITS(0x01, 8); + } + + TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); + + pSaved_output_buf = d->m_pOutput_buf; + saved_bit_buf = d->m_bit_buffer; + saved_bits_in = d->m_bits_in; + + if (!use_raw_block) + comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); + + /* If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. */ + if (((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && + ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size)) + { + mz_uint i; + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + TDEFL_PUT_BITS(0, 2); + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) + { + TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); + } + for (i = 0; i < d->m_total_lz_bytes; ++i) + { + TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); + } + } + /* Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. */ + else if (!comp_block_succeeded) + { + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + tdefl_compress_block(d, MZ_TRUE); + } + + if (flush) + { + if (flush == TDEFL_FINISH) + { + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) + { + mz_uint i, a = d->m_adler32; + for (i = 0; i < 4; i++) + { + TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); + a <<= 8; + } + } + } + else + { + mz_uint i, z = 0; + TDEFL_PUT_BITS(0, 3); + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, z ^= 0xFFFF) + { + TDEFL_PUT_BITS(z & 0xFFFF, 16); + } + } + } + + MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); + + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + d->m_num_flags_left = 8; + d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; + d->m_total_lz_bytes = 0; + d->m_block_index++; + + if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) + { + if (d->m_pPut_buf_func) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) + return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); + } + else if (pOutput_buf_start == d->m_output_buf) + { + int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); + d->m_out_buf_ofs += bytes_to_copy; + if ((n -= bytes_to_copy) != 0) + { + d->m_output_flush_ofs = bytes_to_copy; + d->m_output_flush_remaining = n; + } + } + else + { + d->m_out_buf_ofs += n; + } + } + + return d->m_output_flush_remaining; +} + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES +#ifdef MINIZ_UNALIGNED_USE_MEMCPY +static mz_uint16 TDEFL_READ_UNALIGNED_WORD(const mz_uint8* p) +{ + mz_uint16 ret; + memcpy(&ret, p, sizeof(mz_uint16)); + return ret; +} +static mz_uint16 TDEFL_READ_UNALIGNED_WORD2(const mz_uint16* p) +{ + mz_uint16 ret; + memcpy(&ret, p, sizeof(mz_uint16)); + return ret; +} +#else +#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16 *)(p) +#define TDEFL_READ_UNALIGNED_WORD2(p) *(const mz_uint16 *)(p) +#endif +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q; + mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD2(s); + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) + { + for (;;) + { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + q = (const mz_uint16 *)(d->m_dict + probe_pos); + if (TDEFL_READ_UNALIGNED_WORD2(q) != s01) + continue; + p = s; + probe_len = 32; + do + { + } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); + if (!probe_len) + { + *pMatch_dist = dist; + *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN); + break; + } + else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q)) > match_len) + { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) + break; + c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); + } + } +} +#else +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint8 *s = d->m_dict + pos, *p, *q; + mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) + { + for (;;) + { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + p = s; + q = d->m_dict + probe_pos; + for (probe_len = 0; probe_len < max_match_len; probe_len++) + if (*p++ != *q++) + break; + if (probe_len > match_len) + { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = probe_len) == max_match_len) + return; + c0 = d->m_dict[pos + match_len]; + c1 = d->m_dict[pos + match_len - 1]; + } + } +} +#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES */ + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +#ifdef MINIZ_UNALIGNED_USE_MEMCPY +static mz_uint32 TDEFL_READ_UNALIGNED_WORD32(const mz_uint8* p) +{ + mz_uint32 ret; + memcpy(&ret, p, sizeof(mz_uint32)); + return ret; +} +#else +#define TDEFL_READ_UNALIGNED_WORD32(p) *(const mz_uint32 *)(p) +#endif +static mz_bool tdefl_compress_fast(tdefl_compressor *d) +{ + /* Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. */ + mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; + mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; + mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + + while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) + { + const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; + mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); + d->m_src_buf_left -= num_bytes_to_process; + lookahead_size += num_bytes_to_process; + + while (num_bytes_to_process) + { + mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); + memcpy(d->m_dict + dst_pos, d->m_pSrc, n); + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); + d->m_pSrc += n; + dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; + num_bytes_to_process -= n; + } + + dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); + if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) + break; + + while (lookahead_size >= 4) + { + mz_uint cur_match_dist, cur_match_len = 1; + mz_uint8 *pCur_dict = d->m_dict + cur_pos; + mz_uint first_trigram = TDEFL_READ_UNALIGNED_WORD32(pCur_dict) & 0xFFFFFF; + mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; + mz_uint probe_pos = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)lookahead_pos; + + if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((TDEFL_READ_UNALIGNED_WORD32(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) + { + const mz_uint16 *p = (const mz_uint16 *)pCur_dict; + const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); + mz_uint32 probe_len = 32; + do + { + } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); + cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); + if (!probe_len) + cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; + + if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U))) + { + cur_match_len = 1; + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + else + { + mz_uint32 s0, s1; + cur_match_len = MZ_MIN(cur_match_len, lookahead_size); + + MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); + + cur_match_dist--; + + pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); +#ifdef MINIZ_UNALIGNED_USE_MEMCPY + memcpy(&pLZ_code_buf[1], &cur_match_dist, sizeof(cur_match_dist)); +#else + *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; +#endif + pLZ_code_buf += 3; + *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); + + s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; + s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; + d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; + + d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; + } + } + else + { + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + + if (--num_flags_left == 0) + { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + total_lz_bytes += cur_match_len; + lookahead_pos += cur_match_len; + dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; + MZ_ASSERT(lookahead_size >= cur_match_len); + lookahead_size -= cur_match_len; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + + while (lookahead_size) + { + mz_uint8 lit = d->m_dict[cur_pos]; + + total_lz_bytes++; + *pLZ_code_buf++ = lit; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + if (--num_flags_left == 0) + { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + d->m_huff_count[0][lit]++; + + lookahead_pos++; + dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + lookahead_size--; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + } + + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + return MZ_TRUE; +} +#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ + +static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) +{ + d->m_total_lz_bytes++; + *d->m_pLZ_code_buf++ = lit; + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); + if (--d->m_num_flags_left == 0) + { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + d->m_huff_count[0][lit]++; +} + +static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) +{ + mz_uint32 s0, s1; + + MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); + + d->m_total_lz_bytes += match_len; + + d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); + + match_dist -= 1; + d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); + d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); + d->m_pLZ_code_buf += 3; + + *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); + if (--d->m_num_flags_left == 0) + { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; + d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; + d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; +} + +static mz_bool tdefl_compress_normal(tdefl_compressor *d) +{ + const mz_uint8 *pSrc = d->m_pSrc; + size_t src_buf_left = d->m_src_buf_left; + tdefl_flush flush = d->m_flush; + + while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) + { + mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; + /* Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. */ + if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) + { + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; + mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); + const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; + src_buf_left -= num_bytes_to_process; + d->m_lookahead_size += num_bytes_to_process; + while (pSrc != pSrc_end) + { + mz_uint8 c = *pSrc++; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + ins_pos++; + } + } + else + { + while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + { + mz_uint8 c = *pSrc++; + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + src_buf_left--; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) + { + mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; + mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + } + } + } + d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); + if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + break; + + /* Simple lazy/greedy parsing state machine. */ + len_to_move = 1; + cur_match_dist = 0; + cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); + cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) + { + if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) + { + mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; + cur_match_len = 0; + while (cur_match_len < d->m_lookahead_size) + { + if (d->m_dict[cur_pos + cur_match_len] != c) + break; + cur_match_len++; + } + if (cur_match_len < TDEFL_MIN_MATCH_LEN) + cur_match_len = 0; + else + cur_match_dist = 1; + } + } + else + { + tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); + } + if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) + { + cur_match_dist = cur_match_len = 0; + } + if (d->m_saved_match_len) + { + if (cur_match_len > d->m_saved_match_len) + { + tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); + if (cur_match_len >= 128) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + d->m_saved_match_len = 0; + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[cur_pos]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + } + else + { + tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); + len_to_move = d->m_saved_match_len - 1; + d->m_saved_match_len = 0; + } + } + else if (!cur_match_dist) + tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); + else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + /* Move the lookahead forward by len_to_move bytes. */ + d->m_lookahead_pos += len_to_move; + MZ_ASSERT(d->m_lookahead_size >= len_to_move); + d->m_lookahead_size -= len_to_move; + d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE); + /* Check if it's time to flush the current LZ codes to the internal output buffer. */ + if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || + ((d->m_total_lz_bytes > 31 * 1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))) + { + int n; + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + } + } + + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + return MZ_TRUE; +} + +static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) +{ + if (d->m_pIn_buf_size) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + } + + if (d->m_pOut_buf_size) + { + size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); + d->m_output_flush_ofs += (mz_uint)n; + d->m_output_flush_remaining -= (mz_uint)n; + d->m_out_buf_ofs += n; + + *d->m_pOut_buf_size = d->m_out_buf_ofs; + } + + return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) +{ + if (!d) + { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return TDEFL_STATUS_BAD_PARAM; + } + + d->m_pIn_buf = pIn_buf; + d->m_pIn_buf_size = pIn_buf_size; + d->m_pOut_buf = pOut_buf; + d->m_pOut_buf_size = pOut_buf_size; + d->m_pSrc = (const mz_uint8 *)(pIn_buf); + d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; + d->m_out_buf_ofs = 0; + d->m_flush = flush; + + if (((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || + (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf)) + { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); + } + d->m_wants_to_finish |= (flush == TDEFL_FINISH); + + if ((d->m_output_flush_remaining) || (d->m_finished)) + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && + ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && + ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) + { + if (!tdefl_compress_fast(d)) + return d->m_prev_return_status; + } + else +#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ + { + if (!tdefl_compress_normal(d)) + return d->m_prev_return_status; + } + + if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) + d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); + + if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) + { + if (tdefl_flush_block(d, flush) < 0) + return d->m_prev_return_status; + d->m_finished = (flush == TDEFL_FINISH); + if (flush == TDEFL_FULL_FLUSH) + { + MZ_CLEAR_OBJ(d->m_hash); + MZ_CLEAR_OBJ(d->m_next); + d->m_dict_size = 0; + } + } + + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); +} + +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) +{ + MZ_ASSERT(d->m_pPut_buf_func); + return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); +} + +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + d->m_pPut_buf_func = pPut_buf_func; + d->m_pPut_buf_user = pPut_buf_user; + d->m_flags = (mz_uint)(flags); + d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; + d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; + d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) + MZ_CLEAR_OBJ(d->m_hash); + d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; + d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + *d->m_pLZ_flags = 0; + d->m_num_flags_left = 8; + d->m_pOutput_buf = d->m_output_buf; + d->m_pOutput_buf_end = d->m_output_buf; + d->m_prev_return_status = TDEFL_STATUS_OKAY; + d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; + d->m_adler32 = 1; + d->m_pIn_buf = NULL; + d->m_pOut_buf = NULL; + d->m_pIn_buf_size = NULL; + d->m_pOut_buf_size = NULL; + d->m_flush = TDEFL_NO_FLUSH; + d->m_pSrc = NULL; + d->m_src_buf_left = 0; + d->m_out_buf_ofs = 0; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) + MZ_CLEAR_OBJ(d->m_dict); + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + return TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) +{ + return d->m_prev_return_status; +} + +mz_uint32 tdefl_get_adler32(tdefl_compressor *d) +{ + return d->m_adler32; +} + +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + tdefl_compressor *pComp; + mz_bool succeeded; + if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) + return MZ_FALSE; + pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); + if (!pComp) + return MZ_FALSE; + succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); + succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); + MZ_FREE(pComp); + return succeeded; +} + +typedef struct +{ + size_t m_size, m_capacity; + mz_uint8 *m_pBuf; + mz_bool m_expandable; +} tdefl_output_buffer; + +static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) +{ + tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; + size_t new_size = p->m_size + len; + if (new_size > p->m_capacity) + { + size_t new_capacity = p->m_capacity; + mz_uint8 *pNew_buf; + if (!p->m_expandable) + return MZ_FALSE; + do + { + new_capacity = MZ_MAX(128U, new_capacity << 1U); + } while (new_size > new_capacity); + pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity); + if (!pNew_buf) + return MZ_FALSE; + p->m_pBuf = pNew_buf; + p->m_capacity = new_capacity; + } + memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len); + p->m_size = new_size; + return MZ_TRUE; +} + +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_len) + return MZ_FALSE; + else + *pOut_len = 0; + out_buf.m_expandable = MZ_TRUE; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) + return NULL; + *pOut_len = out_buf.m_size; + return out_buf.m_pBuf; +} + +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +{ + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_buf) + return 0; + out_buf.m_pBuf = (mz_uint8 *)pOut_buf; + out_buf.m_capacity = out_buf_len; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) + return 0; + return out_buf.m_size; +} + +static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + +/* level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). */ +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) +{ + mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (window_bits > 0) + comp_flags |= TDEFL_WRITE_ZLIB_HEADER; + + if (!level) + comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + else if (strategy == MZ_FILTERED) + comp_flags |= TDEFL_FILTER_MATCHES; + else if (strategy == MZ_HUFFMAN_ONLY) + comp_flags &= ~TDEFL_MAX_PROBES_MASK; + else if (strategy == MZ_FIXED) + comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; + else if (strategy == MZ_RLE) + comp_flags |= TDEFL_RLE_MATCHES; + + return comp_flags; +} + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4204) /* nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) */ +#endif + +/* Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at + http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. + This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. */ +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) +{ + /* Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. */ + static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); + tdefl_output_buffer out_buf; + int i, bpl = w * num_chans, y, z; + mz_uint32 c; + *pLen_out = 0; + if (!pComp) + return NULL; + MZ_CLEAR_OBJ(out_buf); + out_buf.m_expandable = MZ_TRUE; + out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h); + if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity))) + { + MZ_FREE(pComp); + return NULL; + } + /* write dummy header */ + for (z = 41; z; --z) + tdefl_output_buffer_putter(&z, 1, &out_buf); + /* compress image data */ + tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); + for (y = 0; y < h; ++y) + { + tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); + tdefl_compress_buffer(pComp, (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); + } + if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) + { + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + /* write real header */ + *pLen_out = out_buf.m_size - 41; + { + static const mz_uint8 chans[] = { 0x00, 0x00, 0x04, 0x02, 0x06 }; + mz_uint8 pnghdr[41] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, + 0x0a, 0x1a, 0x0a, 0x00, 0x00, + 0x00, 0x0d, 0x49, 0x48, 0x44, + 0x52, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x44, 0x41, + 0x54 }; + pnghdr[18] = (mz_uint8)(w >> 8); + pnghdr[19] = (mz_uint8)w; + pnghdr[22] = (mz_uint8)(h >> 8); + pnghdr[23] = (mz_uint8)h; + pnghdr[25] = chans[num_chans]; + pnghdr[33] = (mz_uint8)(*pLen_out >> 24); + pnghdr[34] = (mz_uint8)(*pLen_out >> 16); + pnghdr[35] = (mz_uint8)(*pLen_out >> 8); + pnghdr[36] = (mz_uint8)*pLen_out; + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17); + for (i = 0; i < 4; ++i, c <<= 8) + ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24); + memcpy(out_buf.m_pBuf, pnghdr, 41); + } + /* write footer (IDAT CRC-32, followed by IEND chunk) */ + if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) + { + *pLen_out = 0; + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, *pLen_out + 4); + for (i = 0; i < 4; ++i, c <<= 8) + (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24); + /* compute final size of file, grab compressed data buffer and return */ + *pLen_out += 57; + MZ_FREE(pComp); + return out_buf.m_pBuf; +} +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) +{ + /* Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) */ + return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); +} + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */ +/* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +tdefl_compressor *tdefl_compressor_alloc() +{ + return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); +} + +void tdefl_compressor_free(tdefl_compressor *pComp) +{ + MZ_FREE(pComp); +} +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#ifdef __cplusplus +} +#endif + /************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- Low-level Decompression (completely independent from all compression API's) */ + +#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) +#define TINFL_MEMSET(p, c, l) memset(p, c, l) + +#define TINFL_CR_BEGIN \ + switch (r->m_state) \ + { \ + case 0: +#define TINFL_CR_RETURN(state_index, result) \ + do \ + { \ + status = result; \ + r->m_state = state_index; \ + goto common_exit; \ + case state_index:; \ + } \ + MZ_MACRO_END +#define TINFL_CR_RETURN_FOREVER(state_index, result) \ + do \ + { \ + for (;;) \ + { \ + TINFL_CR_RETURN(state_index, result); \ + } \ + } \ + MZ_MACRO_END +#define TINFL_CR_FINISH } + +#define TINFL_GET_BYTE(state_index, c) \ + do \ + { \ + while (pIn_buf_cur >= pIn_buf_end) \ + { \ + TINFL_CR_RETURN(state_index, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); \ + } \ + c = *pIn_buf_cur++; \ + } \ + MZ_MACRO_END + +#define TINFL_NEED_BITS(state_index, n) \ + do \ + { \ + mz_uint c; \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < (mz_uint)(n)) +#define TINFL_SKIP_BITS(state_index, n) \ + do \ + { \ + if (num_bits < (mz_uint)(n)) \ + { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END +#define TINFL_GET_BITS(state_index, b, n) \ + do \ + { \ + if (num_bits < (mz_uint)(n)) \ + { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + b = bit_buf & ((1 << (n)) - 1); \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END + +/* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. */ +/* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */ +/* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */ +/* bit buffer contains >=15 bits (deflate's max. Huffman code size). */ +#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ + do \ + { \ + temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ + if (temp >= 0) \ + { \ + code_len = temp >> 9; \ + if ((code_len) && (num_bits >= code_len)) \ + break; \ + } \ + else if (num_bits > TINFL_FAST_LOOKUP_BITS) \ + { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do \ + { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while ((temp < 0) && (num_bits >= (code_len + 1))); \ + if (temp >= 0) \ + break; \ + } \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < 15); + +/* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read */ +/* beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully */ +/* decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. */ +/* The slow path is only executed at the very end of the input buffer. */ +/* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we also need to handle the case where the user passes in 1+zillion bytes */ +/* following the deflate data and our non-conservative read-ahead path won't kick in here on this code. This is much trickier. */ +#define TINFL_HUFF_DECODE(state_index, sym, pHuff) \ + do \ + { \ + int temp; \ + mz_uint code_len, c; \ + if (num_bits < 15) \ + { \ + if ((pIn_buf_end - pIn_buf_cur) < 2) \ + { \ + TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ + } \ + else \ + { \ + bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \ + pIn_buf_cur += 2; \ + num_bits += 16; \ + } \ + } \ + if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ + code_len = temp >> 9, temp &= 511; \ + else \ + { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do \ + { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while (temp < 0); \ + } \ + sym = temp; \ + bit_buf >>= code_len; \ + num_bits -= code_len; \ + } \ + MZ_MACRO_END + +tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) +{ + static const int s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 }; + static const int s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; + static const int s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 }; + static const int s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; + static const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + static const int s_min_table_sizes[3] = { 257, 1, 4 }; + + tinfl_status status = TINFL_STATUS_FAILED; + mz_uint32 num_bits, dist, counter, num_extra; + tinfl_bit_buf_t bit_buf; + const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; + mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; + size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; + + /* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */ + if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) + { + *pIn_buf_size = *pOut_buf_size = 0; + return TINFL_STATUS_BAD_PARAM; + } + + num_bits = r->m_num_bits; + bit_buf = r->m_bit_buf; + dist = r->m_dist; + counter = r->m_counter; + num_extra = r->m_num_extra; + dist_from_out_buf_start = r->m_dist_from_out_buf_start; + TINFL_CR_BEGIN + + bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; + r->m_z_adler32 = r->m_check_adler32 = 1; + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + TINFL_GET_BYTE(1, r->m_zhdr0); + TINFL_GET_BYTE(2, r->m_zhdr1); + counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); + if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); + if (counter) + { + TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); + } + } + + do + { + TINFL_GET_BITS(3, r->m_final, 3); + r->m_type = r->m_final >> 1; + if (r->m_type == 0) + { + TINFL_SKIP_BITS(5, num_bits & 7); + for (counter = 0; counter < 4; ++counter) + { + if (num_bits) + TINFL_GET_BITS(6, r->m_raw_header[counter], 8); + else + TINFL_GET_BYTE(7, r->m_raw_header[counter]); + } + if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) + { + TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); + } + while ((counter) && (num_bits)) + { + TINFL_GET_BITS(51, dist, 8); + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)dist; + counter--; + } + while (counter) + { + size_t n; + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); + } + while (pIn_buf_cur >= pIn_buf_end) + { + TINFL_CR_RETURN(38, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); + } + n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); + TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); + pIn_buf_cur += n; + pOut_buf_cur += n; + counter -= (mz_uint)n; + } + } + else if (r->m_type == 3) + { + TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); + } + else + { + if (r->m_type == 1) + { + mz_uint8 *p = r->m_tables[0].m_code_size; + mz_uint i; + r->m_table_sizes[0] = 288; + r->m_table_sizes[1] = 32; + TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); + for (i = 0; i <= 143; ++i) + *p++ = 8; + for (; i <= 255; ++i) + *p++ = 9; + for (; i <= 279; ++i) + *p++ = 7; + for (; i <= 287; ++i) + *p++ = 8; + } + else + { + for (counter = 0; counter < 3; counter++) + { + TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); + r->m_table_sizes[counter] += s_min_table_sizes[counter]; + } + MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); + for (counter = 0; counter < r->m_table_sizes[2]; counter++) + { + mz_uint s; + TINFL_GET_BITS(14, s, 3); + r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; + } + r->m_table_sizes[2] = 19; + } + for (; (int)r->m_type >= 0; r->m_type--) + { + int tree_next, tree_cur; + tinfl_huff_table *pTable; + mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; + pTable = &r->m_tables[r->m_type]; + MZ_CLEAR_OBJ(total_syms); + MZ_CLEAR_OBJ(pTable->m_look_up); + MZ_CLEAR_OBJ(pTable->m_tree); + for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) + total_syms[pTable->m_code_size[i]]++; + used_syms = 0, total = 0; + next_code[0] = next_code[1] = 0; + for (i = 1; i <= 15; ++i) + { + used_syms += total_syms[i]; + next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); + } + if ((65536 != total) && (used_syms > 1)) + { + TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); + } + for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) + { + mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; + if (!code_size) + continue; + cur_code = next_code[code_size]++; + for (l = code_size; l > 0; l--, cur_code >>= 1) + rev_code = (rev_code << 1) | (cur_code & 1); + if (code_size <= TINFL_FAST_LOOKUP_BITS) + { + mz_int16 k = (mz_int16)((code_size << 9) | sym_index); + while (rev_code < TINFL_FAST_LOOKUP_SIZE) + { + pTable->m_look_up[rev_code] = k; + rev_code += (1 << code_size); + } + continue; + } + if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) + { + pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } + rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); + for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) + { + tree_cur -= ((rev_code >>= 1) & 1); + if (!pTable->m_tree[-tree_cur - 1]) + { + pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } + else + tree_cur = pTable->m_tree[-tree_cur - 1]; + } + tree_cur -= ((rev_code >>= 1) & 1); + pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; + } + if (r->m_type == 2) + { + for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);) + { + mz_uint s; + TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); + if (dist < 16) + { + r->m_len_codes[counter++] = (mz_uint8)dist; + continue; + } + if ((dist == 16) && (!counter)) + { + TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); + } + num_extra = "\02\03\07"[dist - 16]; + TINFL_GET_BITS(18, s, num_extra); + s += "\03\03\013"[dist - 16]; + TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); + counter += s; + } + if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) + { + TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); + } + TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); + TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); + } + } + for (;;) + { + mz_uint8 *pSrc; + for (;;) + { + if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) + { + TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); + if (counter >= 256) + break; + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)counter; + } + else + { + int sym2; + mz_uint code_len; +#if TINFL_USE_64BIT_BITBUF + if (num_bits < 30) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 4; + num_bits += 32; + } +#else + if (num_bits < 15) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; + do + { + sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + counter = sym2; + bit_buf >>= code_len; + num_bits -= code_len; + if (counter & 256) + break; + +#if !TINFL_USE_64BIT_BITBUF + if (num_bits < 15) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; + do + { + sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + bit_buf >>= code_len; + num_bits -= code_len; + + pOut_buf_cur[0] = (mz_uint8)counter; + if (sym2 & 256) + { + pOut_buf_cur++; + counter = sym2; + break; + } + pOut_buf_cur[1] = (mz_uint8)sym2; + pOut_buf_cur += 2; + } + } + if ((counter &= 511) == 256) + break; + + num_extra = s_length_extra[counter - 257]; + counter = s_length_base[counter - 257]; + if (num_extra) + { + mz_uint extra_bits; + TINFL_GET_BITS(25, extra_bits, num_extra); + counter += extra_bits; + } + + TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); + num_extra = s_dist_extra[dist]; + dist = s_dist_base[dist]; + if (num_extra) + { + mz_uint extra_bits; + TINFL_GET_BITS(27, extra_bits, num_extra); + dist += extra_bits; + } + + dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; + if ((dist == 0 || dist > dist_from_out_buf_start || dist_from_out_buf_start == 0) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + { + TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); + } + + pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); + + if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) + { + while (counter--) + { + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; + } + continue; + } +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + else if ((counter >= 9) && (counter <= dist)) + { + const mz_uint8 *pSrc_end = pSrc + (counter & ~7); + do + { +#ifdef MINIZ_UNALIGNED_USE_MEMCPY + memcpy(pOut_buf_cur, pSrc, sizeof(mz_uint32)*2); +#else + ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; + ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; +#endif + pOut_buf_cur += 8; + } while ((pSrc += 8) < pSrc_end); + if ((counter &= 7) < 3) + { + if (counter) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + continue; + } + } +#endif + while(counter>2) + { + pOut_buf_cur[0] = pSrc[0]; + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur[2] = pSrc[2]; + pOut_buf_cur += 3; + pSrc += 3; + counter -= 3; + } + if (counter > 0) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + } + } + } while (!(r->m_final & 1)); + + /* Ensure byte alignment and put back any bytes from the bitbuf if we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ + /* I'm being super conservative here. A number of simplifications can be made to the byte alignment part, and the Adler32 check shouldn't ever need to worry about reading from the bitbuf now. */ + TINFL_SKIP_BITS(32, num_bits & 7); + while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) + { + --pIn_buf_cur; + num_bits -= 8; + } + bit_buf &= (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); + MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */ + + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + for (counter = 0; counter < 4; ++counter) + { + mz_uint s; + if (num_bits) + TINFL_GET_BITS(41, s, 8); + else + TINFL_GET_BYTE(42, s); + r->m_z_adler32 = (r->m_z_adler32 << 8) | s; + } + } + TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); + + TINFL_CR_FINISH + +common_exit: + /* As long as we aren't telling the caller that we NEED more input to make forward progress: */ + /* Put back any bytes from the bitbuf in case we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ + /* We need to be very careful here to NOT push back any bytes we definitely know we need to make forward progress, though, or we'll lock the caller up into an inf loop. */ + if ((status != TINFL_STATUS_NEEDS_MORE_INPUT) && (status != TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS)) + { + while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) + { + --pIn_buf_cur; + num_bits -= 8; + } + } + r->m_num_bits = num_bits; + r->m_bit_buf = bit_buf & (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); + r->m_dist = dist; + r->m_counter = counter; + r->m_num_extra = num_extra; + r->m_dist_from_out_buf_start = dist_from_out_buf_start; + *pIn_buf_size = pIn_buf_cur - pIn_buf_next; + *pOut_buf_size = pOut_buf_cur - pOut_buf_next; + if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) + { + const mz_uint8 *ptr = pOut_buf_next; + size_t buf_len = *pOut_buf_size; + mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; + size_t block_len = buf_len % 5552; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; + } + for (; i < block_len; ++i) + s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; + } + r->m_check_adler32 = (s2 << 16) + s1; + if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) + status = TINFL_STATUS_ADLER32_MISMATCH; + } + return status; +} + +/* Higher level helper functions. */ +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tinfl_decompressor decomp; + void *pBuf = NULL, *pNew_buf; + size_t src_buf_ofs = 0, out_buf_capacity = 0; + *pOut_len = 0; + tinfl_init(&decomp); + for (;;) + { + size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, &dst_buf_size, + (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) + { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + src_buf_ofs += src_buf_size; + *pOut_len += dst_buf_size; + if (status == TINFL_STATUS_DONE) + break; + new_out_buf_capacity = out_buf_capacity * 2; + if (new_out_buf_capacity < 128) + new_out_buf_capacity = 128; + pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); + if (!pNew_buf) + { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + pBuf = pNew_buf; + out_buf_capacity = new_out_buf_capacity; + } + return pBuf; +} + +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +{ + tinfl_decompressor decomp; + tinfl_status status; + tinfl_init(&decomp); + status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; +} + +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + int result = 0; + tinfl_decompressor decomp; + mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE); + size_t in_buf_ofs = 0, dict_ofs = 0; + if (!pDict) + return TINFL_STATUS_FAILED; + tinfl_init(&decomp); + for (;;) + { + size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, + (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); + in_buf_ofs += in_buf_size; + if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) + break; + if (status != TINFL_STATUS_HAS_MORE_OUTPUT) + { + result = (status == TINFL_STATUS_DONE); + break; + } + dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); + } + MZ_FREE(pDict); + *pIn_buf_size = in_buf_ofs; + return result; +} + +#ifndef MINIZ_NO_MALLOC +tinfl_decompressor *tinfl_decompressor_alloc() +{ + tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor)); + if (pDecomp) + tinfl_init(pDecomp); + return pDecomp; +} + +void tinfl_decompressor_free(tinfl_decompressor *pDecomp) +{ + MZ_FREE(pDecomp); +} +#endif + +#ifdef __cplusplus +} +#endif + /************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * Copyright 2016 Martin Raiber + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- .ZIP archive reading */ + +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include + +#if defined(_MSC_VER) || defined(__MINGW64__) +static FILE *mz_fopen(const char *pFilename, const char *pMode) +{ + FILE *pFile = NULL; + fopen_s(&pFile, pFilename, pMode); + return pFile; +} +static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) +{ + FILE *pFile = NULL; + if (freopen_s(&pFile, pPath, pMode, pStream)) + return NULL; + return pFile; +} +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN mz_fopen +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 _ftelli64 +#define MZ_FSEEK64 _fseeki64 +#define MZ_FILE_STAT_STRUCT _stat64 +#define MZ_FILE_STAT _stat64 +#define MZ_FFLUSH fflush +#define MZ_FREOPEN mz_freopen +#define MZ_DELETE_FILE remove +#elif defined(__MINGW32__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello64 +#define MZ_FSEEK64 fseeko64 +#define MZ_FILE_STAT_STRUCT _stat +#define MZ_FILE_STAT _stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#elif defined(__TINYC__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#elif defined(__USE_LARGEFILE64) /* gcc, clang */ +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen64(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello64 +#define MZ_FSEEK64 fseeko64 +#define MZ_FILE_STAT_STRUCT stat64 +#define MZ_FILE_STAT stat64 +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(p, m, s) freopen64(p, m, s) +#define MZ_DELETE_FILE remove +#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(p, m, s) freopen(p, m, s) +#define MZ_DELETE_FILE remove + +#else +#if !defined(__MSYS__) && !defined(__CYGWIN__) +#pragma message("Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.") +#endif +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#ifdef __STRICT_ANSI__ +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#else +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#endif +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#endif /* #ifdef _MSC_VER */ +#endif /* #ifdef MINIZ_NO_STDIO */ + +#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) + +/* Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. */ +enum +{ + /* ZIP archive identifiers and record sizes */ + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, + MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, + + /* ZIP64 archive identifier and record sizes */ + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20, + MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001, + MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50, + MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24, + MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16, + + /* Central directory header record offsets */ + MZ_ZIP_CDH_SIG_OFS = 0, + MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, + MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, + MZ_ZIP_CDH_BIT_FLAG_OFS = 8, + MZ_ZIP_CDH_METHOD_OFS = 10, + MZ_ZIP_CDH_FILE_TIME_OFS = 12, + MZ_ZIP_CDH_FILE_DATE_OFS = 14, + MZ_ZIP_CDH_CRC32_OFS = 16, + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, + MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, + MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, + MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, + MZ_ZIP_CDH_DISK_START_OFS = 34, + MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, + MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, + + /* Local directory header offsets */ + MZ_ZIP_LDH_SIG_OFS = 0, + MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, + MZ_ZIP_LDH_BIT_FLAG_OFS = 6, + MZ_ZIP_LDH_METHOD_OFS = 8, + MZ_ZIP_LDH_FILE_TIME_OFS = 10, + MZ_ZIP_LDH_FILE_DATE_OFS = 12, + MZ_ZIP_LDH_CRC32_OFS = 14, + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, + MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, + MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, + MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR = 1 << 3, + + /* End of central directory offsets */ + MZ_ZIP_ECDH_SIG_OFS = 0, + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, + MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, + MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, + MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, + + /* ZIP64 End of central directory locator offsets */ + MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */ + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */ + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */ + + /* ZIP64 End of central directory header offsets */ + MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */ + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */ + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */ + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */ + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */ + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */ + MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0, + MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11 +}; + +typedef struct +{ + void *m_p; + size_t m_size, m_capacity; + mz_uint m_element_size; +} mz_zip_array; + +struct mz_zip_internal_state_tag +{ + mz_zip_array m_central_dir; + mz_zip_array m_central_dir_offsets; + mz_zip_array m_sorted_central_dir_offsets; + + /* The flags passed in when the archive is initially opened. */ + uint32_t m_init_flags; + + /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. */ + mz_bool m_zip64; + + /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 will also be slammed to true too, even if we didn't find a zip64 end of central dir header, etc.) */ + mz_bool m_zip64_has_extended_info_fields; + + /* These fields are used by the file, FILE, memory, and memory/heap read/write helpers. */ + MZ_FILE *m_pFile; + mz_uint64 m_file_archive_start_ofs; + + void *m_pMem; + size_t m_mem_size; + size_t m_mem_capacity; +}; + +#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size + +#if defined(DEBUG) || defined(_DEBUG) +static MZ_FORCEINLINE mz_uint mz_zip_array_range_check(const mz_zip_array *pArray, mz_uint index) +{ + MZ_ASSERT(index < pArray->m_size); + return index; +} +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[mz_zip_array_range_check(array_ptr, index)] +#else +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] +#endif + +static MZ_FORCEINLINE void mz_zip_array_init(mz_zip_array *pArray, mz_uint32 element_size) +{ + memset(pArray, 0, sizeof(mz_zip_array)); + pArray->m_element_size = element_size; +} + +static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) +{ + pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); + memset(pArray, 0, sizeof(mz_zip_array)); +} + +static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) +{ + void *pNew_p; + size_t new_capacity = min_new_capacity; + MZ_ASSERT(pArray->m_element_size); + if (pArray->m_capacity >= min_new_capacity) + return MZ_TRUE; + if (growing) + { + new_capacity = MZ_MAX(1, pArray->m_capacity); + while (new_capacity < min_new_capacity) + new_capacity *= 2; + } + if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) + return MZ_FALSE; + pArray->m_p = pNew_p; + pArray->m_capacity = new_capacity; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) +{ + if (new_capacity > pArray->m_capacity) + { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) + return MZ_FALSE; + } + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) +{ + if (new_size > pArray->m_capacity) + { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) + return MZ_FALSE; + } + pArray->m_size = new_size; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) +{ + return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) +{ + size_t orig_size = pArray->m_size; + if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) + return MZ_FALSE; + if (n > 0) + memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); + return MZ_TRUE; +} + +#ifndef MINIZ_NO_TIME +static MZ_TIME_T mz_zip_dos_to_time_t(int dos_time, int dos_date) +{ + struct tm tm; + memset(&tm, 0, sizeof(tm)); + tm.tm_isdst = -1; + tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; + tm.tm_mon = ((dos_date >> 5) & 15) - 1; + tm.tm_mday = dos_date & 31; + tm.tm_hour = (dos_time >> 11) & 31; + tm.tm_min = (dos_time >> 5) & 63; + tm.tm_sec = (dos_time << 1) & 62; + return mktime(&tm); +} + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static void mz_zip_time_t_to_dos_time(MZ_TIME_T time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +{ +#ifdef _MSC_VER + struct tm tm_struct; + struct tm *tm = &tm_struct; + errno_t err = localtime_s(tm, &time); + if (err) + { + *pDOS_date = 0; + *pDOS_time = 0; + return; + } +#else + struct tm *tm = localtime(&time); +#endif /* #ifdef _MSC_VER */ + + *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); + *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); +} +#endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */ + +#ifndef MINIZ_NO_STDIO +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static mz_bool mz_zip_get_file_modified_time(const char *pFilename, MZ_TIME_T *pTime) +{ + struct MZ_FILE_STAT_STRUCT file_stat; + + /* On Linux with x86 glibc, this call will fail on large files (I think >= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. */ + if (MZ_FILE_STAT(pFilename, &file_stat) != 0) + return MZ_FALSE; + + *pTime = file_stat.st_mtime; + + return MZ_TRUE; +} +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/ + +static mz_bool mz_zip_set_file_times(const char *pFilename, MZ_TIME_T access_time, MZ_TIME_T modified_time) +{ + struct utimbuf t; + + memset(&t, 0, sizeof(t)); + t.actime = access_time; + t.modtime = modified_time; + + return !utime(pFilename, &t); +} +#endif /* #ifndef MINIZ_NO_STDIO */ +#endif /* #ifndef MINIZ_NO_TIME */ + +static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, mz_zip_error err_num) +{ + if (pZip) + pZip->m_last_error = err_num; + return MZ_FALSE; +} + +static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint flags) +{ + (void)flags; + if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!pZip->m_pAlloc) + pZip->m_pAlloc = miniz_def_alloc_func; + if (!pZip->m_pFree) + pZip->m_pFree = miniz_def_free_func; + if (!pZip->m_pRealloc) + pZip->m_pRealloc = miniz_def_realloc_func; + + pZip->m_archive_size = 0; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + pZip->m_last_error = MZ_ZIP_NO_ERROR; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + pZip->m_pState->m_init_flags = flags; + pZip->m_pState->m_zip64 = MZ_FALSE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_FALSE; + + pZip->m_zip_mode = MZ_ZIP_MODE_READING; + + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; + pR++; + } + return (pL == pE) ? (l_len < r_len) : (l < r); +} + +#define MZ_SWAP_UINT32(a, b) \ + do \ + { \ + mz_uint32 t = a; \ + a = b; \ + b = t; \ + } \ + MZ_MACRO_END + +/* Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) */ +static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices; + mz_uint32 start, end; + const mz_uint32 size = pZip->m_total_files; + + if (size <= 1U) + return; + + pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + + start = (size - 2U) >> 1U; + for (;;) + { + mz_uint64 child, root = start; + for (;;) + { + if ((child = (root << 1U) + 1U) >= size) + break; + child += (((child + 1U) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U]))); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + if (!start) + break; + start--; + } + + end = size - 1; + while (end > 0) + { + mz_uint64 child, root = 0; + MZ_SWAP_UINT32(pIndices[end], pIndices[0]); + for (;;) + { + if ((child = (root << 1U) + 1U) >= end) + break; + child += (((child + 1U) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U])); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + end--; + } +} + +static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, mz_uint32 record_sig, mz_uint32 record_size, mz_int64 *pOfs) +{ + mz_int64 cur_file_ofs; + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + + /* Basic sanity checks - reject files which are too small */ + if (pZip->m_archive_size < record_size) + return MZ_FALSE; + + /* Find the record by scanning the file from the end towards the beginning. */ + cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); + for (;;) + { + int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); + + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) + return MZ_FALSE; + + for (i = n - 4; i >= 0; --i) + { + mz_uint s = MZ_READ_LE32(pBuf + i); + if (s == record_sig) + { + if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size) + break; + } + } + + if (i >= 0) + { + cur_file_ofs += i; + break; + } + + /* Give up if we've searched the entire file, or we've gone back "too far" (~64kb) */ + if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (MZ_UINT16_MAX + record_size))) + return MZ_FALSE; + + cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); + } + + *pOfs = cur_file_ofs; + return MZ_TRUE; +} + +static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flags) +{ + mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, cdir_disk_index = 0; + mz_uint64 cdir_ofs = 0; + mz_int64 cur_file_ofs = 0; + const mz_uint8 *p; + + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); + mz_uint32 zip64_end_of_central_dir_locator_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32; + + mz_uint32 zip64_end_of_central_dir_header_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pZip64_end_of_central_dir = (mz_uint8 *)zip64_end_of_central_dir_header_u32; + + mz_uint64 zip64_end_of_central_dir_ofs = 0; + + /* Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. */ + if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_locate_header_sig(pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs)) + return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR); + + /* Read and verify the end of central directory record. */ + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) + { + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, pZip64_locator, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) + { + if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) + { + zip64_end_of_central_dir_ofs = MZ_READ_LE64(pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS); + if (zip64_end_of_central_dir_ofs > (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, pZip64_end_of_central_dir, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) + { + if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) + { + pZip->m_pState->m_zip64 = MZ_TRUE; + } + } + } + } + } + + pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS); + cdir_entries_on_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); + cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); + cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS); + cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); + + if (pZip->m_pState->m_zip64) + { + mz_uint32 zip64_total_num_of_disks = MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS); + mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS); + mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS); + mz_uint64 zip64_size_of_central_directory = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS); + + if (zip64_size_of_end_of_central_dir_record < (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (zip64_total_num_of_disks != 1U) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + /* Check for miniz's practical limits */ + if (zip64_cdir_total_entries > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries; + + if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + cdir_entries_on_this_disk = (mz_uint32)zip64_cdir_total_entries_on_this_disk; + + /* Check for miniz's current practical limits (sorry, this should be enough for millions of files) */ + if (zip64_size_of_central_directory > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + cdir_size = (mz_uint32)zip64_size_of_central_directory; + + num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS); + + cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS); + + cdir_ofs = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS); + } + + if (pZip->m_total_files != cdir_entries_on_this_disk) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pZip->m_central_directory_file_ofs = cdir_ofs; + + if (pZip->m_total_files) + { + mz_uint i, n; + /* Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and possibly another to hold the sorted indices. */ + if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || + (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (sort_central_dir) + { + if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + /* Now create an index into the central directory file records, do some basic sanity checking on each record */ + p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; + for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) + { + mz_uint total_header_size, disk_index, bit_flags, filename_size, ext_data_size; + mz_uint64 comp_size, decomp_size, local_header_ofs; + + if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); + + if (sort_central_dir) + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if ((!pZip->m_pState->m_zip64_has_extended_info_fields) && + (ext_data_size) && + (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == MZ_UINT32_MAX)) + { + /* Attempt to find zip64 extended information field in the entry's extra data */ + mz_uint32 extra_size_remaining = ext_data_size; + + if (extra_size_remaining) + { + const mz_uint8 *pExtra_data; + void* buf = NULL; + + if (MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + ext_data_size > n) + { + buf = MZ_MALLOC(ext_data_size); + if(buf==NULL) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size, buf, ext_data_size) != ext_data_size) + { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + pExtra_data = (mz_uint8*)buf; + } + else + { + pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size; + } + + do + { + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) + { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + /* Ok, the archive didn't have any zip64 headers but it uses a zip64 extended information field so mark it as zip64 anyway (this can occur with infozip's zip util when it reads compresses files from stdin). */ + pZip->m_pState->m_zip64 = MZ_TRUE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE; + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); + + MZ_FREE(buf); + } + } + + /* I've seen archives that aren't marked as zip64 that uses zip64 ext data, argh */ + if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) + { + if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); + if ((disk_index == MZ_UINT16_MAX) || ((disk_index != num_this_disk) && (disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (comp_size != MZ_UINT32_MAX) + { + if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + n -= total_header_size; + p += total_header_size; + } + } + + if (sort_central_dir) + mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); + + return MZ_TRUE; +} + +void mz_zip_zero_struct(mz_zip_archive *pZip) +{ + if (pZip) + MZ_CLEAR_OBJ(*pZip); +} + +static mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) +{ + mz_bool status = MZ_TRUE; + + if (!pZip) + return MZ_FALSE; + + if ((!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + { + if (set_last_error) + pZip->m_last_error = MZ_ZIP_INVALID_PARAMETER; + + return MZ_FALSE; + } + + if (pZip->m_pState) + { + mz_zip_internal_state *pState = pZip->m_pState; + pZip->m_pState = NULL; + + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (MZ_FCLOSE(pState->m_pFile) == EOF) + { + if (set_last_error) + pZip->m_last_error = MZ_ZIP_FILE_CLOSE_FAILED; + status = MZ_FALSE; + } + } + pState->m_pFile = NULL; + } +#endif /* #ifndef MINIZ_NO_STDIO */ + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + } + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + + return status; +} + +mz_bool mz_zip_reader_end(mz_zip_archive *pZip) +{ + return mz_zip_reader_end_internal(pZip, MZ_TRUE); +} +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags) +{ + if ((!pZip) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_USER; + pZip->m_archive_size = size; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); + memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); + return s; +} + +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags) +{ + if (!pMem) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_MEMORY; + pZip->m_archive_size = size; + pZip->m_pRead = mz_zip_mem_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pNeeds_keepalive = NULL; + +#ifdef __cplusplus + pZip->m_pState->m_pMem = const_cast(pMem); +#else + pZip->m_pState->m_pMem = (void *)pMem; +#endif + + pZip->m_pState->m_mem_size = size; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + + file_ofs += pZip->m_pState->m_file_archive_start_ofs; + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + + return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) +{ + return mz_zip_reader_init_file_v2(pZip, pFilename, flags, 0, 0); +} + +mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size) +{ + mz_uint64 file_size; + MZ_FILE *pFile; + + if ((!pZip) || (!pFilename) || ((archive_size) && (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pFile = MZ_FOPEN(pFilename, "rb"); + if (!pFile) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + file_size = archive_size; + if (!file_size) + { + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + { + MZ_FCLOSE(pFile); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + } + + file_size = MZ_FTELL64(pFile); + } + + /* TODO: Better sanity check archive_size and the # of actual remaining bytes */ + + if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + { + MZ_FCLOSE(pFile); + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + } + + if (!mz_zip_reader_init_internal(pZip, flags)) + { + MZ_FCLOSE(pFile); + return MZ_FALSE; + } + + pZip->m_zip_type = MZ_ZIP_TYPE_FILE; + pZip->m_pRead = mz_zip_file_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = file_size; + pZip->m_pState->m_file_archive_start_ofs = file_start_ofs; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags) +{ + mz_uint64 cur_file_ofs; + + if ((!pZip) || (!pFile)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + cur_file_ofs = MZ_FTELL64(pFile); + + if (!archive_size) + { + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + + archive_size = MZ_FTELL64(pFile) - cur_file_ofs; + + if (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + } + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = archive_size; + pZip->m_pState->m_file_archive_start_ofs = cur_file_ofs; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +#endif /* #ifndef MINIZ_NO_STDIO */ + +static MZ_FORCEINLINE const mz_uint8 *mz_zip_get_cdh(mz_zip_archive *pZip, mz_uint file_index) +{ + if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files)) + return NULL; + return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); +} + +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint m_bit_flag; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + return (m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) != 0; +} + +mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint bit_flag; + mz_uint method; + + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); + bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + + if ((method != 0) && (method != MZ_DEFLATED)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + return MZ_FALSE; + } + + if (bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + return MZ_FALSE; + } + + if (bit_flag & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint filename_len, attribute_mapping_id, external_attr; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_len) + { + if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') + return MZ_TRUE; + } + + /* Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. */ + /* Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. */ + /* FIXME: Remove this check? Is it necessary - we already check the filename. */ + attribute_mapping_id = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS) >> 8; + (void)attribute_mapping_id; + + external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + if ((external_attr & MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG) != 0) + { + return MZ_TRUE; + } + + return MZ_FALSE; +} + +static mz_bool mz_zip_file_stat_internal(mz_zip_archive *pZip, mz_uint file_index, const mz_uint8 *pCentral_dir_header, mz_zip_archive_file_stat *pStat, mz_bool *pFound_zip64_extra_data) +{ + mz_uint n; + const mz_uint8 *p = pCentral_dir_header; + + if (pFound_zip64_extra_data) + *pFound_zip64_extra_data = MZ_FALSE; + + if ((!p) || (!pStat)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Extract fields from the central directory record. */ + pStat->m_file_index = file_index; + pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); + pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); + pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); + pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); +#ifndef MINIZ_NO_TIME + pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); +#endif + pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); + pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); + pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + + /* Copy as much of the filename and comment as possible. */ + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); + memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pStat->m_filename[n] = '\0'; + + n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); + pStat->m_comment_size = n; + memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); + pStat->m_comment[n] = '\0'; + + /* Set some flags for convienance */ + pStat->m_is_directory = mz_zip_reader_is_file_a_directory(pZip, file_index); + pStat->m_is_encrypted = mz_zip_reader_is_file_encrypted(pZip, file_index); + pStat->m_is_supported = mz_zip_reader_is_file_supported(pZip, file_index); + + /* See if we need to read any zip64 extended information fields. */ + /* Confusingly, these zip64 fields can be present even on non-zip64 archives (Debian zip on a huge files from stdin piped to stdout creates them). */ + if (MZ_MAX(MZ_MAX(pStat->m_comp_size, pStat->m_uncomp_size), pStat->m_local_header_ofs) == MZ_UINT32_MAX) + { + /* Attempt to find zip64 extended information field in the entry's extra data */ + mz_uint32 extra_size_remaining = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if (extra_size_remaining) + { + const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + + do + { + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pField_data = pExtra_data + sizeof(mz_uint16) * 2; + mz_uint32 field_data_remaining = field_data_size; + + if (pFound_zip64_extra_data) + *pFound_zip64_extra_data = MZ_TRUE; + + if (pStat->m_uncomp_size == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_uncomp_size = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + if (pStat->m_comp_size == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_comp_size = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + if (pStat->m_local_header_ofs == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_local_header_ofs = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); + } + } + + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) +{ + mz_uint i; + if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) + return 0 == memcmp(pA, pB, len); + for (i = 0; i < len; ++i) + if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) + return MZ_FALSE; + return MZ_TRUE; +} + +static MZ_FORCEINLINE int mz_zip_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; + pR++; + } + return (pL == pE) ? (int)(l_len - r_len) : (l - r); +} + +static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename, mz_uint32 *pIndex) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const uint32_t size = pZip->m_total_files; + const mz_uint filename_len = (mz_uint)strlen(pFilename); + + if (pIndex) + *pIndex = 0; + + if (size) + { + /* yes I could use uint32_t's, but then we would have to add some special case checks in the loop, argh, and */ + /* honestly the major expense here on 32-bit CPU's will still be the filename compare */ + mz_int64 l = 0, h = (mz_int64)size - 1; + + while (l <= h) + { + mz_int64 m = l + ((h - l) >> 1); + uint32_t file_index = pIndices[(uint32_t)m]; + + int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); + if (!comp) + { + if (pIndex) + *pIndex = file_index; + return MZ_TRUE; + } + else if (comp < 0) + l = m + 1; + else + h = m - 1; + } + } + + return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +} + +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) +{ + mz_uint32 index; + if (!mz_zip_reader_locate_file_v2(pZip, pName, pComment, flags, &index)) + return -1; + else + return (int)index; +} + +mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex) +{ + mz_uint file_index; + size_t name_len, comment_len; + + if (pIndex) + *pIndex = 0; + + if ((!pZip) || (!pZip->m_pState) || (!pName)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* See if we can use a binary search */ + if (((pZip->m_pState->m_init_flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) && + (pZip->m_zip_mode == MZ_ZIP_MODE_READING) && + ((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) + { + return mz_zip_locate_file_binary_search(pZip, pName, pIndex); + } + + /* Locate the entry by scanning the entire central directory */ + name_len = strlen(pName); + if (name_len > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + comment_len = pComment ? strlen(pComment) : 0; + if (comment_len > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + for (file_index = 0; file_index < pZip->m_total_files; file_index++) + { + const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); + mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); + const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + if (filename_len < name_len) + continue; + if (comment_len) + { + mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); + const char *pFile_comment = pFilename + filename_len + file_extra_len; + if ((file_comment_len != comment_len) || (!mz_zip_string_equal(pComment, pFile_comment, file_comment_len, flags))) + continue; + } + if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) + { + int ofs = filename_len - 1; + do + { + if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) + break; + } while (--ofs >= 0); + ofs++; + pFilename += ofs; + filename_len -= ofs; + } + if ((filename_len == name_len) && (mz_zip_string_equal(pName, pFilename, filename_len, flags))) + { + if (pIndex) + *pIndex = file_index; + return MZ_TRUE; + } + } + + return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +} + +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + int status = TINFL_STATUS_DONE; + mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + tinfl_decompressor inflator; + + if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + /* Ensure supplied output buffer is large enough. */ + needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; + if (buf_size < needed_size) + return mz_zip_set_error(pZip, MZ_ZIP_BUF_TOO_SMALL); + + /* Read and parse the local directory entry. */ + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data. */ + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) == 0) + { + if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) + return mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); + } +#endif + + return MZ_TRUE; + } + + /* Decompress the file either directly from memory or from a file input buffer. */ + tinfl_init(&inflator); + + if (pZip->m_pState->m_pMem) + { + /* Read directly from the archive in memory. */ + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else if (pUser_read_buf) + { + /* Use a user provided read buffer. */ + if (!user_read_buf_size) + return MZ_FALSE; + pRead_buf = (mz_uint8 *)pUser_read_buf; + read_buf_size = user_read_buf_size; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + else + { + /* Temporarily allocate a read buffer. */ + read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + do + { + /* The size_t cast here should be OK because we've verified that the output buffer is >= file_stat.m_uncomp_size above */ + size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + out_buf_ofs += out_buf_size; + } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); + + if (status == TINFL_STATUS_DONE) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (out_buf_ofs != file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); + status = TINFL_STATUS_FAILED; + } +#endif + } + + if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return MZ_FALSE; + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); +} + +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); +} + +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); +} + +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) +{ + mz_uint64 comp_size, uncomp_size, alloc_size; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + void *pBuf; + + if (pSize) + *pSize = 0; + + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return NULL; + } + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + + alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; + if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) + { + mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + return NULL; + } + + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return NULL; + } + + if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return NULL; + } + + if (pSize) + *pSize = (size_t)alloc_size; + return pBuf; +} + +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + { + if (pSize) + *pSize = 0; + return MZ_FALSE; + } + return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); +} + +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + int status = TINFL_STATUS_DONE; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + mz_uint file_crc32 = MZ_CRC32_INIT; +#endif + mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf = NULL; + void *pWrite_buf = NULL; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + if ((!pZip) || (!pZip->m_pState) || (!pCallback) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + /* Read and do some minimal validation of the local directory entry (this doesn't crack the zip64 stuff, which we already have from the central dir) */ + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + /* Decompress the file either directly from memory or from a file input buffer. */ + if (pZip->m_pState->m_pMem) + { + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data. */ + if (pZip->m_pState->m_pMem) + { + if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + } + else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); +#endif + } + + cur_file_ofs += file_stat.m_comp_size; + out_buf_ofs += file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + while (comp_remaining) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); + } +#endif + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + + cur_file_ofs += read_buf_avail; + out_buf_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + } + } + } + else + { + tinfl_decompressor inflator; + tinfl_init(&inflator); + + if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + status = TINFL_STATUS_FAILED; + } + else + { + do + { + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + + if (out_buf_size) + { + if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); +#endif + if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + } + } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); + } + } + + if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (out_buf_ofs != file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (file_crc32 != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + status = TINFL_STATUS_FAILED; + } +#endif + } + + if (!pZip->m_pState->m_pMem) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + if (pWrite_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); +} + +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) +{ + mz_zip_reader_extract_iter_state *pState; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + /* Argument sanity check */ + if ((!pZip) || (!pZip->m_pState)) + return NULL; + + /* Allocate an iterator status structure */ + pState = (mz_zip_reader_extract_iter_state*)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_reader_extract_iter_state)); + if (!pState) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return NULL; + } + + /* Fetch file details */ + if (!mz_zip_reader_file_stat(pZip, file_index, &pState->file_stat)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Encryption and patch files are not supported. */ + if (pState->file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (pState->file_stat.m_method != 0) && (pState->file_stat.m_method != MZ_DEFLATED)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Init state - save args */ + pState->pZip = pZip; + pState->flags = flags; + + /* Init state - reset variables to defaults */ + pState->status = TINFL_STATUS_DONE; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + pState->file_crc32 = MZ_CRC32_INIT; +#endif + pState->read_buf_ofs = 0; + pState->out_buf_ofs = 0; + pState->pRead_buf = NULL; + pState->pWrite_buf = NULL; + pState->out_blk_remain = 0; + + /* Read and parse the local directory entry. */ + pState->cur_file_ofs = pState->file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, pState->cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + pState->cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((pState->cur_file_ofs + pState->file_stat.m_comp_size) > pZip->m_archive_size) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Decompress the file either directly from memory or from a file input buffer. */ + if (pZip->m_pState->m_pMem) + { + pState->pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + pState->cur_file_ofs; + pState->read_buf_size = pState->read_buf_avail = pState->file_stat.m_comp_size; + pState->comp_remaining = pState->file_stat.m_comp_size; + } + else + { + if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) + { + /* Decompression required, therefore intermediate read buffer required */ + pState->read_buf_size = MZ_MIN(pState->file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pState->pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)pState->read_buf_size))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + } + else + { + /* Decompression not required - we will be reading directly into user buffer, no temp buf required */ + pState->read_buf_size = 0; + } + pState->read_buf_avail = 0; + pState->comp_remaining = pState->file_stat.m_comp_size; + } + + if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) + { + /* Decompression required, init decompressor */ + tinfl_init( &pState->inflator ); + + /* Allocate write buffer */ + if (NULL == (pState->pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + if (pState->pRead_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->pRead_buf); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + } + + return pState; +} + +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) +{ + mz_uint32 file_index; + + /* Locate file index by name */ + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return NULL; + + /* Construct iterator */ + return mz_zip_reader_extract_iter_new(pZip, file_index, flags); +} + +size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size) +{ + size_t copied_to_caller = 0; + + /* Argument sanity check */ + if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState) || (!pvBuf)) + return 0; + + if ((pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data, calc amount to return. */ + copied_to_caller = (size_t)MZ_MIN( buf_size, pState->comp_remaining ); + + /* Zip is in memory....or requires reading from a file? */ + if (pState->pZip->m_pState->m_pMem) + { + /* Copy data to caller's buffer */ + memcpy( pvBuf, pState->pRead_buf, copied_to_caller ); + pState->pRead_buf = ((mz_uint8*)pState->pRead_buf) + copied_to_caller; + } + else + { + /* Read directly into caller's buffer */ + if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pvBuf, copied_to_caller) != copied_to_caller) + { + /* Failed to read all that was asked for, flag failure and alert user */ + mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); + pState->status = TINFL_STATUS_FAILED; + copied_to_caller = 0; + } + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + /* Compute CRC if not returning compressed data only */ + if (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, (const mz_uint8 *)pvBuf, copied_to_caller); +#endif + + /* Advance offsets, dec counters */ + pState->cur_file_ofs += copied_to_caller; + pState->out_buf_ofs += copied_to_caller; + pState->comp_remaining -= copied_to_caller; + } + else + { + do + { + /* Calc ptr to write buffer - given current output pos and block size */ + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pState->pWrite_buf + (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + + /* Calc max output size - given current output pos and block size */ + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + + if (!pState->out_blk_remain) + { + /* Read more data from file if none available (and reading from file) */ + if ((!pState->read_buf_avail) && (!pState->pZip->m_pState->m_pMem)) + { + /* Calc read size */ + pState->read_buf_avail = MZ_MIN(pState->read_buf_size, pState->comp_remaining); + if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pState->pRead_buf, (size_t)pState->read_buf_avail) != pState->read_buf_avail) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); + pState->status = TINFL_STATUS_FAILED; + break; + } + + /* Advance offsets, dec counters */ + pState->cur_file_ofs += pState->read_buf_avail; + pState->comp_remaining -= pState->read_buf_avail; + pState->read_buf_ofs = 0; + } + + /* Perform decompression */ + in_buf_size = (size_t)pState->read_buf_avail; + pState->status = tinfl_decompress(&pState->inflator, (const mz_uint8 *)pState->pRead_buf + pState->read_buf_ofs, &in_buf_size, (mz_uint8 *)pState->pWrite_buf, pWrite_buf_cur, &out_buf_size, pState->comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + pState->read_buf_avail -= in_buf_size; + pState->read_buf_ofs += in_buf_size; + + /* Update current output block size remaining */ + pState->out_blk_remain = out_buf_size; + } + + if (pState->out_blk_remain) + { + /* Calc amount to return. */ + size_t to_copy = MZ_MIN( (buf_size - copied_to_caller), pState->out_blk_remain ); + + /* Copy data to caller's buffer */ + memcpy( (uint8_t*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy ); + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + /* Perform CRC */ + pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, pWrite_buf_cur, to_copy); +#endif + + /* Decrement data consumed from block */ + pState->out_blk_remain -= to_copy; + + /* Inc output offset, while performing sanity check */ + if ((pState->out_buf_ofs += to_copy) > pState->file_stat.m_uncomp_size) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); + pState->status = TINFL_STATUS_FAILED; + break; + } + + /* Increment counter of data copied to caller */ + copied_to_caller += to_copy; + } + } while ( (copied_to_caller < buf_size) && ((pState->status == TINFL_STATUS_NEEDS_MORE_INPUT) || (pState->status == TINFL_STATUS_HAS_MORE_OUTPUT)) ); + } + + /* Return how many bytes were copied into user buffer */ + return copied_to_caller; +} + +mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState) +{ + int status; + + /* Argument sanity check */ + if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState)) + return MZ_FALSE; + + /* Was decompression completed and requested? */ + if ((pState->status == TINFL_STATUS_DONE) && (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (pState->out_buf_ofs != pState->file_stat.m_uncomp_size) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + pState->status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (pState->file_crc32 != pState->file_stat.m_crc32) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); + pState->status = TINFL_STATUS_FAILED; + } +#endif + } + + /* Free buffers */ + if (!pState->pZip->m_pState->m_pMem) + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pRead_buf); + if (pState->pWrite_buf) + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pWrite_buf); + + /* Save status */ + status = pState->status; + + /* Free context */ + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState); + + return status == TINFL_STATUS_DONE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) +{ + (void)ofs; + + return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque); +} + +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) +{ + mz_bool status; + mz_zip_archive_file_stat file_stat; + MZ_FILE *pFile; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + pFile = MZ_FOPEN(pDst_filename, "wb"); + if (!pFile) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); + + if (MZ_FCLOSE(pFile) == EOF) + { + if (status) + mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); + + status = MZ_FALSE; + } + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) + if (status) + mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); +#endif + + return status; +} + +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); +} + +mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *pFile, mz_uint flags) +{ + mz_zip_archive_file_stat file_stat; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + return mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); +} + +mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_cfile(pZip, file_index, pFile, flags); +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +static size_t mz_zip_compute_crc32_callback(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_uint32 *p = (mz_uint32 *)pOpaque; + (void)file_ofs; + *p = (mz_uint32)mz_crc32(*p, (const mz_uint8 *)pBuf, n); + return n; +} + +mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) +{ + mz_zip_archive_file_stat file_stat; + mz_zip_internal_state *pState; + const mz_uint8 *pCentral_dir_header; + mz_bool found_zip64_ext_data_in_cdir = MZ_FALSE; + mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint64 local_header_ofs = 0; + mz_uint32 local_header_filename_len, local_header_extra_len, local_header_crc32; + mz_uint64 local_header_comp_size, local_header_uncomp_size; + mz_uint32 uncomp_crc32 = MZ_CRC32_INIT; + mz_bool has_data_descriptor; + mz_uint32 local_header_bit_flags; + + mz_zip_array file_data_array; + mz_zip_array_init(&file_data_array, 1); + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (file_index > pZip->m_total_files) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + pCentral_dir_header = mz_zip_get_cdh(pZip, file_index); + + if (!mz_zip_file_stat_internal(pZip, file_index, pCentral_dir_header, &file_stat, &found_zip64_ext_data_in_cdir)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_uncomp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_is_encrypted) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports stored and deflate. */ + if ((file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + if (!file_stat.m_is_supported) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + /* Read and parse the local directory entry. */ + local_header_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + local_header_filename_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); + local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); + local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); + local_header_crc32 = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_CRC32_OFS); + local_header_bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + has_data_descriptor = (local_header_bit_flags & 8) != 0; + + if (local_header_filename_len != strlen(file_stat.m_filename)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (!mz_zip_array_resize(pZip, &file_data_array, MZ_MAX(local_header_filename_len, local_header_extra_len), MZ_FALSE)) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + goto handle_failure; + } + + if (local_header_filename_len) + { + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE, file_data_array.m_p, local_header_filename_len) != local_header_filename_len) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + /* I've seen 1 archive that had the same pathname, but used backslashes in the local dir and forward slashes in the central dir. Do we care about this? For now, this case will fail validation. */ + if (memcmp(file_stat.m_filename, file_data_array.m_p, local_header_filename_len) != 0) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + + if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) + { + mz_uint32 extra_size_remaining = local_header_extra_len; + const mz_uint8 *pExtra_data = (const mz_uint8 *)file_data_array.m_p; + + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); + + if (field_data_size < sizeof(mz_uint64) * 2) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } + + local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); + local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); + + found_zip64_ext_data_in_ldir = MZ_TRUE; + break; + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + } + + /* TODO: parse local header extra data when local_header_comp_size is 0xFFFFFFFF! (big_descriptor.zip) */ + /* I've seen zips in the wild with the data descriptor bit set, but proper local header values and bogus data descriptors */ + if ((has_data_descriptor) && (!local_header_comp_size) && (!local_header_crc32)) + { + mz_uint8 descriptor_buf[32]; + mz_bool has_id; + const mz_uint8 *pSrc; + mz_uint32 file_crc32; + mz_uint64 comp_size = 0, uncomp_size = 0; + + mz_uint32 num_descriptor_uint32s = ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) ? 6 : 4; + + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size, descriptor_buf, sizeof(mz_uint32) * num_descriptor_uint32s) != (sizeof(mz_uint32) * num_descriptor_uint32s)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + has_id = (MZ_READ_LE32(descriptor_buf) == MZ_ZIP_DATA_DESCRIPTOR_ID); + pSrc = has_id ? (descriptor_buf + sizeof(mz_uint32)) : descriptor_buf; + + file_crc32 = MZ_READ_LE32(pSrc); + + if ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) + { + comp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32)); + uncomp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32) + sizeof(mz_uint64)); + } + else + { + comp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32)); + uncomp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32) + sizeof(mz_uint32)); + } + + if ((file_crc32 != file_stat.m_crc32) || (comp_size != file_stat.m_comp_size) || (uncomp_size != file_stat.m_uncomp_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + else + { + if ((local_header_crc32 != file_stat.m_crc32) || (local_header_comp_size != file_stat.m_comp_size) || (local_header_uncomp_size != file_stat.m_uncomp_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + + mz_zip_array_clear(pZip, &file_data_array); + + if ((flags & MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY) == 0) + { + if (!mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_compute_crc32_callback, &uncomp_crc32, 0)) + return MZ_FALSE; + + /* 1 more check to be sure, although the extract checks too. */ + if (uncomp_crc32 != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + return MZ_FALSE; + } + } + + return MZ_TRUE; + +handle_failure: + mz_zip_array_clear(pZip, &file_data_array); + return MZ_FALSE; +} + +mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags) +{ + mz_zip_internal_state *pState; + uint32_t i; + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + /* Basic sanity checks */ + if (!pState->m_zip64) + { + if (pZip->m_total_files > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (pZip->m_archive_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + else + { + if (pZip->m_total_files >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + + for (i = 0; i < pZip->m_total_files; i++) + { + if (MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG & flags) + { + mz_uint32 found_index; + mz_zip_archive_file_stat stat; + + if (!mz_zip_reader_file_stat(pZip, i, &stat)) + return MZ_FALSE; + + if (!mz_zip_reader_locate_file_v2(pZip, stat.m_filename, NULL, 0, &found_index)) + return MZ_FALSE; + + /* This check can fail if there are duplicate filenames in the archive (which we don't check for when writing - that's up to the user) */ + if (found_index != i) + return mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + } + + if (!mz_zip_validate_file(pZip, i, flags)) + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr) +{ + mz_bool success = MZ_TRUE; + mz_zip_archive zip; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + if ((!pMem) || (!size)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + mz_zip_zero_struct(&zip); + + if (!mz_zip_reader_init_mem(&zip, pMem, size, flags)) + { + if (pErr) + *pErr = zip.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_validate_archive(&zip, flags)) + { + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (!mz_zip_reader_end_internal(&zip, success)) + { + if (!actual_err) + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (pErr) + *pErr = actual_err; + + return success; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr) +{ + mz_bool success = MZ_TRUE; + mz_zip_archive zip; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + if (!pFilename) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + mz_zip_zero_struct(&zip); + + if (!mz_zip_reader_init_file_v2(&zip, pFilename, flags, 0, 0)) + { + if (pErr) + *pErr = zip.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_validate_archive(&zip, flags)) + { + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (!mz_zip_reader_end_internal(&zip, success)) + { + if (!actual_err) + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (pErr) + *pErr = actual_err; + + return success; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +/* ------------------- .ZIP archive writing */ + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +static MZ_FORCEINLINE void mz_write_le16(mz_uint8 *p, mz_uint16 v) +{ + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); +} +static MZ_FORCEINLINE void mz_write_le32(mz_uint8 *p, mz_uint32 v) +{ + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); + p[2] = (mz_uint8)(v >> 16); + p[3] = (mz_uint8)(v >> 24); +} +static MZ_FORCEINLINE void mz_write_le64(mz_uint8 *p, mz_uint64 v) +{ + mz_write_le32(p, (mz_uint32)v); + mz_write_le32(p + sizeof(mz_uint32), (mz_uint32)(v >> 32)); +} + +#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) +#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) +#define MZ_WRITE_LE64(p, v) mz_write_le64((mz_uint8 *)(p), (mz_uint64)(v)) + +static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); + + if (!n) + return 0; + + /* An allocation this big is likely to just fail on 32-bit systems, so don't even go there. */ + if ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + return 0; + } + + if (new_size > pState->m_mem_capacity) + { + void *pNew_block; + size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); + + while (new_capacity < new_size) + new_capacity *= 2; + + if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return 0; + } + + pState->m_pMem = pNew_block; + pState->m_mem_capacity = new_capacity; + } + memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); + pState->m_mem_size = (size_t)new_size; + return n; +} + +static mz_bool mz_zip_writer_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) +{ + mz_zip_internal_state *pState; + mz_bool status = MZ_TRUE; + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) + { + if (set_last_error) + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + pState = pZip->m_pState; + pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (MZ_FCLOSE(pState->m_pFile) == EOF) + { + if (set_last_error) + mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); + status = MZ_FALSE; + } + } + + pState->m_pFile = NULL; + } +#endif /* #ifndef MINIZ_NO_STDIO */ + + if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); + pState->m_pMem = NULL; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + return status; +} + +mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags) +{ + mz_bool zip64 = (flags & MZ_ZIP_FLAG_WRITE_ZIP64) != 0; + + if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + { + if (!pZip->m_pRead) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + if (pZip->m_file_offset_alignment) + { + /* Ensure user specified file offset alignment is a power of 2. */ + if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + if (!pZip->m_pAlloc) + pZip->m_pAlloc = miniz_def_alloc_func; + if (!pZip->m_pFree) + pZip->m_pFree = miniz_def_free_func; + if (!pZip->m_pRealloc) + pZip->m_pRealloc = miniz_def_realloc_func; + + pZip->m_archive_size = existing_size; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + + pZip->m_pState->m_zip64 = zip64; + pZip->m_pState->m_zip64_has_extended_info_fields = zip64; + + pZip->m_zip_type = MZ_ZIP_TYPE_USER; + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) +{ + return mz_zip_writer_init_v2(pZip, existing_size, 0); +} + +mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags) +{ + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_mem_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_HEAP; + + if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) + { + if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) + { + mz_zip_writer_end_internal(pZip, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + pZip->m_pState->m_mem_capacity = initial_allocation_size; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) +{ + return mz_zip_writer_init_heap_v2(pZip, size_to_reserve_at_beginning, initial_allocation_size, 0); +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + + file_ofs += pZip->m_pState->m_file_archive_start_ofs; + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + return 0; + } + + return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) +{ + return mz_zip_writer_init_file_v2(pZip, pFilename, size_to_reserve_at_beginning, 0); +} + +mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags) +{ + MZ_FILE *pFile; + + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) + return MZ_FALSE; + + if (NULL == (pFile = MZ_FOPEN(pFilename, (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ? "w+b" : "wb"))) + { + mz_zip_writer_end(pZip); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + } + + pZip->m_pState->m_pFile = pFile; + pZip->m_zip_type = MZ_ZIP_TYPE_FILE; + + if (size_to_reserve_at_beginning) + { + mz_uint64 cur_ofs = 0; + char buf[4096]; + + MZ_CLEAR_OBJ(buf); + + do + { + size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) + { + mz_zip_writer_end(pZip); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_ofs += n; + size_to_reserve_at_beginning -= n; + } while (size_to_reserve_at_beginning); + } + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags) +{ + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, 0, flags)) + return MZ_FALSE; + + pZip->m_pState->m_pFile = pFile; + pZip->m_pState->m_file_archive_start_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; + + return MZ_TRUE; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) +{ + mz_zip_internal_state *pState; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (flags & MZ_ZIP_FLAG_WRITE_ZIP64) + { + /* We don't support converting a non-zip64 file to zip64 - this seems like more trouble than it's worth. (What about the existing 32-bit data descriptors that could follow the compressed data?) */ + if (!pZip->m_pState->m_zip64) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + /* No sense in trying to write to an archive that's already at the support max size */ + if (pZip->m_pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + } + + pState = pZip->m_pState; + + if (pState->m_pFile) + { +#ifdef MINIZ_NO_STDIO + (void)pFilename; + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +#else + if (pZip->m_pIO_opaque != pZip) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (!pFilename) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Archive is being read from stdio and was originally opened only for reading. Try to reopen as writable. */ + if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) + { + /* The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. */ + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + } + } + + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; +#endif /* #ifdef MINIZ_NO_STDIO */ + } + else if (pState->m_pMem) + { + /* Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. */ + if (pZip->m_pIO_opaque != pZip) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState->m_mem_capacity = pState->m_mem_size; + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pNeeds_keepalive = NULL; + } + /* Archive is being read via a user provided read function - make sure the user has specified a write function too. */ + else if (!pZip->m_pWrite) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Start writing new files at the archive's current central directory location. */ + /* TODO: We could add a flag that lets the user start writing immediately AFTER the existing central dir - this would be safer. */ + pZip->m_archive_size = pZip->m_central_directory_file_ofs; + pZip->m_central_directory_file_ofs = 0; + + /* Clear the sorted central dir offsets, they aren't useful or maintained now. */ + /* Even though we're now in write mode, files can still be extracted and verified, but file locates will be slow. */ + /* TODO: We could easily maintain the sorted central directory offsets. */ + mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets); + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) +{ + return mz_zip_writer_init_from_reader_v2(pZip, pFilename, 0); +} + +/* TODO: pArchive_name is a terrible name here! */ +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) +{ + return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); +} + +typedef struct +{ + mz_zip_archive *m_pZip; + mz_uint64 m_cur_archive_file_ofs; + mz_uint64 m_comp_size; +} mz_zip_writer_add_state; + +static mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, void *pUser) +{ + mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; + if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) + return MZ_FALSE; + + pState->m_cur_archive_file_ofs += len; + pState->m_comp_size += len; + return MZ_TRUE; +} + +#define MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 2) +#define MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 3) +static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64 *pUncomp_size, mz_uint64 *pComp_size, mz_uint64 *pLocal_header_ofs) +{ + mz_uint8 *pDst = pBuf; + mz_uint32 field_size = 0; + + MZ_WRITE_LE16(pDst + 0, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); + MZ_WRITE_LE16(pDst + 2, 0); + pDst += sizeof(mz_uint16) * 2; + + if (pUncomp_size) + { + MZ_WRITE_LE64(pDst, *pUncomp_size); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + if (pComp_size) + { + MZ_WRITE_LE64(pDst, *pComp_size); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + if (pLocal_header_ofs) + { + MZ_WRITE_LE64(pDst, *pLocal_header_ofs); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + MZ_WRITE_LE16(pBuf + 2, field_size); + + return (mz_uint32)(pDst - pBuf); +} + +static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, + mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, + mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, + mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, + mz_uint64 local_header_ofs, mz_uint32 ext_attributes) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_MIN(local_header_ofs, MZ_UINT32_MAX)); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, + const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, + mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, + mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, + mz_uint64 local_header_ofs, mz_uint32 ext_attributes, + const char *user_extra_data, mz_uint user_extra_data_len) +{ + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; + size_t orig_central_dir_size = pState->m_central_dir.m_size; + mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + + if (!pZip->m_pState->m_zip64) + { + if (local_header_ofs > 0xFFFFFFFF) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + } + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + user_extra_data_len + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, (mz_uint16)(extra_size + user_extra_data_len), comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, user_extra_data, user_extra_data_len)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) + { + /* Try to resize the central directory array back into its original state. */ + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) +{ + /* Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. */ + if (*pArchive_name == '/') + return MZ_FALSE; + + /* Making sure the name does not contain drive letters or DOS style backward slashes is the responsibility of the program using miniz*/ + + return MZ_TRUE; +} + +static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) +{ + mz_uint32 n; + if (!pZip->m_file_offset_alignment) + return 0; + n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); + return (mz_uint)((pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1)); +} + +static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) +{ + char buf[4096]; + memset(buf, 0, MZ_MIN(sizeof(buf), n)); + while (n) + { + mz_uint32 s = MZ_MIN(sizeof(buf), n); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_file_ofs += s; + n -= s; + } + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) +{ + return mz_zip_writer_add_mem_ex_v2(pZip, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, uncomp_size, uncomp_crc32, NULL, NULL, 0, NULL, 0); +} + +mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, + mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + mz_uint16 method = 0, dos_time = 0, dos_date = 0; + mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; + mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + tdefl_compressor *pComp = NULL; + mz_bool store_data_uncompressed; + mz_zip_internal_state *pState; + mz_uint8 *pExtra_data = NULL; + mz_uint32 extra_size = 0; + mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; + mz_uint16 bit_flags = 0; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + + if (uncomp_size || (buf_size && !(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + bit_flags |= MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + + if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) + bit_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + + level = level_and_flags & 0xF; + store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if (pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ + } + if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + + if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + +#ifndef MINIZ_NO_TIME + if (last_modified != NULL) + { + mz_zip_time_t_to_dos_time(*last_modified, &dos_time, &dos_date); + } + else + { + MZ_TIME_T cur_time; + time(&cur_time); + mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date); + } +#endif /* #ifndef MINIZ_NO_TIME */ + + if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size); + uncomp_size = buf_size; + if (uncomp_size <= 3) + { + level = 0; + store_data_uncompressed = MZ_TRUE; + } + } + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!pState->m_zip64) + { + /* Bail early if the archive would obviously become too large */ + if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len + + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + user_extra_data_central_len + + MZ_ZIP_DATA_DESCRIPTER_SIZE32) > 0xFFFFFFFF) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + + if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) + { + /* Set DOS Subdirectory attribute bit. */ + ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; + + /* Subdirectories cannot contain data. */ + if ((buf_size) || (uncomp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + /* Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) */ + if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + (pState->m_zip64 ? MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE : 0))) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if ((!store_data_uncompressed) && (buf_size)) + { + if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + cur_archive_file_ofs += num_alignment_padding_bytes; + + MZ_CLEAR_OBJ(local_dir_header); + + if (!store_data_uncompressed || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + method = MZ_DEFLATED; + } + + if (pState->m_zip64) + { + if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) + { + pExtra_data = extra_data; + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, bit_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_archive_file_ofs += archive_name_size; + + if (pExtra_data != NULL) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += extra_size; + } + } + else + { + if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_archive_file_ofs += archive_name_size; + } + + if (user_extra_data_len > 0) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += user_extra_data_len; + } + + if (store_data_uncompressed) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += buf_size; + comp_size = buf_size; + } + else if (buf_size) + { + mz_zip_writer_add_state state; + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || + (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pComp = NULL; + + if (uncomp_size) + { + mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; + mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + + MZ_ASSERT(bit_flags & MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR); + + MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); + MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); + if (pExtra_data == NULL) + { + if (comp_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(local_dir_footer + 8, comp_size); + MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); + } + else + { + MZ_WRITE_LE64(local_dir_footer + 8, comp_size); + MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); + local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) + return MZ_FALSE; + + cur_archive_file_ofs += local_dir_footer_size; + } + + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment, + comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, + user_extra_data_central, user_extra_data_central_len)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + mz_uint16 gen_flags = (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) ? 0 : MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; + mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; + mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + mz_uint8 *pExtra_data = NULL; + mz_uint32 extra_size = 0; + mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; + mz_zip_internal_state *pState; + mz_uint64 file_ofs = 0, cur_archive_header_file_ofs; + + if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) + gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; + + /* Sanity checks */ + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if ((!pState->m_zip64) && (max_size > MZ_UINT32_MAX)) + { + /* Source file is too large for non-zip64 */ + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + pState->m_zip64 = MZ_TRUE; + } + + /* We could support this, but why? */ + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + if (pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ + } + } + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!pState->m_zip64) + { + /* Bail early if the archive would obviously become too large */ + if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 1024 + + MZ_ZIP_DATA_DESCRIPTER_SIZE32 + user_extra_data_central_len) > 0xFFFFFFFF) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + +#ifndef MINIZ_NO_TIME + if (pFile_time) + { + mz_zip_time_t_to_dos_time(*pFile_time, &dos_time, &dos_date); + } +#endif + + if (max_size <= 3) + level = 0; + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += num_alignment_padding_bytes; + local_dir_header_ofs = cur_archive_file_ofs; + + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + + if (max_size && level) + { + method = MZ_DEFLATED; + } + + MZ_CLEAR_OBJ(local_dir_header); + if (pState->m_zip64) + { + if (max_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) + { + pExtra_data = extra_data; + if (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL, + (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + else + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, NULL, + NULL, + (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += archive_name_size; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += extra_size; + } + else + { + if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += archive_name_size; + } + + if (user_extra_data_len > 0) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += user_extra_data_len; + } + + if (max_size) + { + void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); + if (!pRead_buf) + { + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!level) + { + while (1) + { + size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE); + if (n == 0) + break; + + if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + file_ofs += n; + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + cur_archive_file_ofs += n; + } + uncomp_size = file_ofs; + comp_size = uncomp_size; + } + else + { + mz_bool result = MZ_FALSE; + mz_zip_writer_add_state state; + tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + } + + for (;;) + { + tdefl_status status; + tdefl_flush flush = TDEFL_NO_FLUSH; + + size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE); + if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + break; + } + + file_ofs += n; + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + + if (pZip->m_pNeeds_keepalive != NULL && pZip->m_pNeeds_keepalive(pZip->m_pIO_opaque)) + flush = TDEFL_FULL_FLUSH; + + if (n == 0) + flush = TDEFL_FINISH; + + status = tdefl_compress_buffer(pComp, pRead_buf, n, flush); + if (status == TDEFL_STATUS_DONE) + { + result = MZ_TRUE; + break; + } + else if (status != TDEFL_STATUS_OKAY) + { + mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); + break; + } + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + + if (!result) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return MZ_FALSE; + } + + uncomp_size = file_ofs; + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + } + + if (!(level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE)) + { + mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; + mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + + MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); + MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); + if (pExtra_data == NULL) + { + if (comp_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(local_dir_footer + 8, comp_size); + MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); + } + else + { + MZ_WRITE_LE64(local_dir_footer + 8, comp_size); + MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); + local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) + return MZ_FALSE; + + cur_archive_file_ofs += local_dir_footer_size; + } + + if (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) + { + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, + (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), + (max_size >= MZ_UINT32_MAX) ? MZ_UINT32_MAX : uncomp_size, + (max_size >= MZ_UINT32_MAX) ? MZ_UINT32_MAX : comp_size, + uncomp_crc32, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + cur_archive_header_file_ofs = local_dir_header_ofs; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + if (pExtra_data != NULL) + { + cur_archive_header_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_header_file_ofs += archive_name_size; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_header_file_ofs += extra_size; + } + } + + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment, comment_size, + uncomp_size, comp_size, uncomp_crc32, method, gen_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, + user_extra_data_central, user_extra_data_central_len)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO + +static size_t mz_file_read_func_stdio(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + MZ_FILE *pSrc_file = (MZ_FILE *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pSrc_file); + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pSrc_file, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + + return MZ_FREAD(pBuf, 1, n, pSrc_file); +} + +mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + return mz_zip_writer_add_read_buf_callback(pZip, pArchive_name, mz_file_read_func_stdio, pSrc_file, max_size, pFile_time, pComment, comment_size, level_and_flags, + user_extra_data, user_extra_data_len, user_extra_data_central, user_extra_data_central_len); +} + +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + MZ_FILE *pSrc_file = NULL; + mz_uint64 uncomp_size = 0; + MZ_TIME_T file_modified_time; + MZ_TIME_T *pFile_time = NULL; + mz_bool status; + + memset(&file_modified_time, 0, sizeof(file_modified_time)); + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) + pFile_time = &file_modified_time; + if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_STAT_FAILED); +#endif + + pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); + if (!pSrc_file) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + MZ_FSEEK64(pSrc_file, 0, SEEK_END); + uncomp_size = MZ_FTELL64(pSrc_file); + MZ_FSEEK64(pSrc_file, 0, SEEK_SET); + + status = mz_zip_writer_add_cfile(pZip, pArchive_name, pSrc_file, uncomp_size, pFile_time, pComment, comment_size, level_and_flags, NULL, 0, NULL, 0); + + MZ_FCLOSE(pSrc_file); + + return status; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +static mz_bool mz_zip_writer_update_zip64_extension_block(mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, uint32_t ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start) +{ + /* + 64 should be enough for any new zip64 data */ + if (!mz_zip_array_reserve(pZip, pNew_ext, ext_len + 64, MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + mz_zip_array_resize(pZip, pNew_ext, 0, MZ_FALSE); + + if ((pUncomp_size) || (pComp_size) || (pLocal_header_ofs) || (pDisk_start)) + { + mz_uint8 new_ext_block[64]; + mz_uint8 *pDst = new_ext_block; + mz_write_le16(pDst, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); + mz_write_le16(pDst + sizeof(mz_uint16), 0); + pDst += sizeof(mz_uint16) * 2; + + if (pUncomp_size) + { + mz_write_le64(pDst, *pUncomp_size); + pDst += sizeof(mz_uint64); + } + + if (pComp_size) + { + mz_write_le64(pDst, *pComp_size); + pDst += sizeof(mz_uint64); + } + + if (pLocal_header_ofs) + { + mz_write_le64(pDst, *pLocal_header_ofs); + pDst += sizeof(mz_uint64); + } + + if (pDisk_start) + { + mz_write_le32(pDst, *pDisk_start); + pDst += sizeof(mz_uint32); + } + + mz_write_le16(new_ext_block + sizeof(mz_uint16), (mz_uint16)((pDst - new_ext_block) - sizeof(mz_uint16) * 2)); + + if (!mz_zip_array_push_back(pZip, pNew_ext, new_ext_block, pDst - new_ext_block)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if ((pExt) && (ext_len)) + { + mz_uint32 extra_size_remaining = ext_len; + const mz_uint8 *pExtra_data = pExt; + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id != MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + if (!mz_zip_array_push_back(pZip, pNew_ext, pExtra_data, field_total_size)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + } + + return MZ_TRUE; +} + +/* TODO: This func is now pretty freakin complex due to zip64, split it up? */ +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index) +{ + mz_uint n, bit_flags, num_alignment_padding_bytes, src_central_dir_following_data_size; + mz_uint64 src_archive_bytes_remaining, local_dir_header_ofs; + mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint8 new_central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + size_t orig_central_dir_size; + mz_zip_internal_state *pState; + void *pBuf; + const mz_uint8 *pSrc_central_header; + mz_zip_archive_file_stat src_file_stat; + mz_uint32 src_filename_len, src_comment_len, src_ext_len; + mz_uint32 local_header_filename_size, local_header_extra_len; + mz_uint64 local_header_comp_size, local_header_uncomp_size; + mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; + + /* Sanity checks */ + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pSource_zip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + /* Don't support copying files from zip64 archives to non-zip64, even though in some cases this is possible */ + if ((pSource_zip->m_pState->m_zip64) && (!pZip->m_pState->m_zip64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Get pointer to the source central dir header and crack it */ + if (NULL == (pSrc_central_header = mz_zip_get_cdh(pSource_zip, src_file_index))) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_SIG_OFS) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + src_filename_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS); + src_comment_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); + src_ext_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS); + src_central_dir_following_data_size = src_filename_len + src_ext_len + src_comment_len; + + /* TODO: We don't support central dir's >= MZ_UINT32_MAX bytes right now (+32 fudge factor in case we need to add more extra data) */ + if ((pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + 32) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + if (!pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + /* TODO: Our zip64 support still has some 32-bit limits that may not be worth fixing. */ + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + + if (!mz_zip_file_stat_internal(pSource_zip, src_file_index, pSrc_central_header, &src_file_stat, NULL)) + return MZ_FALSE; + + cur_src_file_ofs = src_file_stat.m_local_header_ofs; + cur_dst_file_ofs = pZip->m_archive_size; + + /* Read the source archive's local dir header */ + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + /* Compute the total size we need to copy (filename+extra data+compressed data) */ + local_header_filename_size = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); + local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); + local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); + src_archive_bytes_remaining = local_header_filename_size + local_header_extra_len + src_file_stat.m_comp_size; + + /* Try to find a zip64 extended information field */ + if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) + { + mz_zip_array file_data_array; + const mz_uint8 *pExtra_data; + mz_uint32 extra_size_remaining = local_header_extra_len; + + mz_zip_array_init(&file_data_array, 1); + if (!mz_zip_array_resize(pZip, &file_data_array, local_header_extra_len, MZ_FALSE)) + { + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, src_file_stat.m_local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_size, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + pExtra_data = (const mz_uint8 *)file_data_array.m_p; + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); + + if (field_data_size < sizeof(mz_uint64) * 2) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); + local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); /* may be 0 if there's a descriptor */ + + found_zip64_ext_data_in_ldir = MZ_TRUE; + break; + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + + mz_zip_array_clear(pZip, &file_data_array); + } + + if (!pState->m_zip64) + { + /* Try to detect if the new archive will most likely wind up too big and bail early (+(sizeof(mz_uint32) * 4) is for the optional descriptor which could be present, +64 is a fudge factor). */ + /* We also check when the archive is finalized so this doesn't need to be perfect. */ + mz_uint64 approx_new_archive_size = cur_dst_file_ofs + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + src_archive_bytes_remaining + (sizeof(mz_uint32) * 4) + + pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 64; + + if (approx_new_archive_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + + /* Write dest archive padding */ + if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) + return MZ_FALSE; + + cur_dst_file_ofs += num_alignment_padding_bytes; + + local_dir_header_ofs = cur_dst_file_ofs; + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + + /* The original zip's local header+ext block doesn't change, even with zip64, so we can just copy it over to the dest zip */ + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + /* Copy over the source archive bytes to the dest archive, also ensure we have enough buf space to handle optional data descriptor */ + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(32U, MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining))))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + while (src_archive_bytes_remaining) + { + n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining); + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + cur_src_file_ofs += n; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_dst_file_ofs += n; + + src_archive_bytes_remaining -= n; + } + + /* Now deal with the optional data descriptor */ + bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + if (bit_flags & 8) + { + /* Copy data descriptor */ + if ((pSource_zip->m_pState->m_zip64) || (found_zip64_ext_data_in_ldir)) + { + /* src is zip64, dest must be zip64 */ + + /* name uint32_t's */ + /* id 1 (optional in zip64?) */ + /* crc 1 */ + /* comp_size 2 */ + /* uncomp_size 2 */ + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, (sizeof(mz_uint32) * 6)) != (sizeof(mz_uint32) * 6)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID) ? 6 : 5); + } + else + { + /* src is NOT zip64 */ + mz_bool has_id; + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + has_id = (MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID); + + if (pZip->m_pState->m_zip64) + { + /* dest is zip64, so upgrade the data descriptor */ + const mz_uint32 *pSrc_descriptor = (const mz_uint32 *)((const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0)); + const mz_uint32 src_crc32 = pSrc_descriptor[0]; + const mz_uint64 src_comp_size = pSrc_descriptor[1]; + const mz_uint64 src_uncomp_size = pSrc_descriptor[2]; + + mz_write_le32((mz_uint8 *)pBuf, MZ_ZIP_DATA_DESCRIPTOR_ID); + mz_write_le32((mz_uint8 *)pBuf + sizeof(mz_uint32) * 1, src_crc32); + mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 2, src_comp_size); + mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 4, src_uncomp_size); + + n = sizeof(mz_uint32) * 6; + } + else + { + /* dest is NOT zip64, just copy it as-is */ + n = sizeof(mz_uint32) * (has_id ? 4 : 3); + } + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_src_file_ofs += n; + cur_dst_file_ofs += n; + } + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + + /* Finally, add the new central dir header */ + orig_central_dir_size = pState->m_central_dir.m_size; + + memcpy(new_central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + + if (pState->m_zip64) + { + /* This is the painful part: We need to write a new central dir header + ext block with updated zip64 fields, and ensure the old fields (if any) are not included. */ + const mz_uint8 *pSrc_ext = pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len; + mz_zip_array new_ext_block; + + mz_zip_array_init(&new_ext_block, sizeof(mz_uint8)); + + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_UINT32_MAX); + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_UINT32_MAX); + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_UINT32_MAX); + + if (!mz_zip_writer_update_zip64_extension_block(&new_ext_block, pZip, pSrc_ext, src_ext_len, &src_file_stat.m_comp_size, &src_file_stat.m_uncomp_size, &local_dir_header_ofs, NULL)) + { + mz_zip_array_clear(pZip, &new_ext_block); + return MZ_FALSE; + } + + MZ_WRITE_LE16(new_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS, new_ext_block.m_size); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + { + mz_zip_array_clear(pZip, &new_ext_block); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_filename_len)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_ext_block.m_p, new_ext_block.m_size)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len + src_ext_len, src_comment_len)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + mz_zip_array_clear(pZip, &new_ext_block); + } + else + { + /* sanity checks */ + if (cur_dst_file_ofs > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (local_dir_header_ofs >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_central_dir_following_data_size)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + } + + /* This shouldn't trigger unless we screwed up during the initial sanity checks */ + if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) + { + /* TODO: Support central dirs >= 32-bits in size */ + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + } + + n = (mz_uint32)orig_central_dir_size; + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + pZip->m_total_files++; + pZip->m_archive_size = cur_dst_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState; + mz_uint64 central_dir_ofs, central_dir_size; + mz_uint8 hdr[256]; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if (pState->m_zip64) + { + if ((pZip->m_total_files > MZ_UINT32_MAX) || (pState->m_central_dir.m_size >= MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if ((pZip->m_total_files > MZ_UINT16_MAX) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + + central_dir_ofs = 0; + central_dir_size = 0; + if (pZip->m_total_files) + { + /* Write central directory */ + central_dir_ofs = pZip->m_archive_size; + central_dir_size = pState->m_central_dir.m_size; + pZip->m_central_directory_file_ofs = central_dir_ofs; + if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += central_dir_size; + } + + if (pState->m_zip64) + { + /* Write zip64 end of central directory header */ + mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size; + + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64)); + MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); /* TODO: always Unix */ + MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS, 0x002D); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_SIZE_OFS, central_dir_size); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_OFS_OFS, central_dir_ofs); + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE; + + /* Write zip64 end of central directory locator */ + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1); + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE; + } + + /* Write end of central directory record */ + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_size)); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_ofs)); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +#ifndef MINIZ_NO_STDIO + if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); +#endif /* #ifndef MINIZ_NO_STDIO */ + + pZip->m_archive_size += MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE; + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize) +{ + if ((!ppBuf) || (!pSize)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + *ppBuf = NULL; + *pSize = 0; + + if ((!pZip) || (!pZip->m_pState)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (pZip->m_pWrite != mz_zip_heap_write_func) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_finalize_archive(pZip)) + return MZ_FALSE; + + *ppBuf = pZip->m_pState->m_pMem; + *pSize = pZip->m_pState->m_mem_size; + pZip->m_pState->m_pMem = NULL; + pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_end(mz_zip_archive *pZip) +{ + return mz_zip_writer_end_internal(pZip, MZ_TRUE); +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + return mz_zip_add_mem_to_archive_file_in_place_v2(pZip_filename, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, NULL); +} + +mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr) +{ + mz_bool status, created_new_archive = MZ_FALSE; + mz_zip_archive zip_archive; + struct MZ_FILE_STAT_STRUCT file_stat; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + mz_zip_zero_struct(&zip_archive); + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + + if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_FILENAME; + return MZ_FALSE; + } + + /* Important: The regular non-64 bit version of stat() can fail here if the file is very large, which could cause the archive to be overwritten. */ + /* So be sure to compile with _LARGEFILE64_SOURCE 1 */ + if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) + { + /* Create a new archive. */ + if (!mz_zip_writer_init_file_v2(&zip_archive, pZip_filename, 0, level_and_flags)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + return MZ_FALSE; + } + + created_new_archive = MZ_TRUE; + } + else + { + /* Append to an existing archive. */ + if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_writer_init_from_reader_v2(&zip_archive, pZip_filename, level_and_flags)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + + mz_zip_reader_end_internal(&zip_archive, MZ_FALSE); + + return MZ_FALSE; + } + } + + status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); + actual_err = zip_archive.m_last_error; + + /* Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) */ + if (!mz_zip_writer_finalize_archive(&zip_archive)) + { + if (!actual_err) + actual_err = zip_archive.m_last_error; + + status = MZ_FALSE; + } + + if (!mz_zip_writer_end_internal(&zip_archive, status)) + { + if (!actual_err) + actual_err = zip_archive.m_last_error; + + status = MZ_FALSE; + } + + if ((!status) && (created_new_archive)) + { + /* It's a new archive and something went wrong, so just delete it. */ + int ignoredStatus = MZ_DELETE_FILE(pZip_filename); + (void)ignoredStatus; + } + + if (pErr) + *pErr = actual_err; + + return status; +} + +void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr) +{ + mz_uint32 file_index; + mz_zip_archive zip_archive; + void *p = NULL; + + if (pSize) + *pSize = 0; + + if ((!pZip_filename) || (!pArchive_name)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + + return NULL; + } + + mz_zip_zero_struct(&zip_archive); + if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + + return NULL; + } + + if (mz_zip_reader_locate_file_v2(&zip_archive, pArchive_name, pComment, flags, &file_index)) + { + p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); + } + + mz_zip_reader_end_internal(&zip_archive, p != NULL); + + if (pErr) + *pErr = zip_archive.m_last_error; + + return p; +} + +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) +{ + return mz_zip_extract_archive_file_to_heap_v2(pZip_filename, pArchive_name, NULL, pSize, flags, NULL); +} + +#endif /* #ifndef MINIZ_NO_STDIO */ + +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +/* ------------------- Misc utils */ + +mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_zip_mode : MZ_ZIP_MODE_INVALID; +} + +mz_zip_type mz_zip_get_type(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_zip_type : MZ_ZIP_TYPE_INVALID; +} + +mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num) +{ + mz_zip_error prev_err; + + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + prev_err = pZip->m_last_error; + + pZip->m_last_error = err_num; + return prev_err; +} + +mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip) +{ + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + return pZip->m_last_error; +} + +mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip) +{ + return mz_zip_set_last_error(pZip, MZ_ZIP_NO_ERROR); +} + +mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip) +{ + mz_zip_error prev_err; + + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + prev_err = pZip->m_last_error; + + pZip->m_last_error = MZ_ZIP_NO_ERROR; + return prev_err; +} + +const char *mz_zip_get_error_string(mz_zip_error mz_err) +{ + switch (mz_err) + { + case MZ_ZIP_NO_ERROR: + return "no error"; + case MZ_ZIP_UNDEFINED_ERROR: + return "undefined error"; + case MZ_ZIP_TOO_MANY_FILES: + return "too many files"; + case MZ_ZIP_FILE_TOO_LARGE: + return "file too large"; + case MZ_ZIP_UNSUPPORTED_METHOD: + return "unsupported method"; + case MZ_ZIP_UNSUPPORTED_ENCRYPTION: + return "unsupported encryption"; + case MZ_ZIP_UNSUPPORTED_FEATURE: + return "unsupported feature"; + case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR: + return "failed finding central directory"; + case MZ_ZIP_NOT_AN_ARCHIVE: + return "not a ZIP archive"; + case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED: + return "invalid header or archive is corrupted"; + case MZ_ZIP_UNSUPPORTED_MULTIDISK: + return "unsupported multidisk archive"; + case MZ_ZIP_DECOMPRESSION_FAILED: + return "decompression failed or archive is corrupted"; + case MZ_ZIP_COMPRESSION_FAILED: + return "compression failed"; + case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE: + return "unexpected decompressed size"; + case MZ_ZIP_CRC_CHECK_FAILED: + return "CRC-32 check failed"; + case MZ_ZIP_UNSUPPORTED_CDIR_SIZE: + return "unsupported central directory size"; + case MZ_ZIP_ALLOC_FAILED: + return "allocation failed"; + case MZ_ZIP_FILE_OPEN_FAILED: + return "file open failed"; + case MZ_ZIP_FILE_CREATE_FAILED: + return "file create failed"; + case MZ_ZIP_FILE_WRITE_FAILED: + return "file write failed"; + case MZ_ZIP_FILE_READ_FAILED: + return "file read failed"; + case MZ_ZIP_FILE_CLOSE_FAILED: + return "file close failed"; + case MZ_ZIP_FILE_SEEK_FAILED: + return "file seek failed"; + case MZ_ZIP_FILE_STAT_FAILED: + return "file stat failed"; + case MZ_ZIP_INVALID_PARAMETER: + return "invalid parameter"; + case MZ_ZIP_INVALID_FILENAME: + return "invalid filename"; + case MZ_ZIP_BUF_TOO_SMALL: + return "buffer too small"; + case MZ_ZIP_INTERNAL_ERROR: + return "internal error"; + case MZ_ZIP_FILE_NOT_FOUND: + return "file not found"; + case MZ_ZIP_ARCHIVE_TOO_LARGE: + return "archive is too large"; + case MZ_ZIP_VALIDATION_FAILED: + return "validation failed"; + case MZ_ZIP_WRITE_CALLBACK_FAILED: + return "write calledback failed"; + default: + break; + } + + return "unknown error"; +} + +/* Note: Just because the archive is not zip64 doesn't necessarily mean it doesn't have Zip64 extended information extra field, argh. */ +mz_bool mz_zip_is_zip64(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return MZ_FALSE; + + return pZip->m_pState->m_zip64; +} + +size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + + return pZip->m_pState->m_central_dir.m_size; +} + +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_total_files : 0; +} + +mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip) +{ + if (!pZip) + return 0; + return pZip->m_archive_size; +} + +mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + return pZip->m_pState->m_file_archive_start_ofs; +} + +MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + return pZip->m_pState->m_pFile; +} + +size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + return pZip->m_pRead(pZip->m_pIO_opaque, file_ofs, pBuf, n); +} + +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) +{ + mz_uint n; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + if (filename_buf_size) + pFilename[0] = '\0'; + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return 0; + } + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_buf_size) + { + n = MZ_MIN(n, filename_buf_size - 1); + memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pFilename[n] = '\0'; + } + return n + 1; +} + +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) +{ + return mz_zip_file_stat_internal(pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL); +} + +mz_bool mz_zip_end(mz_zip_archive *pZip) +{ + if (!pZip) + return MZ_FALSE; + + if (pZip->m_zip_mode == MZ_ZIP_MODE_READING) + return mz_zip_reader_end(pZip); +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + else if ((pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) || (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)) + return mz_zip_writer_end(pZip); +#endif + + return MZ_FALSE; +} + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_ARCHIVE_APIS*/ diff --git a/luaclib/src/lz/miniz.h b/luaclib/src/lz/miniz.h new file mode 100644 index 00000000..4191145c --- /dev/null +++ b/luaclib/src/lz/miniz.h @@ -0,0 +1,1346 @@ +#define MINIZ_EXPORT +/* miniz.c 2.2.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing + See "unlicense" statement at the end of this file. + Rich Geldreich , last updated Oct. 13, 2013 + Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt + + Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define + MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). + + * Low-level Deflate/Inflate implementation notes: + + Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or + greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses + approximately as well as zlib. + + Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function + coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory + block large enough to hold the entire file. + + The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. + + * zlib-style API notes: + + miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in + zlib replacement in many apps: + The z_stream struct, optional memory allocation callbacks + deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound + inflateInit/inflateInit2/inflate/inflateReset/inflateEnd + compress, compress2, compressBound, uncompress + CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. + Supports raw deflate streams or standard zlib streams with adler-32 checking. + + Limitations: + The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. + I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but + there are no guarantees that miniz.c pulls this off perfectly. + + * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by + Alex Evans. Supports 1-4 bytes/pixel images. + + * ZIP archive API notes: + + The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to + get the job done with minimal fuss. There are simple API's to retrieve file information, read files from + existing archives, create new archives, append new files to existing archives, or clone archive data from + one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), + or you can specify custom file read/write callbacks. + + - Archive reading: Just call this function to read a single file from a disk archive: + + void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, + size_t *pSize, mz_uint zip_flags); + + For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central + directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. + + - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: + + int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + + The locate operation can optionally check file comments too, which (as one example) can be used to identify + multiple versions of the same file in an archive. This function uses a simple linear search through the central + directory, so it's not very fast. + + Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and + retrieve detailed info on each file by calling mz_zip_reader_file_stat(). + + - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data + to disk and builds an exact image of the central directory in memory. The central directory image is written + all at once at the end of the archive file when the archive is finalized. + + The archive writer can optionally align each file's local header and file data to any power of 2 alignment, + which can be useful when the archive will be read from optical media. Also, the writer supports placing + arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still + readable by any ZIP tool. + + - Archive appending: The simple way to add a single file to an archive is to call this function: + + mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, + const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + + The archive will be created if it doesn't already exist, otherwise it'll be appended to. + Note the appending is done in-place and is not an atomic operation, so if something goes wrong + during the operation it's possible the archive could be left without a central directory (although the local + file headers and file data will be fine, so the archive will be recoverable). + + For more complex archive modification scenarios: + 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to + preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the + compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and + you're done. This is safe but requires a bunch of temporary disk space or heap memory. + + 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), + append new files as needed, then finalize the archive which will write an updated central directory to the + original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a + possibility that the archive's central directory could be lost with this method if anything goes wrong, though. + + - ZIP archive support limitations: + No spanning support. Extraction functions can only handle unencrypted, stored or deflated files. + Requires streams capable of seeking. + + * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the + below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. + + * Important: For best perf. be sure to customize the below macros for your target platform: + #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 + #define MINIZ_LITTLE_ENDIAN 1 + #define MINIZ_HAS_64BIT_REGISTERS 1 + + * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz + uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files + (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). +*/ +#pragma once + + + +/* Defines to completely disable specific portions of miniz.c: + If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. */ + +/* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */ +/*#define MINIZ_NO_STDIO */ + +/* If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or */ +/* get/set file times, and the C run-time funcs that get/set times won't be called. */ +/* The current downside is the times written to your archives will be from 1979. */ +/*#define MINIZ_NO_TIME */ + +/* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_APIS */ + +/* Define MINIZ_NO_ARCHIVE_WRITING_APIS to disable all writing related ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_WRITING_APIS */ + +/* Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. */ +/*#define MINIZ_NO_ZLIB_APIS */ + +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. */ +/*#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +/* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. + Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc + callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user + functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */ +/*#define MINIZ_NO_MALLOC */ + +#if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) +/* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */ +#define MINIZ_NO_TIME +#endif + +#include + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) +#include +#endif + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) +/* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */ +#define MINIZ_X86_OR_X64_CPU 1 +#else +#define MINIZ_X86_OR_X64_CPU 0 +#endif + +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU +/* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ +#define MINIZ_LITTLE_ENDIAN 1 +#else +#define MINIZ_LITTLE_ENDIAN 0 +#endif + +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */ +#if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES) +#if MINIZ_X86_OR_X64_CPU +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */ +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 +#define MINIZ_UNALIGNED_USE_MEMCPY +#else +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#endif +#endif + +#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) +/* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). */ +#define MINIZ_HAS_64BIT_REGISTERS 1 +#else +#define MINIZ_HAS_64BIT_REGISTERS 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- zlib-style API Definitions. */ + +/* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! */ +typedef unsigned long mz_ulong; + +/* mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. */ +MINIZ_EXPORT void mz_free(void *p); + +#define MZ_ADLER32_INIT (1) +/* mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. */ +MINIZ_EXPORT mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); + +#define MZ_CRC32_INIT (0) +/* mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. */ +MINIZ_EXPORT mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); + +/* Compression strategies. */ +enum +{ + MZ_DEFAULT_STRATEGY = 0, + MZ_FILTERED = 1, + MZ_HUFFMAN_ONLY = 2, + MZ_RLE = 3, + MZ_FIXED = 4 +}; + +/* Method */ +#define MZ_DEFLATED 8 + +/* Heap allocation callbacks. +Note that mz_alloc_func parameter types purposely differ from zlib's: items/size is size_t, not unsigned long. */ +typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); +typedef void (*mz_free_func)(void *opaque, void *address); +typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); + +/* Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. */ +enum +{ + MZ_NO_COMPRESSION = 0, + MZ_BEST_SPEED = 1, + MZ_BEST_COMPRESSION = 9, + MZ_UBER_COMPRESSION = 10, + MZ_DEFAULT_LEVEL = 6, + MZ_DEFAULT_COMPRESSION = -1 +}; + +#define MZ_VERSION "10.2.0" +#define MZ_VERNUM 0xA100 +#define MZ_VER_MAJOR 10 +#define MZ_VER_MINOR 2 +#define MZ_VER_REVISION 0 +#define MZ_VER_SUBREVISION 0 + +#ifndef MINIZ_NO_ZLIB_APIS + +/* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). */ +enum +{ + MZ_NO_FLUSH = 0, + MZ_PARTIAL_FLUSH = 1, + MZ_SYNC_FLUSH = 2, + MZ_FULL_FLUSH = 3, + MZ_FINISH = 4, + MZ_BLOCK = 5 +}; + +/* Return status codes. MZ_PARAM_ERROR is non-standard. */ +enum +{ + MZ_OK = 0, + MZ_STREAM_END = 1, + MZ_NEED_DICT = 2, + MZ_ERRNO = -1, + MZ_STREAM_ERROR = -2, + MZ_DATA_ERROR = -3, + MZ_MEM_ERROR = -4, + MZ_BUF_ERROR = -5, + MZ_VERSION_ERROR = -6, + MZ_PARAM_ERROR = -10000 +}; + +/* Window bits */ +#define MZ_DEFAULT_WINDOW_BITS 15 + +struct mz_internal_state; + +/* Compression/decompression stream struct. */ +typedef struct mz_stream_s +{ + const unsigned char *next_in; /* pointer to next byte to read */ + unsigned int avail_in; /* number of bytes available at next_in */ + mz_ulong total_in; /* total number of bytes consumed so far */ + + unsigned char *next_out; /* pointer to next byte to write */ + unsigned int avail_out; /* number of bytes that can be written to next_out */ + mz_ulong total_out; /* total number of bytes produced so far */ + + char *msg; /* error msg (unused) */ + struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */ + + mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */ + mz_free_func zfree; /* optional heap free function (defaults to free) */ + void *opaque; /* heap alloc function user pointer */ + + int data_type; /* data_type (unused) */ + mz_ulong adler; /* adler32 of the source or uncompressed data */ + mz_ulong reserved; /* not used */ +} mz_stream; + +typedef mz_stream *mz_streamp; + +/* Returns the version string of miniz.c. */ +MINIZ_EXPORT const char *mz_version(void); + +/* mz_deflateInit() initializes a compressor with default options: */ +/* Parameters: */ +/* pStream must point to an initialized mz_stream struct. */ +/* level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. */ +/* level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. */ +/* (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if the input parameters are bogus. */ +/* MZ_MEM_ERROR on out of memory. */ +MINIZ_EXPORT int mz_deflateInit(mz_streamp pStream, int level); + +/* mz_deflateInit2() is like mz_deflate(), except with more control: */ +/* Additional parameters: */ +/* method must be MZ_DEFLATED */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) */ +/* mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */ +MINIZ_EXPORT int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + +/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */ +MINIZ_EXPORT int mz_deflateReset(mz_streamp pStream); + +/* mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. */ +/* Return values: */ +/* MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). */ +/* MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) */ +MINIZ_EXPORT int mz_deflate(mz_streamp pStream, int flush); + +/* mz_deflateEnd() deinitializes a compressor: */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +MINIZ_EXPORT int mz_deflateEnd(mz_streamp pStream); + +/* mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. */ +MINIZ_EXPORT mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); + +/* Single-call compression functions mz_compress() and mz_compress2(): */ +/* Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. */ +MINIZ_EXPORT int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +MINIZ_EXPORT int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); + +/* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */ +MINIZ_EXPORT mz_ulong mz_compressBound(mz_ulong source_len); + +/* Initializes a decompressor. */ +MINIZ_EXPORT int mz_inflateInit(mz_streamp pStream); + +/* mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). */ +MINIZ_EXPORT int mz_inflateInit2(mz_streamp pStream, int window_bits); + +/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_inflateEnd() followed by mz_inflateInit()/mz_inflateInit2(). */ +MINIZ_EXPORT int mz_inflateReset(mz_streamp pStream); + +/* Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. */ +/* On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). */ +/* MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. */ +/* Return values: */ +/* MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. */ +/* MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_DATA_ERROR if the deflate stream is invalid. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again */ +/* with more input data, or with more room in the output buffer (except when using single call decompression, described above). */ +MINIZ_EXPORT int mz_inflate(mz_streamp pStream, int flush); + +/* Deinitializes a decompressor. */ +MINIZ_EXPORT int mz_inflateEnd(mz_streamp pStream); + +/* Single-call decompression. */ +/* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */ +MINIZ_EXPORT int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +MINIZ_EXPORT int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len); + +/* Returns a string description of the specified error code, or NULL if the error code is invalid. */ +MINIZ_EXPORT const char *mz_error(int err); + +/* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. */ +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. */ +#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES +typedef unsigned char Byte; +typedef unsigned int uInt; +typedef mz_ulong uLong; +typedef Byte Bytef; +typedef uInt uIntf; +typedef char charf; +typedef int intf; +typedef void *voidpf; +typedef uLong uLongf; +typedef void *voidp; +typedef void *const voidpc; +#define Z_NULL 0 +#define Z_NO_FLUSH MZ_NO_FLUSH +#define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH +#define Z_SYNC_FLUSH MZ_SYNC_FLUSH +#define Z_FULL_FLUSH MZ_FULL_FLUSH +#define Z_FINISH MZ_FINISH +#define Z_BLOCK MZ_BLOCK +#define Z_OK MZ_OK +#define Z_STREAM_END MZ_STREAM_END +#define Z_NEED_DICT MZ_NEED_DICT +#define Z_ERRNO MZ_ERRNO +#define Z_STREAM_ERROR MZ_STREAM_ERROR +#define Z_DATA_ERROR MZ_DATA_ERROR +#define Z_MEM_ERROR MZ_MEM_ERROR +#define Z_BUF_ERROR MZ_BUF_ERROR +#define Z_VERSION_ERROR MZ_VERSION_ERROR +#define Z_PARAM_ERROR MZ_PARAM_ERROR +#define Z_NO_COMPRESSION MZ_NO_COMPRESSION +#define Z_BEST_SPEED MZ_BEST_SPEED +#define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION +#define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION +#define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY +#define Z_FILTERED MZ_FILTERED +#define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY +#define Z_RLE MZ_RLE +#define Z_FIXED MZ_FIXED +#define Z_DEFLATED MZ_DEFLATED +#define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS +#define alloc_func mz_alloc_func +#define free_func mz_free_func +#define internal_state mz_internal_state +#define z_stream mz_stream +#define deflateInit mz_deflateInit +#define deflateInit2 mz_deflateInit2 +#define deflateReset mz_deflateReset +#define deflate mz_deflate +#define deflateEnd mz_deflateEnd +#define deflateBound mz_deflateBound +#define compress mz_compress +#define compress2 mz_compress2 +#define compressBound mz_compressBound +#define inflateInit mz_inflateInit +#define inflateInit2 mz_inflateInit2 +#define inflateReset mz_inflateReset +#define inflate mz_inflate +#define inflateEnd mz_inflateEnd +#define uncompress mz_uncompress +#define uncompress2 mz_uncompress2 +#define crc32 mz_crc32 +#define adler32 mz_adler32 +#define MAX_WBITS 15 +#define MAX_MEM_LEVEL 9 +#define zError mz_error +#define ZLIB_VERSION MZ_VERSION +#define ZLIB_VERNUM MZ_VERNUM +#define ZLIB_VER_MAJOR MZ_VER_MAJOR +#define ZLIB_VER_MINOR MZ_VER_MINOR +#define ZLIB_VER_REVISION MZ_VER_REVISION +#define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION +#define zlibVersion mz_version +#define zlib_version mz_version() +#endif /* #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +#endif /* MINIZ_NO_ZLIB_APIS */ + +#ifdef __cplusplus +} +#endif + + + + + +#pragma once +#include +#include +#include +#include + + + +/* ------------------- Types and macros */ +typedef unsigned char mz_uint8; +typedef signed short mz_int16; +typedef unsigned short mz_uint16; +typedef unsigned int mz_uint32; +typedef unsigned int mz_uint; +typedef int64_t mz_int64; +typedef uint64_t mz_uint64; +typedef int mz_bool; + +#define MZ_FALSE (0) +#define MZ_TRUE (1) + +/* Works around MSVC's spammy "warning C4127: conditional expression is constant" message. */ +#ifdef _MSC_VER +#define MZ_MACRO_END while (0, 0) +#else +#define MZ_MACRO_END while (0) +#endif + +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include +#define MZ_FILE FILE +#endif /* #ifdef MINIZ_NO_STDIO */ + +#ifdef MINIZ_NO_TIME +typedef struct mz_dummy_time_t_tag +{ + int m_dummy; +} mz_dummy_time_t; +#define MZ_TIME_T mz_dummy_time_t +#else +#define MZ_TIME_T time_t +#endif + +#define MZ_ASSERT(x) assert(x) + +#if !defined(MINIZ_NO_MALLOC) +#define MZ_MALLOC(x) malloc(x) +#define MZ_FREE(x) free(x) +#define MZ_REALLOC(p, x) realloc(p, x) +#endif + +#define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +#define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) +#define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) +#else +#define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) +#define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) +#endif + +#define MZ_READ_LE64(p) (((mz_uint64)MZ_READ_LE32(p)) | (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) << 32U)) + +#ifdef _MSC_VER +#define MZ_FORCEINLINE __forceinline +#elif defined(__GNUC__) +#define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__)) +#else +#define MZ_FORCEINLINE inline +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +extern MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size); +extern MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address); +extern MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size); + +#define MZ_UINT16_MAX (0xFFFFU) +#define MZ_UINT32_MAX (0xFFFFFFFFU) + +#ifdef __cplusplus +} +#endif + #pragma once + + +#ifdef __cplusplus +extern "C" { +#endif +/* ------------------- Low-level Compression API Definitions */ + +/* Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). */ +#define TDEFL_LESS_MEMORY 0 + +/* tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): */ +/* TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). */ +enum +{ + TDEFL_HUFFMAN_ONLY = 0, + TDEFL_DEFAULT_MAX_PROBES = 128, + TDEFL_MAX_PROBES_MASK = 0xFFF +}; + +/* TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. */ +/* TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). */ +/* TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. */ +/* TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). */ +/* TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) */ +/* TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. */ +/* TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. */ +/* TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. */ +/* The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). */ +enum +{ + TDEFL_WRITE_ZLIB_HEADER = 0x01000, + TDEFL_COMPUTE_ADLER32 = 0x02000, + TDEFL_GREEDY_PARSING_FLAG = 0x04000, + TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, + TDEFL_RLE_MATCHES = 0x10000, + TDEFL_FILTER_MATCHES = 0x20000, + TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, + TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 +}; + +/* High level compression functions: */ +/* tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of source block to compress. */ +/* flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must free() the returned block when it's no longer needed. */ +MINIZ_EXPORT void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. */ +/* Returns 0 on failure. */ +MINIZ_EXPORT size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* Compresses an image to a compressed PNG file in memory. */ +/* On entry: */ +/* pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. */ +/* The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. */ +/* level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL */ +/* If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pLen_out will be set to the size of the PNG image file. */ +/* The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. */ +MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); +MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); + +/* Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */ +typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); + +/* tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. */ +MINIZ_EXPORT mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +enum +{ + TDEFL_MAX_HUFF_TABLES = 3, + TDEFL_MAX_HUFF_SYMBOLS_0 = 288, + TDEFL_MAX_HUFF_SYMBOLS_1 = 32, + TDEFL_MAX_HUFF_SYMBOLS_2 = 19, + TDEFL_LZ_DICT_SIZE = 32768, + TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, + TDEFL_MIN_MATCH_LEN = 3, + TDEFL_MAX_MATCH_LEN = 258 +}; + +/* TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). */ +#if TDEFL_LESS_MEMORY +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 12, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#else +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 15, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#endif + +/* The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. */ +typedef enum { + TDEFL_STATUS_BAD_PARAM = -2, + TDEFL_STATUS_PUT_BUF_FAILED = -1, + TDEFL_STATUS_OKAY = 0, + TDEFL_STATUS_DONE = 1 +} tdefl_status; + +/* Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums */ +typedef enum { + TDEFL_NO_FLUSH = 0, + TDEFL_SYNC_FLUSH = 2, + TDEFL_FULL_FLUSH = 3, + TDEFL_FINISH = 4 +} tdefl_flush; + +/* tdefl's compression state structure. */ +typedef struct +{ + tdefl_put_buf_func_ptr m_pPut_buf_func; + void *m_pPut_buf_user; + mz_uint m_flags, m_max_probes[2]; + int m_greedy_parsing; + mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; + mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; + mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; + mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; + tdefl_status m_prev_return_status; + const void *m_pIn_buf; + void *m_pOut_buf; + size_t *m_pIn_buf_size, *m_pOut_buf_size; + tdefl_flush m_flush; + const mz_uint8 *m_pSrc; + size_t m_src_buf_left, m_out_buf_ofs; + mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; + mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; + mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; + mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; + mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; +} tdefl_compressor; + +/* Initializes the compressor. */ +/* There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. */ +/* pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. */ +/* If pBut_buf_func is NULL the user should always call the tdefl_compress() API. */ +/* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) */ +MINIZ_EXPORT tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +/* Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. */ +MINIZ_EXPORT tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); + +/* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. */ +/* tdefl_compress_buffer() always consumes the entire input buffer. */ +MINIZ_EXPORT tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); + +MINIZ_EXPORT tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); +MINIZ_EXPORT mz_uint32 tdefl_get_adler32(tdefl_compressor *d); + +/* Create tdefl_compress() flags given zlib-style compression parameters. */ +/* level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) */ +/* window_bits may be -15 (raw deflate) or 15 (zlib) */ +/* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED */ +MINIZ_EXPORT mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tdefl_compressor structure in C so that */ +/* non-C language bindings to tdefl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +MINIZ_EXPORT tdefl_compressor *tdefl_compressor_alloc(void); +MINIZ_EXPORT void tdefl_compressor_free(tdefl_compressor *pComp); +#endif + +#ifdef __cplusplus +} +#endif + #pragma once + +/* ------------------- Low-level Decompression API Definitions */ + +#ifdef __cplusplus +extern "C" { +#endif +/* Decompression flags used by tinfl_decompress(). */ +/* TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. */ +/* TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. */ +/* TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). */ +/* TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. */ +enum +{ + TINFL_FLAG_PARSE_ZLIB_HEADER = 1, + TINFL_FLAG_HAS_MORE_INPUT = 2, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, + TINFL_FLAG_COMPUTE_ADLER32 = 8 +}; + +/* High level decompression functions: */ +/* tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. */ +/* On return: */ +/* Function returns a pointer to the decompressed data, or NULL on failure. */ +/* *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must call mz_free() on the returned block when it's no longer needed. */ +MINIZ_EXPORT void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */ +/* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */ +#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +MINIZ_EXPORT size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */ +/* Returns 1 on success or 0 on failure. */ +typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); +MINIZ_EXPORT int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +struct tinfl_decompressor_tag; +typedef struct tinfl_decompressor_tag tinfl_decompressor; + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tinfl_decompressor structure in C so that */ +/* non-C language bindings to tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +MINIZ_EXPORT tinfl_decompressor *tinfl_decompressor_alloc(void); +MINIZ_EXPORT void tinfl_decompressor_free(tinfl_decompressor *pDecomp); +#endif + +/* Max size of LZ dictionary. */ +#define TINFL_LZ_DICT_SIZE 32768 + +/* Return status. */ +typedef enum { + /* This flags indicates the inflator needs 1 or more input bytes to make forward progress, but the caller is indicating that no more are available. The compressed data */ + /* is probably corrupted. If you call the inflator again with more bytes it'll try to continue processing the input but this is a BAD sign (either the data is corrupted or you called it incorrectly). */ + /* If you call it again with no input you'll just get TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS again. */ + TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS = -4, + + /* This flag indicates that one or more of the input parameters was obviously bogus. (You can try calling it again, but if you get this error the calling code is wrong.) */ + TINFL_STATUS_BAD_PARAM = -3, + + /* This flags indicate the inflator is finished but the adler32 check of the uncompressed data didn't match. If you call it again it'll return TINFL_STATUS_DONE. */ + TINFL_STATUS_ADLER32_MISMATCH = -2, + + /* This flags indicate the inflator has somehow failed (bad code, corrupted input, etc.). If you call it again without resetting via tinfl_init() it it'll just keep on returning the same status failure code. */ + TINFL_STATUS_FAILED = -1, + + /* Any status code less than TINFL_STATUS_DONE must indicate a failure. */ + + /* This flag indicates the inflator has returned every byte of uncompressed data that it can, has consumed every byte that it needed, has successfully reached the end of the deflate stream, and */ + /* if zlib headers and adler32 checking enabled that it has successfully checked the uncompressed data's adler32. If you call it again you'll just get TINFL_STATUS_DONE over and over again. */ + TINFL_STATUS_DONE = 0, + + /* This flag indicates the inflator MUST have more input data (even 1 byte) before it can make any more forward progress, or you need to clear the TINFL_FLAG_HAS_MORE_INPUT */ + /* flag on the next call if you don't have any more source data. If the source data was somehow corrupted it's also possible (but unlikely) for the inflator to keep on demanding input to */ + /* proceed, so be sure to properly set the TINFL_FLAG_HAS_MORE_INPUT flag. */ + TINFL_STATUS_NEEDS_MORE_INPUT = 1, + + /* This flag indicates the inflator definitely has 1 or more bytes of uncompressed data available, but it cannot write this data into the output buffer. */ + /* Note if the source compressed data was corrupted it's possible for the inflator to return a lot of uncompressed data to the caller. I've been assuming you know how much uncompressed data to expect */ + /* (either exact or worst case) and will stop calling the inflator and fail after receiving too much. In pure streaming scenarios where you have no idea how many bytes to expect this may not be possible */ + /* so I may need to add some code to address this. */ + TINFL_STATUS_HAS_MORE_OUTPUT = 2 +} tinfl_status; + +/* Initializes the decompressor to its initial state. */ +#define tinfl_init(r) \ + do \ + { \ + (r)->m_state = 0; \ + } \ + MZ_MACRO_END +#define tinfl_get_adler32(r) (r)->m_check_adler32 + +/* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */ +/* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */ +MINIZ_EXPORT tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); + +/* Internal/private bits follow. */ +enum +{ + TINFL_MAX_HUFF_TABLES = 3, + TINFL_MAX_HUFF_SYMBOLS_0 = 288, + TINFL_MAX_HUFF_SYMBOLS_1 = 32, + TINFL_MAX_HUFF_SYMBOLS_2 = 19, + TINFL_FAST_LOOKUP_BITS = 10, + TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS +}; + +typedef struct +{ + mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; +} tinfl_huff_table; + +#if MINIZ_HAS_64BIT_REGISTERS +#define TINFL_USE_64BIT_BITBUF 1 +#else +#define TINFL_USE_64BIT_BITBUF 0 +#endif + +#if TINFL_USE_64BIT_BITBUF +typedef mz_uint64 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (64) +#else +typedef mz_uint32 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (32) +#endif + +struct tinfl_decompressor_tag +{ + mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; + tinfl_bit_buf_t m_bit_buf; + size_t m_dist_from_out_buf_start; + tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; + mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; +}; + +#ifdef __cplusplus +} +#endif + +#pragma once + + +/* ------------------- ZIP archive reading/writing */ + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +enum +{ + /* Note: These enums can be reduced as needed to save memory or stack space - they are pretty conservative. */ + MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, + MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512, + MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512 +}; + +typedef struct +{ + /* Central directory file index. */ + mz_uint32 m_file_index; + + /* Byte offset of this entry in the archive's central directory. Note we currently only support up to UINT_MAX or less bytes in the central dir. */ + mz_uint64 m_central_dir_ofs; + + /* These fields are copied directly from the zip's central dir. */ + mz_uint16 m_version_made_by; + mz_uint16 m_version_needed; + mz_uint16 m_bit_flag; + mz_uint16 m_method; + +#ifndef MINIZ_NO_TIME + MZ_TIME_T m_time; +#endif + + /* CRC-32 of uncompressed data. */ + mz_uint32 m_crc32; + + /* File's compressed size. */ + mz_uint64 m_comp_size; + + /* File's uncompressed size. Note, I've seen some old archives where directory entries had 512 bytes for their uncompressed sizes, but when you try to unpack them you actually get 0 bytes. */ + mz_uint64 m_uncomp_size; + + /* Zip internal and external file attributes. */ + mz_uint16 m_internal_attr; + mz_uint32 m_external_attr; + + /* Entry's local header file offset in bytes. */ + mz_uint64 m_local_header_ofs; + + /* Size of comment in bytes. */ + mz_uint32 m_comment_size; + + /* MZ_TRUE if the entry appears to be a directory. */ + mz_bool m_is_directory; + + /* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip doesn't support) */ + mz_bool m_is_encrypted; + + /* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a compression method we support. */ + mz_bool m_is_supported; + + /* Filename. If string ends in '/' it's a subdirectory entry. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; + + /* Comment field. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; + +} mz_zip_archive_file_stat; + +typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); +typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); +typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); + +struct mz_zip_internal_state_tag; +typedef struct mz_zip_internal_state_tag mz_zip_internal_state; + +typedef enum { + MZ_ZIP_MODE_INVALID = 0, + MZ_ZIP_MODE_READING = 1, + MZ_ZIP_MODE_WRITING = 2, + MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 +} mz_zip_mode; + +typedef enum { + MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, + MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, + MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800, + MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */ + MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */ + MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */ + MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000, + MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000, + /*After adding a compressed file, seek back + to local file header and set the correct sizes*/ + MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE = 0x20000 +} mz_zip_flags; + +typedef enum { + MZ_ZIP_TYPE_INVALID = 0, + MZ_ZIP_TYPE_USER, + MZ_ZIP_TYPE_MEMORY, + MZ_ZIP_TYPE_HEAP, + MZ_ZIP_TYPE_FILE, + MZ_ZIP_TYPE_CFILE, + MZ_ZIP_TOTAL_TYPES +} mz_zip_type; + +/* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or modify this enum. */ +typedef enum { + MZ_ZIP_NO_ERROR = 0, + MZ_ZIP_UNDEFINED_ERROR, + MZ_ZIP_TOO_MANY_FILES, + MZ_ZIP_FILE_TOO_LARGE, + MZ_ZIP_UNSUPPORTED_METHOD, + MZ_ZIP_UNSUPPORTED_ENCRYPTION, + MZ_ZIP_UNSUPPORTED_FEATURE, + MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, + MZ_ZIP_NOT_AN_ARCHIVE, + MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, + MZ_ZIP_UNSUPPORTED_MULTIDISK, + MZ_ZIP_DECOMPRESSION_FAILED, + MZ_ZIP_COMPRESSION_FAILED, + MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, + MZ_ZIP_CRC_CHECK_FAILED, + MZ_ZIP_UNSUPPORTED_CDIR_SIZE, + MZ_ZIP_ALLOC_FAILED, + MZ_ZIP_FILE_OPEN_FAILED, + MZ_ZIP_FILE_CREATE_FAILED, + MZ_ZIP_FILE_WRITE_FAILED, + MZ_ZIP_FILE_READ_FAILED, + MZ_ZIP_FILE_CLOSE_FAILED, + MZ_ZIP_FILE_SEEK_FAILED, + MZ_ZIP_FILE_STAT_FAILED, + MZ_ZIP_INVALID_PARAMETER, + MZ_ZIP_INVALID_FILENAME, + MZ_ZIP_BUF_TOO_SMALL, + MZ_ZIP_INTERNAL_ERROR, + MZ_ZIP_FILE_NOT_FOUND, + MZ_ZIP_ARCHIVE_TOO_LARGE, + MZ_ZIP_VALIDATION_FAILED, + MZ_ZIP_WRITE_CALLBACK_FAILED, + MZ_ZIP_TOTAL_ERRORS +} mz_zip_error; + +typedef struct +{ + mz_uint64 m_archive_size; + mz_uint64 m_central_directory_file_ofs; + + /* We only support up to UINT32_MAX files in zip64 mode. */ + mz_uint32 m_total_files; + mz_zip_mode m_zip_mode; + mz_zip_type m_zip_type; + mz_zip_error m_last_error; + + mz_uint64 m_file_offset_alignment; + + mz_alloc_func m_pAlloc; + mz_free_func m_pFree; + mz_realloc_func m_pRealloc; + void *m_pAlloc_opaque; + + mz_file_read_func m_pRead; + mz_file_write_func m_pWrite; + mz_file_needs_keepalive m_pNeeds_keepalive; + void *m_pIO_opaque; + + mz_zip_internal_state *m_pState; + +} mz_zip_archive; + +typedef struct +{ + mz_zip_archive *pZip; + mz_uint flags; + + int status; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + mz_uint file_crc32; +#endif + mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + void *pWrite_buf; + + size_t out_blk_remain; + + tinfl_decompressor inflator; + +} mz_zip_reader_extract_iter_state; + +/* -------- ZIP reading */ + +/* Inits a ZIP archive reader. */ +/* These functions read and validate the archive's central directory. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags); + +MINIZ_EXPORT mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +/* Read a archive from a disk file. */ +/* file_start_ofs is the file offset where the archive actually begins, or 0. */ +/* actual_archive_size is the true total size of the archive, which may be smaller than the file's actual size on disk. If zero the entire file is treated as the archive. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); +MINIZ_EXPORT mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); + +/* Read an archive from an already opened FILE, beginning at the current file position. */ +/* The archive is assumed to be archive_size bytes long. If archive_size is 0, then the entire rest of the file is assumed to contain the archive. */ +/* The FILE will NOT be closed when mz_zip_reader_end() is called. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags); +#endif + +/* Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. */ +MINIZ_EXPORT mz_bool mz_zip_reader_end(mz_zip_archive *pZip); + +/* -------- ZIP reading or writing */ + +/* Clears a mz_zip_archive struct to all zeros. */ +/* Important: This must be done before passing the struct to any mz_zip functions. */ +MINIZ_EXPORT void mz_zip_zero_struct(mz_zip_archive *pZip); + +MINIZ_EXPORT mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_type mz_zip_get_type(mz_zip_archive *pZip); + +/* Returns the total number of files in the archive. */ +MINIZ_EXPORT mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); + +MINIZ_EXPORT mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip); +MINIZ_EXPORT mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip); +MINIZ_EXPORT MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip); + +/* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */ +MINIZ_EXPORT size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n); + +/* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. These functions retrieve/manipulate this field. */ +/* Note that the m_last_error functionality is not thread safe. */ +MINIZ_EXPORT mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num); +MINIZ_EXPORT mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT const char *mz_zip_get_error_string(mz_zip_error mz_err); + +/* MZ_TRUE if the archive file entry is a directory entry. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the file is encrypted/strong encrypted. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the compression method is supported, and the file is not encrypted, and the file is not a compressed patch file. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index); + +/* Retrieves the filename of an archive file entry. */ +/* Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. */ +MINIZ_EXPORT mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); + +/* Attempts to locates a file in the archive's central directory. */ +/* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ +/* Returns -1 if the file cannot be found. */ +MINIZ_EXPORT int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index); + +/* Returns detailed information about an archive file entry. */ +MINIZ_EXPORT mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); + +/* MZ_TRUE if the file is in zip64 format. */ +/* A file is considered zip64 if it contained a zip64 end of central directory marker, or if it contained any zip64 extended file information fields in the central directory. */ +MINIZ_EXPORT mz_bool mz_zip_is_zip64(mz_zip_archive *pZip); + +/* Returns the total central directory size in bytes. */ +/* The current max supported size is <= MZ_UINT32_MAX. */ +MINIZ_EXPORT size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip); + +/* Extracts a archive file to a memory buffer using no memory allocation. */ +/* There must be at least enough room on the stack to store the inflator's state (~34KB or so). */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); + +/* Extracts a archive file to a memory buffer. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); + +/* Extracts a archive file to a dynamically allocated heap buffer. */ +/* The memory will be allocated via the mz_zip_archive's alloc/realloc functions. */ +/* Returns NULL and sets the last error on failure. */ +MINIZ_EXPORT void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); +MINIZ_EXPORT void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); + +/* Extracts a archive file using a callback function to output the file's data. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); + +/* Extract a file iteratively */ +MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); +MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); +MINIZ_EXPORT size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState); + +#ifndef MINIZ_NO_STDIO +/* Extracts a archive file to a disk file and sets its last accessed and modified times. */ +/* This function only extracts files, not archive directory records. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); + +/* Extracts a archive file starting at the current position in the destination FILE stream. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags); +#endif + +#if 0 +/* TODO */ + typedef void *mz_zip_streaming_extract_state_ptr; + mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + uint64_t mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + uint64_t mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, uint64_t new_ofs); + size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size); + mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); +#endif + +/* This function compares the archive's local headers, the optional local zip64 extended information block, and the optional descriptor following the compressed data vs. the data in the central directory. */ +/* It also validates that each file can be successfully uncompressed unless the MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY is specified. */ +MINIZ_EXPORT mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + +/* Validates an entire archive by calling mz_zip_validate_file() on each file. */ +MINIZ_EXPORT mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags); + +/* Misc utils/helpers, valid for ZIP reading or writing */ +MINIZ_EXPORT mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr); +MINIZ_EXPORT mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr); + +/* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */ +MINIZ_EXPORT mz_bool mz_zip_end(mz_zip_archive *pZip); + +/* -------- ZIP writing */ + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +/* Inits a ZIP archive writer. */ +/*Set pZip->m_pWrite (and pZip->m_pIO_opaque) before calling mz_zip_writer_init or mz_zip_writer_init_v2*/ +/*The output is streamable, i.e. file_ofs in mz_file_write_func always increases only by n*/ +MINIZ_EXPORT mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); +MINIZ_EXPORT mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags); + +MINIZ_EXPORT mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); +MINIZ_EXPORT mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +MINIZ_EXPORT mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); +MINIZ_EXPORT mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags); +#endif + +/* Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. */ +/* For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. */ +/* For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). */ +/* Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. */ +/* Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before */ +/* the archive is finalized the file's central directory will be hosed. */ +MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); +MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); + +/* Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. */ +/* To add a directory entry, call this method with an archive name ending in a forwardslash with an empty buffer. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_mem(), except you can specify a file comment field, and optionally supply the function with already compressed data. */ +/* uncomp_size/uncomp_crc32 are only used if the MZ_ZIP_FLAG_COMPRESSED_DATA flag is specified. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); + +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); + +/* Adds the contents of a file to an archive. This function also records the disk file's modified time into the archive. */ +/* File data is supplied via a read callback function. User mz_zip_writer_add_(c)file to add a file directly.*/ +MINIZ_EXPORT mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, + const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); + + +#ifndef MINIZ_NO_STDIO +/* Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_file(), except the file data is read from the specified FILE stream. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, + const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); +#endif + +/* Adds a file to an archive by fully cloning the data from another archive. */ +/* This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data (it may add or modify the zip64 local header extra data field), and the optional descriptor following the compressed data. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index); + +/* Finalizes the archive by writing the central directory records followed by the end of central directory record. */ +/* After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). */ +/* An archive must be manually finalized by calling this function for it to be valid. */ +MINIZ_EXPORT mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); + +/* Finalizes a heap archive, returning a poiner to the heap block and its size. */ +/* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */ +MINIZ_EXPORT mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize); + +/* Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. */ +/* Note for the archive to be valid, it *must* have been finalized before ending (this function will not do it for you). */ +MINIZ_EXPORT mz_bool mz_zip_writer_end(mz_zip_archive *pZip); + +/* -------- Misc. high-level helper functions: */ + +/* mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. */ +/* Note this is NOT a fully safe operation. If it crashes or dies in some way your archive can be left in a screwed up state (without a central directory). */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +/* TODO: Perhaps add an option to leave the existing central dir in place in case the add dies? We could then truncate the file (so the old central dir would be at the end) if something goes wrong. */ +MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); +MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr); + +/* Reads a single file from an archive into a heap block. */ +/* If pComment is not NULL, only the file with the specified comment will be extracted. */ +/* Returns NULL on failure. */ +MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); +MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); + +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +#ifdef __cplusplus +} +#endif + +#endif /* MINIZ_NO_ARCHIVE_APIS */ From e05e5de34de25ca96e205fcb6c6f2e4a0a25403d Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Wed, 28 Dec 2022 21:55:56 +0800 Subject: [PATCH 913/956] =?UTF-8?q?=E5=AE=8C=E5=96=84gzip=E5=8E=8B?= =?UTF-8?q?=E7=BC=A9=E6=A0=BC=E5=BC=8F.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luaclib/src/lz/Makefile | 8 ++++---- luaclib/src/lz/lzlib.c | 39 +++++++++++++++++++++++++++++---------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/luaclib/src/lz/Makefile b/luaclib/src/lz/Makefile index 3b1b54b1..0c0e7353 100644 --- a/luaclib/src/lz/Makefile +++ b/luaclib/src/lz/Makefile @@ -15,11 +15,11 @@ LIBS = -L../ -L../../ -L../../../ -L/usr/local/lib CFLAGS = -O3 -Wall -shared -fPIC -Wl,-rpath,. -Wl,-rpath,.. -Wl,-rpath,/usr/local/lib # 是用内置库 -MICRO = -DLL = -lcore +# MICRO = +# DLL = -lcore # 使用`zlib` -#MICRO = -DUSE_ZLIB=1 -#DLL = -lcore -lz +MICRO = -DUSE_ZLIB=1 +DLL = -lcore -lz build: @$(CC) -o lz.so lzlib.c miniz.c $(INCLUDES) $(LIBS) $(CFLAGS) $(DLL) $(MICRO) diff --git a/luaclib/src/lz/lzlib.c b/luaclib/src/lz/lzlib.c index 774c744d..9dbc8ddf 100644 --- a/luaclib/src/lz/lzlib.c +++ b/luaclib/src/lz/lzlib.c @@ -20,6 +20,12 @@ #if defined(USE_ZLIB) void* stream_zalloc(void* opaque, unsigned items, unsigned nsize) #else +static inline void set_int32(char data[4], uint32_t bit) { + data[0] = (uint8_t)(bit) & 0xff; + data[1] = (uint8_t)(bit >> 8) & 0xff; + data[2] = (uint8_t)(bit >> 16) & 0xff; + data[3] = (uint8_t)(bit >> 24) & 0xff; +} void* stream_zalloc(void* opaque, size_t items, size_t nsize) #endif { @@ -77,7 +83,7 @@ static inline size_t gzip_check(const uint8_t *buffer, size_t bsize) { #endif /* 压缩 */ -static inline int stream_deflate(lua_State* L, z_stream *z, int Z_MYMODE, const uint8_t* in, size_t in_size) { +static inline int stream_deflate(lua_State* L, z_stream *z, int Z_MYFLUSH, int Z_MYMODE, const uint8_t* in, size_t in_size) { if (!z) return luaL_error(L, "[ZLIB ERROR]: `stream_deflate` got invalid `z_stream`."); @@ -92,12 +98,17 @@ static inline int stream_deflate(lua_State* L, z_stream *z, int Z_MYMODE, const uint8_t *out = lua_newuserdata(L, out_size); z->next_out = out; z->avail_out = out_size; + /* 计算偏移值 */ + int offset = 0; + if (Z_MYFLUSH == Z_SYNC_FLUSH) + offset = 4; + /* 压缩数据 */ - deflate(z, Z_SYNC_FLUSH); + deflate(z, Z_MYFLUSH); deflateEnd(z); /* 结束 */ - lua_pushlstring(L, (const char*)out, z->total_out - 4); + lua_pushlstring(L, (const char*)out, z->total_out - offset); lua_pushinteger(L, in_size); return 2; } @@ -150,7 +161,7 @@ static inline int stream_inflate(lua_State* L, z_stream *z, int Z_MYWIND, const int lws_compress(lua_State* L) { z_stream z; stream_init(&z); size_t bsize; const uint8_t *buffer = (const uint8_t *)luaL_checklstring(L, 1, &bsize); - return stream_deflate(L, &z, -MAX_WBITS, buffer, bsize); + return stream_deflate(L, &z, Z_SYNC_FLUSH, -MAX_WBITS, buffer, bsize); } int lws_uncompress(lua_State* L) { @@ -164,11 +175,19 @@ int lgzip_compress(lua_State* L) { z_stream z; stream_init(&z); size_t bsize; const uint8_t *buffer = (const uint8_t *)luaL_checklstring(L, 1, &bsize); #if defined(USE_ZLIB) - stream_deflate(L, &z, MAX_WBITS + 16, buffer, bsize); + stream_deflate(L, &z, Z_FINISH, MAX_WBITS + 16, buffer, bsize); #else - stream_deflate(L, &z, -MAX_WBITS, buffer, bsize); - lua_pushlstring(L, /* GZIP Header */"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x13", 10); - lua_pushvalue(L, -3); lua_concat(L, 2); lua_pushvalue(L, -2); + // 计算文件长度 与 CRC32 校验 + stream_deflate(L, &z, Z_FINISH, -MAX_WBITS, buffer, bsize); + size_t tsize; const char *text = lua_tolstring(L, -2, &tsize); + // 拷贝`GZIP`头部与压缩帧 + char *data = lua_newuserdata(L, tsize + 18); + memcpy(data, "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x13", 10); memcpy(data + 10, text, tsize); + // 拷贝`CRC32`与`ISIZE`长度 + set_int32(data + tsize + 10, crc32(0, buffer, bsize)); + set_int32(data + tsize + 14, (uint32_t)bsize % 0xffffffff); + // `GZIP`包装完成 + lua_pushlstring(L, data, tsize + 18); lua_pushinteger(L, bsize); #endif return 2; } @@ -193,7 +212,7 @@ int lgzip_uncompress(lua_State* L) { int ldeflate_compress(lua_State* L) { z_stream z; stream_init(&z); size_t bsize; const uint8_t *buffer = (const uint8_t *)luaL_checklstring(L, 1, &bsize); - return stream_deflate(L, &z, -MAX_WBITS, buffer, bsize); + return stream_deflate(L, &z, Z_FINISH, -MAX_WBITS, buffer, bsize); } int ldeflate_uncompress(lua_State* L) { @@ -206,7 +225,7 @@ int ldeflate_uncompress(lua_State* L) { int lzlib_compress(lua_State* L) { z_stream z; stream_init(&z); size_t bsize; const uint8_t *buffer = (const uint8_t *)luaL_checklstring(L, 1, &bsize); - return stream_deflate(L, &z, MAX_WBITS, buffer, bsize); + return stream_deflate(L, &z, Z_FINISH, MAX_WBITS, buffer, bsize); } int lzlib_uncompress(lua_State* L) { From 9e57ee9c3b216ddda3f348d5580f4d6c7aa4f34c Mon Sep 17 00:00:00 2001 From: "869646063@qq.com" <869646063@qq.com> Date: Sun, 19 Mar 2023 11:25:13 +0800 Subject: [PATCH 914/956] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dtable=E5=BC=95?= =?UTF-8?q?=E7=94=A8=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/admin/html/system/user/user.html | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lualib/admin/html/system/user/user.html b/lualib/admin/html/system/user/user.html index 8d3781cf..df1b141c 100644 --- a/lualib/admin/html/system/user/user.html +++ b/lualib/admin/html/system/user/user.html @@ -47,7 +47,7 @@ -
                  +