├── .gitignore ├── README.md ├── .lua-format ├── Makefile ├── api.lua ├── .clang-format ├── test.lua ├── buffer.hpp └── lyyjson.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | .libs/ 3 | *.gcda 4 | *.gcno 5 | *.profraw 6 | *.dSYM 7 | *.so 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lua-yyjson 2 | 3 | 代码搬运来源 4 | https://github.com/sniper00/moon/blob/master/lualib-src/lua_json.cpp 5 | -------------------------------------------------------------------------------- /.lua-format: -------------------------------------------------------------------------------- 1 | break_after_table_lb: true 2 | break_before_table_rb: false 3 | break_before_functioncall_rp: true 4 | break_before_functiondef_rp: true 5 | chop_down_table: true 6 | extra_sep_at_table_end: true 7 | keep_simple_control_block_one_line: false 8 | keep_simple_function_one_line: false 9 | column_table_limit: 1 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SKYNET_ROOT ?= ../skynet 2 | include $(SKYNET_ROOT)/platform.mk 3 | 4 | PLAT ?= none 5 | 6 | TARGET = ./yyjson.so 7 | 8 | YYJSON_INC ?= ../yyjson/ 9 | YYJSON_SRC ?= ./yyjson/ 10 | 11 | CXX=g++ 12 | 13 | ifeq ($(PLAT), macosx) 14 | CXXFLAGS = -g -O2 -pedantic -bundle -undefined dynamic_lookup -std=c++17 15 | else 16 | ifeq ($(PLAT), linux) 17 | CXXFLAGS = -g -O2 -shared -fPIC -std=c++17 18 | endif 19 | endif 20 | 21 | LUA_LIB ?= $(SKYNET_ROOT)/3rd/lua/ 22 | LUA_INC ?= $(SKYNET_ROOT)/3rd/lua/ 23 | 24 | SRC = . 25 | 26 | .PHONY: all clean 27 | 28 | all: $(TARGET) 29 | 30 | $(TARGET): $(foreach dir, $(SRC), $(wildcard $(dir)/*.cpp)) \ 31 | $(foreach dir, $(YYJSON_SRC), $(wildcard $(dir)/*.c)) 32 | 33 | $(CXX) $(CXXFLAGS) -o $@ $^ -I$(YYJSON_INC) -I$(LUA_INC) 34 | 35 | 36 | clean: 37 | rm -f *.o $(TARGET) && \ 38 | rm -rf *.dSYM -------------------------------------------------------------------------------- /api.lua: -------------------------------------------------------------------------------- 1 | ---@meta 2 | 3 | error("DO NOT REQUIRE THIS FILE") 4 | 5 | ---@class json 6 | ---@field null lightuserdata @ Represents json "null" values 7 | local yyjson = {} 8 | 9 | ---@param t table|number|string|boolean 10 | ---@param empty_as_array? boolean @default true 11 | ---@param format? boolean @default true, pretty 12 | ---@return string 13 | function yyjson.encode(t, empty_as_array, format) 14 | end 15 | 16 | ---@param t table 17 | ---@return string 18 | function yyjson.pretty_encode(t) end 19 | 20 | ---@param str string|cstring_ptr 21 | ---@param n? integer 22 | ---@return table 23 | function yyjson.decode(str, n) end 24 | 25 | --- Concat array item to string, table type will encode to json string 26 | ---@param array any[] 27 | ---@return buffer_ptr 28 | ---@overload fun(str:string):buffer_ptr 29 | function yyjson.concat(array) end 30 | 31 | --- concat params as redis protocol, table type will encode to json string 32 | ---@return buffer_ptr 33 | function yyjson.concat_resp(...) end 34 | 35 | return yyjson -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: LLVM 4 | AlignAfterOpenBracket: Align 5 | AlignArrayOfStructures: Left 6 | AlignConsecutiveAssignments: AcrossComments 7 | AlignConsecutiveBitFields: AcrossComments 8 | AlignConsecutiveDeclarations: None 9 | AlignConsecutiveMacros: AcrossComments 10 | AlignEscapedNewlines: Right 11 | AlignOperands: Align 12 | AlignTrailingComments: true 13 | AllowAllArgumentsOnNextLine: true 14 | AllowAllParametersOfDeclarationOnNextLine: false 15 | AllowShortBlocksOnASingleLine: Never 16 | AllowShortCaseLabelsOnASingleLine: false 17 | AllowShortEnumsOnASingleLine: false 18 | AllowShortFunctionsOnASingleLine: Empty 19 | AllowShortIfStatementsOnASingleLine: Never 20 | AllowShortLoopsOnASingleLine: false 21 | AlwaysBreakAfterReturnType: None 22 | AlwaysBreakBeforeMultilineStrings: false 23 | AlwaysBreakTemplateDeclarations: No 24 | BinPackArguments: true 25 | BinPackParameters: true 26 | BitFieldColonSpacing: None 27 | BraceWrapping: 28 | AfterCaseLabel: false 29 | AfterControlStatement: Never 30 | AfterEnum: false 31 | AfterFunction: false 32 | AfterStruct: false 33 | AfterUnion: false 34 | AfterExternBlock: false 35 | BeforeCatch: false 36 | BeforeElse: false 37 | BeforeWhile: false 38 | IndentBraces: false 39 | SplitEmptyFunction: true 40 | SplitEmptyRecord: true 41 | BreakBeforeBinaryOperators: None 42 | BreakBeforeBraces: Linux 43 | BreakBeforeTernaryOperators: false 44 | BreakStringLiterals: true 45 | ColumnLimit: 80 46 | CommentPragmas: '^ IWYU pragma:' 47 | ContinuationIndentWidth: 4 48 | DeriveLineEnding: true 49 | DerivePointerAlignment: true 50 | DisableFormat: false 51 | ExperimentalAutoDetectBinPacking: false 52 | FixNamespaceComments: true 53 | ForEachMacros: 54 | - foreach 55 | - Q_FOREACH 56 | - BOOST_FOREACH 57 | IncludeBlocks: Preserve 58 | IncludeCategories: 59 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 60 | Priority: 2 61 | SortPriority: 0 62 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 63 | Priority: 3 64 | SortPriority: 0 65 | - Regex: '.*' 66 | Priority: 1 67 | SortPriority: 0 68 | IncludeIsMainRegex: '(Test)?$' 69 | IncludeIsMainSourceRegex: '' 70 | IndentCaseBlocks: false 71 | IndentCaseLabels: false 72 | IndentExternBlock: NoIndent 73 | IndentGotoLabels: false 74 | IndentPPDirectives: AfterHash 75 | IndentWidth: 4 76 | IndentWrappedFunctionNames: false 77 | InsertTrailingCommas: None 78 | KeepEmptyLinesAtTheStartOfBlocks: false 79 | MacroBlockBegin: '' 80 | MacroBlockEnd: '' 81 | MaxEmptyLinesToKeep: 1 82 | NamespaceIndentation: None 83 | PPIndentWidth: 1 84 | PenaltyBreakAssignment: 2 85 | PenaltyBreakBeforeFirstCallParameter: 19 86 | PenaltyBreakComment: 300 87 | PenaltyBreakFirstLessLess: 120 88 | PenaltyBreakString: 1000 89 | PenaltyBreakTemplateDeclaration: 10 90 | PenaltyExcessCharacter: 1000000 91 | PenaltyReturnTypeOnItsOwnLine: 60 92 | PointerAlignment: Right 93 | # QualifierAlignment Leave 94 | # QualifierOrder [] 95 | ReflowComments: true 96 | SortIncludes: CaseSensitive 97 | SpaceAfterCStyleCast: false 98 | SpaceAfterLogicalNot: false 99 | SpaceBeforeAssignmentOperators: true 100 | SpaceBeforeCaseColon: false 101 | SpaceBeforeParens: ControlStatements 102 | SpaceBeforeRangeBasedForLoopColon: false 103 | SpaceBeforeSquareBrackets: false 104 | SpaceInEmptyBlock: false 105 | SpaceInEmptyParentheses: false 106 | SpacesBeforeTrailingComments: 1 107 | SpacesInCStyleCastParentheses: false 108 | SpacesInConditionalStatement: false 109 | SpacesInContainerLiterals: false 110 | SpacesInParentheses: false 111 | SpacesInSquareBrackets: false 112 | Standard: Latest 113 | StatementMacros: 114 | - Q_UNUSED 115 | - QT_REQUIRE_VERSION 116 | TabWidth: 8 117 | UseCRLF: false 118 | UseTab: Never 119 | WhitespaceSensitiveMacros: 120 | - STRINGIZE 121 | - PP_STRINGIZE 122 | - BOOST_PP_STRINGIZE 123 | ... 124 | -------------------------------------------------------------------------------- /test.lua: -------------------------------------------------------------------------------- 1 | local json = require("yyjson") 2 | 3 | do 4 | local double = 2 ^ 53 5 | print(json.encode(double)) 6 | assert(json.encode(double)=="9007199254740992") 7 | assert(json.decode(json.encode(double))==double) 8 | end 9 | 10 | do 11 | local t1 = {} 12 | t1[4] = "d" 13 | t1[2] = "b" 14 | t1[3] = "c" 15 | t1[1] = "a" 16 | local str = json.encode(t1) --["a","b","c","d"] 17 | print(str) 18 | assert(string.sub(str,1,1)=="[") 19 | local t2 = json.decode(str) 20 | assert(#t2==4) 21 | assert(t2[1]=="a") 22 | assert(t2[2]=="b") 23 | assert(t2[3]=="c") 24 | assert(t2[4]=="d") 25 | end 26 | 27 | do 28 | local t1 = {[4] = "d", [2]="b", [3]="c", [1]="a"} 29 | local str = json.encode(t1) --["a","b","c","d"] 30 | print(str) 31 | assert(string.sub(str,1,1)=="[") 32 | local t2 = json.decode(str) 33 | assert(#t2==4) 34 | assert(t2[1]=="a") 35 | assert(t2[2]=="b") 36 | assert(t2[3]=="c") 37 | assert(t2[4]=="d") 38 | end 39 | 40 | do 41 | local t1 = {} 42 | table.insert(t1,"a") 43 | table.insert(t1,"b") 44 | table.insert(t1,"c") 45 | table.insert(t1,"d") 46 | local str = json.encode(t1) --["a","b","c","d"] 47 | print(str) 48 | assert(string.sub(str,1,1)=="[") 49 | local t2 = json.decode(str) 50 | assert(#t2==4) 51 | assert(t2[1]=="a") 52 | assert(t2[2]=="b") 53 | assert(t2[3]=="c") 54 | assert(t2[4]=="d") 55 | end 56 | 57 | do 58 | local t1 = {"a","b","c","d"} 59 | local str = json.encode(t1) --["a","b","c","d"] 60 | print(str) 61 | assert(string.sub(str,1,1)=="[") 62 | local t2 = json.decode(str) 63 | assert(#t2==4) 64 | assert(t2[1]=="a") 65 | assert(t2[2]=="b") 66 | assert(t2[3]=="c") 67 | assert(t2[4]=="d") 68 | end 69 | 70 | do 71 | local t1 = {"a","b",[5]="c","d", e={ 72 | w = 1, 73 | x = 2 74 | }} 75 | local str = json.encode(t1) --{"1":"a","2":"b","3":"d","5":"c"} 76 | print(str) 77 | assert(string.sub(str,1,1)=="{") 78 | local t2 = json.decode(str) 79 | assert(t2[1]=="a") 80 | assert(t2[2]=="b") 81 | assert(t2[5]=="c") 82 | assert(t2[3]=="d") 83 | end 84 | 85 | do 86 | local t1 = {[1] = "a", [2] = "b",[100] = "c",} 87 | local str = json.encode(t1) --{"1":"a","2":"b","100":"c"} 88 | print(str) 89 | assert(string.sub(str,1,1)=="{") 90 | local t2 = json.decode(str) 91 | assert(t2[1]=="a") 92 | assert(t2[2]=="b") 93 | assert(t2[100]=="c") 94 | end 95 | 96 | do 97 | local t1 = {["a"]=1,["b"]=2,["c"] =3} 98 | local str = json.encode(t1) -- {"b":2,"c":3,"a":1} 99 | print(str) 100 | assert(string.sub(str,1,1)=="{") 101 | local t2 = json.decode(str) 102 | assert(t2["a"]==1) 103 | assert(t2["b"]==2) 104 | assert(t2["c"]==3) 105 | end 106 | 107 | do 108 | local t1 = {[100] = "a", [200] = "b",[300] = "c",} 109 | local str = json.encode(t1) -- {"300":"c","100":"a","200":"b"} 110 | print(str) 111 | assert(string.sub(str,1,1)=="{") 112 | local t2 = json.decode(str) 113 | assert(t2[100]=="a") 114 | assert(t2[200]=="b") 115 | assert(t2[300]=="c") 116 | end 117 | 118 | do 119 | local t1 = {["1.0"]=1,["2.0"]=2,["3.0"] =3} 120 | local str = json.encode(t1) -- {"1.0":1,"3.0":3,"2.0":2} 121 | print(str) 122 | assert(string.sub(str,1,1)=="{") 123 | local t2 = json.decode(str) 124 | assert(t2["1.0"]==1) 125 | assert(t2["2.0"]==2) 126 | assert(t2["3.0"]==3) 127 | end 128 | 129 | do 130 | local t1 = {["100abc"]="hello"} 131 | local str = json.encode(t1) -- {"100abc":"hello"} 132 | print(str) 133 | assert(string.sub(str,1,1)=="{") 134 | local t2 = json.decode(str) 135 | assert(t2["100abc"]=="hello") 136 | end 137 | 138 | do 139 | ---issue case: try convert string key to integer 140 | local t1 = {["1"]=1,["2"]=2,["3"] =3} 141 | local str = json.encode(t1) -- {"1":1,"2":2,"3":3} 142 | print(str) 143 | assert(string.sub(str,1,1)=="{","adadadad") 144 | local t2 = json.decode(str) 145 | assert(t2[1]==1) 146 | assert(t2[2]==2) 147 | assert(t2[3]==3) 148 | 149 | str = json.encode(t2) -- [1,2,3] 150 | assert(string.sub(str,1,1)=="[") 151 | end 152 | 153 | do 154 | local ok, err = xpcall(json.encode, debug.traceback, { a = function() 155 | end }) 156 | assert(not ok, err) 157 | end 158 | 159 | do 160 | local ok, err = xpcall(json.concat, debug.traceback, { function() 161 | end }) 162 | assert(not ok, err) 163 | end 164 | 165 | do 166 | local ok, err = xpcall(json.concat_resp, debug.traceback, { function() 167 | end }) 168 | assert(not ok, err) 169 | end 170 | 171 | do 172 | local t = {nil,nil,nil, 100} 173 | assert(string.sub(json.encode(t),1,1)=="[") 174 | assert(#json.decode(json.encode(t)) == 4) 175 | local t2 = json.decode(json.encode(t)) 176 | assert(t2[1]==json.null) 177 | assert(t2[2]==json.null) 178 | assert(t2[3]==json.null) 179 | assert(t2[4]==100) 180 | end -------------------------------------------------------------------------------- /buffer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace lyyjson 14 | { 15 | template 16 | class buffer_iterator 17 | { 18 | public: 19 | using iterator_category = std::random_access_iterator_tag; 20 | using self_type = buffer_iterator; 21 | using value_type = ValueType; 22 | using pointer = value_type*; 23 | using reference = value_type&; 24 | using difference_type = std::ptrdiff_t; 25 | 26 | explicit buffer_iterator(pointer p) 27 | :_Ptr(p) 28 | { 29 | } 30 | 31 | reference operator*() const 32 | { 33 | return *_Ptr; 34 | } 35 | 36 | buffer_iterator& operator++() 37 | { 38 | ++_Ptr; 39 | return *this; 40 | } 41 | 42 | buffer_iterator operator++(int) 43 | { // postincrement 44 | buffer_iterator _Tmp = *this; 45 | ++* this; 46 | return (_Tmp); 47 | } 48 | 49 | buffer_iterator& operator--() 50 | { 51 | --_Ptr; 52 | return *this; 53 | } 54 | 55 | buffer_iterator operator--(int) 56 | { // postdecrement 57 | buffer_iterator _Tmp = *this; 58 | --* this; 59 | return (_Tmp); 60 | } 61 | 62 | buffer_iterator& operator+=(const difference_type _Off) 63 | { // increment by integer 64 | _Ptr += _Off; 65 | return (*this); 66 | } 67 | 68 | buffer_iterator operator+(const difference_type _Off) const 69 | { // return this + integer 70 | buffer_iterator _Tmp = *this; 71 | return (_Tmp += _Off); 72 | } 73 | 74 | buffer_iterator& operator-=(const difference_type _Off) 75 | { // decrement by integer 76 | return (*this += -_Off); 77 | } 78 | 79 | buffer_iterator operator-(const difference_type _Off) const 80 | { // return this - integer 81 | buffer_iterator _Tmp = *this; 82 | return (_Tmp -= _Off); 83 | } 84 | 85 | reference operator[](const difference_type _Off) const 86 | { // subscript 87 | return (*(*this + _Off)); 88 | } 89 | 90 | difference_type operator-(const buffer_iterator& _Right) const 91 | { // return difference of iterators 92 | return (_Ptr - _Right._Ptr); 93 | } 94 | 95 | bool operator!=(const buffer_iterator& other) const 96 | { 97 | return _Ptr != other._Ptr; 98 | } 99 | 100 | bool operator==(const buffer_iterator& _Right) const 101 | { // test for iterator equality 102 | return (_Ptr == _Right._Ptr); 103 | } 104 | 105 | bool operator<(const buffer_iterator& _Right) const 106 | { // test if this < _Right 107 | return (_Ptr < _Right._Ptr); 108 | } 109 | 110 | bool operator>(const buffer_iterator& _Right) const 111 | { // test if this > _Right 112 | return (_Right < *this); 113 | } 114 | 115 | bool operator<=(const buffer_iterator& _Right) const 116 | { // test if this <= _Right 117 | return (!(_Right < *this)); 118 | } 119 | 120 | bool operator>=(const buffer_iterator& _Right) const 121 | { // test if this >= _Right 122 | return (!(*this < _Right)); 123 | } 124 | private: 125 | pointer _Ptr; 126 | }; 127 | 128 | template 129 | class base_buffer 130 | { 131 | public: 132 | using allocator_type = Alloc; 133 | using value_type = typename allocator_type::value_type; 134 | using iterator = buffer_iterator; 135 | using const_iterator = buffer_iterator; 136 | using pointer = typename iterator::pointer; 137 | using const_pointer = typename const_iterator::pointer; 138 | 139 | //buffer default size 140 | constexpr static size_t DEFAULT_CAPACITY = 128; 141 | 142 | enum class seek_origin 143 | { 144 | Begin, 145 | Current, 146 | }; 147 | 148 | base_buffer(size_t capacity = DEFAULT_CAPACITY, uint32_t headreserved = 0, const allocator_type& al = allocator_type()) 149 | : allocator_(al) 150 | , flag_(0) 151 | , headreserved_(headreserved) 152 | , capacity_(0) 153 | , readpos_(0) 154 | , writepos_(0) 155 | { 156 | capacity = (capacity > 0 ? capacity : DEFAULT_CAPACITY); 157 | prepare(capacity + headreserved); 158 | readpos_ = writepos_ = headreserved_; 159 | } 160 | 161 | base_buffer(const base_buffer&) = delete; 162 | 163 | base_buffer& operator=(const base_buffer&) = delete; 164 | 165 | base_buffer(base_buffer&& other) noexcept 166 | : allocator_(std::move(other.allocator_)) 167 | , flag_(std::exchange(other.flag_, 0)) 168 | , headreserved_(std::exchange(other.headreserved_, 0)) 169 | , capacity_(std::exchange(other.capacity_, 0)) 170 | , readpos_(std::exchange(other.readpos_, 0)) 171 | , writepos_(std::exchange(other.writepos_, 0)) 172 | , data_(std::exchange(other.data_, nullptr)) 173 | { 174 | } 175 | 176 | base_buffer& operator=(base_buffer&& other) noexcept 177 | { 178 | if (this != std::addressof(*other)) 179 | { 180 | allocator_.deallocate(data_, capacity_); 181 | flag_ = std::exchange(other.flag_, 0); 182 | headreserved_ = std::exchange(other.headreserved_, 0); 183 | capacity_ = std::exchange(other.capacity_, 0); 184 | readpos_ = std::exchange(other.readpos_, 0); 185 | writepos_ = std::exchange(other.writepos_, 0); 186 | data_ = std::exchange(other.data_, nullptr); 187 | allocator_ = std::move(other.allocator_); 188 | } 189 | return *this; 190 | } 191 | 192 | ~base_buffer() 193 | { 194 | allocator_.deallocate(data_, capacity_); 195 | } 196 | 197 | template 198 | void write_back(const T* Indata, size_t count) 199 | { 200 | static_assert(std::is_trivially_copyable::value, "type T must be trivially copyable"); 201 | if (nullptr == Indata || 0 == count) 202 | return; 203 | size_t n = sizeof(T) * count; 204 | auto* buff = reinterpret_cast(prepare(n)); 205 | memcpy(buff, Indata, n); 206 | writepos_ += n; 207 | } 208 | 209 | void write_back(char c) 210 | { 211 | *prepare(1) = c; 212 | ++writepos_; 213 | } 214 | 215 | void unsafe_write_back(char c) 216 | { 217 | *(data_ + (writepos_++)) = c; 218 | } 219 | 220 | template 221 | bool write_front(const T* Indata, size_t count) noexcept 222 | { 223 | static_assert(std::is_trivially_copyable::value, "type T must be trivially copyable"); 224 | if (nullptr == Indata || 0 == count) 225 | return false; 226 | 227 | size_t n = sizeof(T) * count; 228 | 229 | if (n > readpos_) 230 | { 231 | return false; 232 | } 233 | 234 | readpos_ -= n; 235 | auto* buff = reinterpret_cast(std::addressof(*begin())); 236 | memcpy(buff, Indata, n); 237 | return true; 238 | } 239 | 240 | template>> 241 | bool write_chars(T value) 242 | { 243 | static constexpr size_t MAX_NUMBER_2_STR = 44; 244 | auto* b = prepare(MAX_NUMBER_2_STR); 245 | #ifndef _MSC_VER//std::to_chars in C++17: gcc and clang only integral types supported 246 | if constexpr (std::is_floating_point_v) 247 | { 248 | int len = std::snprintf(b, MAX_NUMBER_2_STR, "%.16g", value); 249 | if (len < 0) 250 | return false; 251 | commit(len); 252 | return true; 253 | } 254 | else 255 | #endif 256 | { 257 | auto* e = b + MAX_NUMBER_2_STR; 258 | auto res = std::to_chars(b, e, value); 259 | if (res.ec != std::errc()) 260 | return false; 261 | commit(res.ptr - b); 262 | return true; 263 | } 264 | } 265 | 266 | template 267 | bool read(T* Outdata, size_t count) noexcept 268 | { 269 | static_assert(std::is_trivially_copyable::value, "type T must be trivially copyable"); 270 | if (nullptr == Outdata || 0 == count) 271 | return false; 272 | 273 | size_t n = sizeof(T) * count; 274 | 275 | if (readpos_ + n > writepos_) 276 | { 277 | return false; 278 | } 279 | 280 | auto* buff = std::addressof(*begin()); 281 | memcpy(Outdata, buff, n); 282 | readpos_ += n; 283 | return true; 284 | } 285 | 286 | void consume(std::size_t n) noexcept 287 | { 288 | seek(n); 289 | } 290 | 291 | //set read or forward read pos 292 | bool seek(size_t offset, seek_origin s = seek_origin::Current) noexcept 293 | { 294 | switch (s) 295 | { 296 | case seek_origin::Begin: 297 | if (offset > writepos_) 298 | return false; 299 | readpos_ = offset; 300 | break; 301 | case seek_origin::Current: 302 | if (readpos_ + offset > writepos_) 303 | return false; 304 | readpos_ += offset; 305 | break; 306 | default: 307 | assert(false); 308 | return false; 309 | } 310 | return true; 311 | } 312 | 313 | void clear() noexcept 314 | { 315 | flag_ = 0; 316 | writepos_ = readpos_ = headreserved_; 317 | } 318 | 319 | template 320 | void set_flag(ValueType v) noexcept 321 | { 322 | flag_ |= static_cast(v); 323 | } 324 | 325 | template 326 | bool has_flag(ValueType v) const noexcept 327 | { 328 | return ((flag_ & static_cast(v)) != 0); 329 | } 330 | 331 | template 332 | void clear_flag(ValueType v) noexcept 333 | { 334 | flag_ &= ~static_cast(v); 335 | } 336 | 337 | void commit(std::size_t n) noexcept 338 | { 339 | writepos_ += n; 340 | assert(writepos_ <= capacity_); 341 | if (writepos_ >= capacity_) 342 | { 343 | writepos_ = capacity_; 344 | } 345 | } 346 | 347 | pointer revert(size_t n) noexcept 348 | { 349 | assert(writepos_ >= (readpos_+n)); 350 | if (writepos_ >= n) 351 | { 352 | writepos_ -= n; 353 | } 354 | return (data_ + writepos_); 355 | } 356 | 357 | const_iterator begin() const noexcept 358 | { 359 | return const_iterator{ data_ + readpos_ }; 360 | } 361 | 362 | iterator begin() noexcept 363 | { 364 | return iterator{ data_ + readpos_ }; 365 | } 366 | 367 | const_iterator end() const noexcept 368 | { 369 | return const_iterator{ data_ + writepos_ }; 370 | } 371 | 372 | iterator end() noexcept 373 | { 374 | return iterator{ data_ + writepos_ }; 375 | } 376 | 377 | pointer data() noexcept 378 | { 379 | return std::addressof(*begin()); 380 | } 381 | 382 | const_pointer data() const noexcept 383 | { 384 | return std::addressof(*begin()); 385 | } 386 | 387 | //readable size 388 | size_t size() const noexcept 389 | { 390 | return writepos_ - readpos_; 391 | } 392 | 393 | size_t capacity() const noexcept 394 | { 395 | return capacity_; 396 | } 397 | 398 | size_t reserved() const noexcept 399 | { 400 | return headreserved_; 401 | } 402 | 403 | pointer prepare(size_t need) 404 | { 405 | if (writeablesize() >= need) 406 | { 407 | return (data_ + writepos_); 408 | } 409 | 410 | if (writeablesize() + readpos_ < need + headreserved_) 411 | { 412 | auto required_size = writepos_ + need; 413 | required_size = next_pow2(required_size); 414 | auto tmp = allocator_.allocate(required_size); 415 | if (nullptr != data_) 416 | { 417 | std::memcpy(tmp, data_, writepos_); 418 | allocator_.deallocate(data_, capacity_); 419 | } 420 | data_ = tmp; 421 | capacity_ = required_size; 422 | } 423 | else 424 | { 425 | size_t readable = size(); 426 | if (readable != 0) 427 | { 428 | assert(readpos_ >= headreserved_); 429 | std::memmove(data_ + headreserved_, data_ + readpos_, readable); 430 | } 431 | readpos_ = headreserved_; 432 | writepos_ = readpos_ + readable; 433 | } 434 | return (data_ + writepos_); 435 | } 436 | 437 | size_t writeablesize() const noexcept 438 | { 439 | assert(capacity_ >= writepos_); 440 | return capacity_ - writepos_; 441 | } 442 | 443 | private: 444 | size_t next_pow2(size_t x) 445 | { 446 | if (!(x & (x - 1))) 447 | { 448 | return x; 449 | } 450 | x |= x >> 1; 451 | x |= x >> 2; 452 | x |= x >> 4; 453 | x |= x >> 8; 454 | x |= x >> 16; 455 | return x + 1; 456 | } 457 | private: 458 | allocator_type allocator_; 459 | 460 | uint32_t flag_; 461 | 462 | uint32_t headreserved_; 463 | 464 | size_t capacity_; 465 | //read position 466 | size_t readpos_; 467 | //write position 468 | size_t writepos_; 469 | value_type* data_ = nullptr; 470 | }; 471 | }; 472 | 473 | #ifdef MOON_ENABLE_MIMALLOC 474 | #include "mimalloc.h" 475 | namespace lyyjson 476 | { 477 | using buffer = base_buffer>; 478 | } 479 | #else 480 | namespace lyyjson 481 | { 482 | using buffer = base_buffer>; 483 | } 484 | #endif 485 | 486 | 487 | 488 | -------------------------------------------------------------------------------- /lyyjson.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include "yyjson/yyjson.h" 7 | #include "buffer.hpp" 8 | 9 | #ifdef MOON_ENABLE_MIMALLOC 10 | static void* json_malloc(void*, size_t size) { 11 | return mi_malloc(size); 12 | } 13 | 14 | static void* json_realloc(void*, void* ptr, size_t size) 15 | { 16 | return mi_realloc(ptr, size); 17 | } 18 | 19 | static void json_free(void*, void* ptr) 20 | { 21 | mi_free(ptr); 22 | } 23 | 24 | static const yyjson_alc allocator = {json_malloc, json_realloc, json_free,nullptr}; 25 | #else 26 | static const yyjson_alc allocator = { nullptr }; 27 | #endif 28 | 29 | using namespace std::literals::string_view_literals; 30 | using namespace lyyjson; 31 | 32 | static constexpr std::string_view json_null = "null"sv; 33 | static constexpr std::string_view json_true = "true"sv; 34 | static constexpr std::string_view json_false = "false"sv; 35 | 36 | static constexpr int EMPTY_AS_ARRAY = 1; 37 | 38 | static constexpr int MAX_DEPTH = 64; 39 | 40 | static const char char2escape[256] = { 41 | 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', 'u', 'u', 'u', 'u', // 0~19 42 | 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 0, 0, '"', 0, 0, 0, 0, 0, // 20~39 43 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40~59 44 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60~79 45 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\\', 0, 0, 0, 0, 0, 0, 0, // 80~99 46 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 100~119 47 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 120~139 48 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 140~159 49 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160~179 50 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 180~199 51 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 200~219 52 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 220~239 53 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 240~256 54 | }; 55 | 56 | static const char hex_digits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 57 | 58 | template 59 | static void format_new_line(buffer* writer) 60 | { 61 | if constexpr (format) 62 | { 63 | writer->write_back('\n'); 64 | } 65 | } 66 | 67 | template 68 | static void format_space(buffer* writer, int n) 69 | { 70 | if constexpr (format) 71 | { 72 | writer->prepare(2 * size_t(n)); 73 | for (int i = 0; i < n; ++i) 74 | { 75 | writer->unsafe_write_back(' '); 76 | writer->unsafe_write_back(' '); 77 | } 78 | } 79 | } 80 | 81 | template 82 | static void encode_table(lua_State* L, buffer* writer, int idx, int depth); 83 | 84 | template 85 | static void encode_one(lua_State* L, buffer* writer, int idx, int depth = 0) 86 | { 87 | int t = lua_type(L, idx); 88 | switch (t) 89 | { 90 | case LUA_TBOOLEAN: 91 | { 92 | if (lua_toboolean(L, idx)) 93 | writer->write_back(json_true.data(), json_true.size()); 94 | else 95 | writer->write_back(json_false.data(), json_false.size()); 96 | return; 97 | } 98 | case LUA_TNUMBER: 99 | { 100 | if (lua_isinteger(L, idx)) 101 | writer->write_chars(lua_tointeger(L, idx)); 102 | else 103 | writer->write_chars(lua_tonumber(L, idx)); 104 | return; 105 | } 106 | case LUA_TSTRING: 107 | { 108 | size_t len = 0; 109 | const char* str = lua_tolstring(L, idx, &len); 110 | writer->prepare(len * 6 + 2); 111 | writer->unsafe_write_back('\"'); 112 | for (size_t i = 0; i < len; ++i) 113 | { 114 | unsigned char ch = (unsigned char)str[i]; 115 | char esc = char2escape[ch]; 116 | if (!esc) 117 | { 118 | writer->unsafe_write_back((char)ch); 119 | } 120 | else 121 | { 122 | writer->unsafe_write_back('\\'); 123 | writer->unsafe_write_back(esc); 124 | if (esc == 'u') 125 | { 126 | writer->unsafe_write_back('0'); 127 | writer->unsafe_write_back('0'); 128 | writer->unsafe_write_back(hex_digits[(unsigned char)esc >> 4]); 129 | writer->unsafe_write_back(hex_digits[(unsigned char)esc & 0xF]); 130 | } 131 | } 132 | } 133 | writer->unsafe_write_back('\"'); 134 | return; 135 | } 136 | case LUA_TTABLE: 137 | { 138 | encode_table(L, writer, idx, depth); 139 | return; 140 | } 141 | case LUA_TNIL: 142 | { 143 | writer->write_back(json_null.data(), json_null.size()); 144 | return; 145 | } 146 | case LUA_TLIGHTUSERDATA: 147 | { 148 | if (lua_touserdata(L, idx) == nullptr) 149 | { 150 | writer->write_back(json_null.data(), json_null.size()); 151 | return; 152 | } 153 | break; 154 | } 155 | } 156 | throw std::logic_error{ std::string("json encode: unsupport value type :")+lua_typename(L, t)}; 157 | } 158 | 159 | static inline size_t array_size(lua_State* L, int index) 160 | { 161 | // test first key 162 | lua_pushnil(L); 163 | if (lua_next(L, index) == 0) // empty table 164 | return 0; 165 | 166 | lua_Integer firstkey = lua_isinteger(L, -2) ? lua_tointeger(L, -2) : 0; 167 | lua_pop(L, 2); 168 | 169 | if (firstkey <= 0) 170 | { 171 | return 0; 172 | } 173 | else if (firstkey == 1) 174 | { 175 | /* 176 | * https://www.lua.org/manual/5.4/manual.html#3.4.7 177 | * The length operator applied on a table returns a border in that table. 178 | * A border in a table t is any natural number that satisfies the following condition : 179 | * (border == 0 or t[border] ~= nil) and t[border + 1] == nil 180 | */ 181 | auto len = (lua_Integer)lua_rawlen(L, index); 182 | lua_pushinteger(L, len); 183 | if (lua_next(L, index)) // has more fields? 184 | { 185 | lua_pop(L, 2); 186 | return 0; 187 | } 188 | return len; 189 | } 190 | 191 | auto len = (lua_Integer)lua_rawlen(L, index); 192 | if (firstkey > len) 193 | return 0; 194 | 195 | lua_pushnil(L); 196 | while (lua_next(L, index) != 0) 197 | { 198 | if (lua_isinteger(L, -2)) 199 | { 200 | lua_Integer x = lua_tointeger(L, -2); 201 | if (x > 0 && x <= len) 202 | { 203 | lua_pop(L, 1); 204 | continue; 205 | } 206 | } 207 | lua_pop(L, 2); 208 | return 0; 209 | } 210 | return len; 211 | } 212 | 213 | template 214 | static void encode_table(lua_State* L, buffer* writer, int idx, int depth) 215 | { 216 | if ((++depth) > MAX_DEPTH) 217 | throw std::logic_error{ "nested too depth" }; 218 | 219 | if (idx < 0) 220 | { 221 | idx = lua_gettop(L) + idx + 1; 222 | } 223 | 224 | luaL_checkstack(L, 6, "json.encode_table"); 225 | 226 | if (size_t size = array_size(L, idx); size > 0) 227 | { 228 | writer->write_back('['); 229 | for (size_t i = 1; i <= size; i++) 230 | { 231 | if (i == 1) 232 | format_new_line(writer); 233 | format_space(writer, depth); 234 | lua_rawgeti(L, idx, i); 235 | encode_one(L, writer, -1, depth); 236 | lua_pop(L, 1); 237 | if (i < size) 238 | writer->write_back(','); 239 | format_new_line(writer); 240 | } 241 | format_space(writer, depth - 1); 242 | writer->write_back(']'); 243 | } 244 | else 245 | { 246 | size_t i = 0; 247 | writer->write_back('{'); 248 | lua_pushnil(L); // [table, nil] 249 | while (lua_next(L, idx)) 250 | { 251 | if (i++ > 0) 252 | writer->write_back(','); 253 | format_new_line(writer); 254 | int key_type = lua_type(L, -2); 255 | switch (key_type) 256 | { 257 | case LUA_TSTRING: 258 | { 259 | format_space(writer, depth); 260 | size_t len = 0; 261 | const char* key = lua_tolstring(L, -2, &len); 262 | writer->write_back('\"'); 263 | writer->write_back(key, len); 264 | writer->write_back('\"'); 265 | writer->write_back(':'); 266 | if constexpr (format) 267 | writer->write_back(' '); 268 | encode_one(L, writer, -1, depth); 269 | break; 270 | } 271 | case LUA_TNUMBER: 272 | { 273 | if (lua_isinteger(L, -2)) 274 | { 275 | format_space(writer, depth); 276 | lua_Integer key = lua_tointeger(L, -2); 277 | writer->write_back('\"'); 278 | writer->write_chars(key); 279 | writer->write_back('\"'); 280 | writer->write_back(':'); 281 | if constexpr (format) 282 | writer->write_back(' '); 283 | encode_one(L, writer, -1, depth); 284 | } 285 | else 286 | { 287 | throw std::logic_error{ "json encode: not support double type 'key'." }; 288 | } 289 | break; 290 | } 291 | default: 292 | throw std::logic_error{std::string("json encode: unsupport key type : ") + lua_typename(L, key_type)}; 293 | } 294 | lua_pop(L, 1); 295 | } 296 | 297 | if (i == 0 && writer->has_flag(EMPTY_AS_ARRAY)) 298 | { 299 | writer->revert(1); 300 | writer->write_back('['); 301 | writer->write_back(']'); 302 | } 303 | else 304 | { 305 | if (i > 0) 306 | { 307 | format_new_line(writer); 308 | format_space(writer, depth - 1); 309 | } 310 | writer->write_back('}'); 311 | } 312 | } 313 | } 314 | 315 | static int encode(lua_State* L) 316 | { 317 | try 318 | { 319 | luaL_checkany(L, 1); 320 | buffer writer(512); 321 | bool empty_as_array = (bool)luaL_opt(L, lua_toboolean, 2, true); 322 | if (empty_as_array) 323 | writer.set_flag(EMPTY_AS_ARRAY); 324 | bool format = lua_toboolean(L, 3); 325 | lua_settop(L, 1); 326 | if (!format) 327 | encode_one(L, &writer, 1); 328 | else 329 | encode_one(L, &writer, 1); 330 | lua_pushlstring(L, writer.data(), writer.size()); 331 | return 1; 332 | } 333 | catch (const std::exception& ex) 334 | { 335 | lua_pushstring(L, ex.what()); 336 | } 337 | return lua_error(L); 338 | } 339 | 340 | static void decode_one(lua_State* L, yyjson_val* value) 341 | { 342 | yyjson_type type = yyjson_get_type(value); 343 | switch (type) 344 | { 345 | case YYJSON_TYPE_ARR: 346 | { 347 | luaL_checkstack(L, 6, "json.decode.array"); 348 | lua_createtable(L, (int)yyjson_arr_size(value), 0); 349 | lua_Integer pos = 1; 350 | yyjson_arr_iter iter; 351 | yyjson_arr_iter_init(value, &iter); 352 | while (nullptr != (value = yyjson_arr_iter_next(&iter))) 353 | { 354 | decode_one(L, value); 355 | lua_rawseti(L, -2, pos++); 356 | } 357 | break; 358 | } 359 | case YYJSON_TYPE_OBJ: 360 | { 361 | luaL_checkstack(L, 6, "json.decode.object"); 362 | lua_createtable(L, 0, (int)yyjson_obj_size(value)); 363 | 364 | yyjson_val* key, * val; 365 | yyjson_obj_iter iter; 366 | yyjson_obj_iter_init(value, &iter); 367 | while (nullptr != (key = yyjson_obj_iter_next(&iter))) 368 | { 369 | val = yyjson_obj_iter_get_val(key); 370 | std::string_view view{ unsafe_yyjson_get_str(key), unsafe_yyjson_get_len(key) }; 371 | if (view.size() > 0) 372 | { 373 | char c = view.data()[0]; 374 | if (c == '-' || (c >= '0' && c <= '9')) 375 | { 376 | const char* last = view.data() + view.size(); 377 | int64_t v = 0; 378 | auto [p, ec] = std::from_chars(view.data(), last, v); 379 | if (ec == std::errc() && p == last) 380 | lua_pushinteger(L, v); 381 | else 382 | lua_pushlstring(L, view.data(), view.size()); 383 | } 384 | else 385 | { 386 | lua_pushlstring(L, view.data(), view.size()); 387 | } 388 | decode_one(L, val); 389 | lua_rawset(L, -3); 390 | } 391 | } 392 | break; 393 | } 394 | case YYJSON_TYPE_NUM: 395 | { 396 | yyjson_subtype subtype = yyjson_get_subtype(value); 397 | switch (subtype) 398 | { 399 | case YYJSON_SUBTYPE_UINT: 400 | { 401 | lua_pushinteger(L, (int64_t)unsafe_yyjson_get_uint(value)); 402 | break; 403 | } 404 | case YYJSON_SUBTYPE_SINT: 405 | { 406 | lua_pushinteger(L, unsafe_yyjson_get_sint(value)); 407 | break; 408 | } 409 | case YYJSON_SUBTYPE_REAL: 410 | { 411 | lua_pushnumber(L, unsafe_yyjson_get_real(value)); 412 | break; 413 | } 414 | } 415 | break; 416 | } 417 | case YYJSON_TYPE_STR: 418 | { 419 | lua_pushlstring(L, unsafe_yyjson_get_str(value), unsafe_yyjson_get_len(value)); 420 | break; 421 | } 422 | case YYJSON_TYPE_BOOL: 423 | lua_pushboolean(L, (yyjson_get_subtype(value) == YYJSON_SUBTYPE_TRUE) ? 1 : 0); 424 | break; 425 | case YYJSON_TYPE_NULL: 426 | { 427 | lua_pushlightuserdata(L, nullptr); 428 | break; 429 | } 430 | default: 431 | break; 432 | } 433 | } 434 | 435 | static int decode(lua_State* L) 436 | { 437 | size_t len = 0; 438 | const char* str = nullptr; 439 | if (lua_type(L, 1) == LUA_TSTRING) 440 | { 441 | str = luaL_checklstring(L, 1, &len); 442 | } 443 | else 444 | { 445 | str = reinterpret_cast(lua_touserdata(L, 1)); 446 | len = luaL_checkinteger(L, 2); 447 | } 448 | 449 | if (nullptr == str || str[0] == '\0') 450 | return 0; 451 | 452 | lua_settop(L, 1); 453 | 454 | yyjson_read_err err; 455 | yyjson_doc* doc = yyjson_read_opts((char*)str, len, 0, (allocator.malloc) ? &allocator : nullptr, &err); 456 | if (nullptr == doc) 457 | { 458 | return luaL_error(L, "decode error: %s code: %d at position: %d\n", err.msg, (int)err.code, (int)err.pos); 459 | } 460 | decode_one(L, yyjson_doc_get_root(doc)); 461 | yyjson_doc_free(doc); 462 | return 1; 463 | } 464 | 465 | static int concat(lua_State* L) 466 | { 467 | if (lua_type(L, 1) == LUA_TSTRING) 468 | { 469 | size_t size; 470 | const char* sz = lua_tolstring(L, -1, &size); 471 | auto buf = new lyyjson::buffer(size, 16); 472 | buf->write_back(sz, size); 473 | lua_pushlightuserdata(L, buf); 474 | return 1; 475 | } 476 | 477 | luaL_checktype(L, 1, LUA_TTABLE); 478 | 479 | lua_settop(L, 1); 480 | 481 | auto buf = new lyyjson::buffer(512, 16); 482 | try 483 | { 484 | int array_size = (int)lua_rawlen(L, 1); 485 | for (int i = 1; i <= array_size; i++) 486 | { 487 | lua_rawgeti(L, 1, i); 488 | int t = lua_type(L, -1); 489 | switch (t) 490 | { 491 | case LUA_TNUMBER: 492 | { 493 | if (lua_isinteger(L, -1)) 494 | buf->write_chars(lua_tointeger(L, -1)); 495 | else 496 | buf->write_chars(lua_tonumber(L, -1)); 497 | break; 498 | } 499 | case LUA_TBOOLEAN: 500 | { 501 | if (lua_toboolean(L, -1)) 502 | buf->write_back(json_true.data(), json_true.size()); 503 | else 504 | buf->write_back(json_false.data(), json_false.size()); 505 | break; 506 | } 507 | case LUA_TSTRING: 508 | { 509 | size_t size; 510 | const char* sz = lua_tolstring(L, -1, &size); 511 | buf->write_back(sz, size); 512 | break; 513 | } 514 | case LUA_TTABLE: 515 | { 516 | encode_one(L, buf, -1); 517 | break; 518 | } 519 | default: 520 | throw std::logic_error{std::string("json encode: unsupport value type :") + 521 | lua_typename(L, t)}; 522 | break; 523 | } 524 | lua_pop(L, 1); 525 | } 526 | lua_pushlightuserdata(L, buf); 527 | return 1; 528 | } 529 | catch (const std::exception& ex) 530 | { 531 | delete buf; 532 | lua_pushstring(L, ex.what()); 533 | } 534 | return lua_error(L); 535 | } 536 | 537 | static void write_resp(lyyjson::buffer* buf, const char* cmd, size_t size) 538 | { 539 | buf->write_back("\r\n$", 3); 540 | buf->write_chars(size); 541 | buf->write_back("\r\n", 2); 542 | buf->write_back(cmd, size); 543 | } 544 | 545 | static void concat_resp_one(lyyjson::buffer* buf, lua_State* L, int i) 546 | { 547 | int t = lua_type(L, i); 548 | switch (t) 549 | { 550 | case LUA_TNIL: 551 | { 552 | std::string_view sv = "\r\n$-1"sv; 553 | buf->write_back(sv.data(), sv.size()); 554 | break; 555 | } 556 | case LUA_TNUMBER: 557 | { 558 | if (lua_isinteger(L, i)) 559 | { 560 | std::string s = std::to_string(lua_tointeger(L, i)); 561 | write_resp(buf, s.data(), s.size()); 562 | } 563 | else 564 | { 565 | std::string s = std::to_string(lua_tonumber(L, i)); 566 | write_resp(buf, s.data(), s.size()); 567 | } 568 | break; 569 | } 570 | case LUA_TBOOLEAN: 571 | { 572 | if (lua_toboolean(L, i)) 573 | write_resp(buf, json_true.data(), json_true.size()); 574 | else 575 | write_resp(buf, json_false.data(), json_false.size()); 576 | break; 577 | } 578 | case LUA_TSTRING: 579 | { 580 | size_t msize; 581 | const char* sz = lua_tolstring(L, i, &msize); 582 | write_resp(buf, sz, msize); 583 | break; 584 | } 585 | case LUA_TTABLE: 586 | { 587 | if (luaL_getmetafield(L, i, "__redis") != LUA_TNIL) 588 | { 589 | lua_pop(L, 1); 590 | auto size = lua_rawlen(L, i); 591 | for (unsigned n = 1; n <= size; n++) 592 | { 593 | lua_rawgeti(L, i, n); 594 | concat_resp_one(buf, L, -1); 595 | lua_pop(L, 1); 596 | } 597 | } 598 | else 599 | { 600 | buffer writer(512); 601 | encode_one(L, &writer, i, 0); 602 | write_resp(buf, writer.data(), writer.size()); 603 | } 604 | break; 605 | } 606 | default: 607 | throw std::logic_error{std::string("json encode: unsupport value type :") + lua_typename(L, t)}; 608 | break; 609 | } 610 | } 611 | 612 | static int concat_resp(lua_State* L) 613 | { 614 | int n = lua_gettop(L); 615 | if (0 == n) 616 | return 0; 617 | auto buf = new lyyjson::buffer(512, 16); 618 | try 619 | { 620 | buf->write_back('*'); 621 | buf->write_chars(n); 622 | 623 | for (int i = 1; i <= n; i++) 624 | { 625 | concat_resp_one(buf, L, i); 626 | } 627 | 628 | buf->write_back("\r\n", 2); 629 | lua_pushlightuserdata(L, buf); 630 | return 1; 631 | } 632 | catch (const std::exception& ex) 633 | { 634 | delete buf; 635 | lua_pushstring(L, ex.what()); 636 | } 637 | return lua_error(L); 638 | } 639 | 640 | static int pretty_encode(lua_State *L) 641 | { 642 | try 643 | { 644 | luaL_checkany(L, 1); 645 | buffer writer(512); 646 | lua_settop(L, 1); 647 | encode_one(L, &writer, 1); 648 | lua_pushlstring(L, writer.data(), writer.size()); 649 | return 1; 650 | } 651 | catch (const std::exception& ex) 652 | { 653 | lua_pushstring(L, ex.what()); 654 | } 655 | return lua_error(L); 656 | } 657 | 658 | extern "C" { 659 | int LUAMOD_API luaopen_yyjson(lua_State* L) 660 | { 661 | luaL_Reg l[] = { 662 | {"encode", encode}, 663 | {"pretty_encode", pretty_encode}, 664 | {"decode", decode}, 665 | {"concat", concat}, 666 | {"concat_resp", concat_resp}, 667 | {NULL, NULL} }; 668 | luaL_newlib(L, l); 669 | lua_pushstring(L, "null"); 670 | lua_pushlightuserdata(L, nullptr); 671 | lua_rawset(L, -3); 672 | return 1; 673 | } 674 | } 675 | --------------------------------------------------------------------------------